File Coverage

lib/BalanceOfPower/World.pm
Criterion Covered Total %
statement 457 541 84.4
branch 145 202 71.7
condition 14 26 53.8
subroutine 46 49 93.8
pod 0 38 0.0
total 662 856 77.3


line stmt bran cond sub pod time code
1             package BalanceOfPower::World;
2             $BalanceOfPower::World::VERSION = '0.400115';
3 13     13   21699 use strict;
  13         17  
  13         375  
4 13     13   140 use v5.10;
  13         35  
5              
6 13     13   7159 use Moo;
  13         155023  
  13         97  
7 13     13   25167 use Data::Dumper;
  13         119159  
  13         1021  
8 13     13   83 use Cwd 'abs_path';
  13         25  
  13         653  
9 13     13   63 use File::Path 'make_path';
  13         18  
  13         815  
10              
11 13     13   4959 use BalanceOfPower::Constants ':all';
  13         28  
  13         9015  
12 13     13   5220 use BalanceOfPower::Utils qw(prev_turn next_turn);
  13         25  
  13         1013  
13 13     13   5414 use BalanceOfPower::Nation;
  13         41  
  13         495  
14 13     13   4339 use BalanceOfPower::Dice;
  13         28  
  13         456  
15 13     13   5996 use BalanceOfPower::Commands;
  13         41  
  13         70455  
16              
17             has name => (
18             is => 'ro',
19             default => 'WORLD'
20             );
21             has silent => (
22             is => 'rw',
23             default => 0
24             );
25             has first_year => (
26             is => 'ro'
27             );
28             has current_year => (
29             is => 'rw'
30             );
31             has nations => (
32             is => 'rw',
33             default => sub { [] }
34             );
35             has nation_names => (
36             is => 'rw',
37             default => sub { [] }
38             );
39             has nation_codes => (
40             is => 'rw',
41             default => sub { {} }
42             );
43             has order => (
44             is => 'rw',
45             default => ""
46             );
47             has ia_orders => (
48             is => 'rw',
49             default => sub { [] }
50             );
51             has autoplay => (
52             is => 'rw',
53             default => 0
54             );
55             has data_directory => (
56             is => 'rw',
57             default => sub {
58             my $module_file_path = __FILE__;
59             my $root_path = abs_path($module_file_path);
60             $root_path =~ s/World\.pm//;
61             my $data_directory = $root_path . "data";
62              
63             }
64             );
65             has dice => (
66             is => 'ro',
67             default => sub { BalanceOfPower::Dice->new( log_name => "bop-dice.log" ) },
68             handles => { random => 'random',
69             random10 => 'random10',
70             random_around_zero => 'random_around_zero',
71             shuffle => 'shuffle_array',
72             tricks => 'tricks',
73             forced_advisor => 'forced_advisor',
74             only_one_nation_acting => 'only_one_nation_acting',
75             dice_log => 'log_active'
76             }
77             );
78             has savefile => (
79             is => 'rw',
80             default => ""
81             );
82              
83              
84              
85             with 'BalanceOfPower::Role::GameMaster';
86             with 'BalanceOfPower::Role::Historian';
87             with 'BalanceOfPower::Role::Herald';
88             with 'BalanceOfPower::Role::Ruler';
89             with 'BalanceOfPower::Role::Mapmaker';
90             with 'BalanceOfPower::Role::Supporter';
91             with 'BalanceOfPower::Role::Diplomat';
92             with 'BalanceOfPower::Role::Merchant';
93             with 'BalanceOfPower::Role::Broker';
94             with 'BalanceOfPower::Role::Warlord';
95             with 'BalanceOfPower::Role::Rebel';
96             with 'BalanceOfPower::Role::CrisisManager';
97             with 'BalanceOfPower::Role::Analyst';
98             with 'BalanceOfPower::Role::Recorder';
99             with 'BalanceOfPower::Role::Shopper';
100             with 'BalanceOfPower::Role::WebMaster';
101              
102             sub get_nation
103             {
104 1339     1339 0 12915 my $self = shift;
105 1339         1397 my $nation = shift;
106 1339 50       2361 if(! $nation)
107             {
108 0         0 say "Nation is undef";
109 0         0 return undef;
110             }
111 1339         1161 my @nations = grep { $_->name eq $nation } @{$self->nations};
  6591         11256  
  1339         3043  
112 1339 50       2542 if(@nations > 0)
113             {
114 1339         3501 return $nations[0];
115             }
116             else
117             {
118 0         0 say "Cannot find $nation";
119 0         0 return undef;
120             }
121             }
122             sub correct_nation_name
123             {
124 6     6 0 8 my $self = shift;
125 6         9 my $nation = shift;
126 6 50       15 return undef if(! $nation);
127 6 50       32 $nation = $self->nation_codes->{uc $nation} if(exists $self->nation_codes->{uc $nation});
128 6         5 for(@{$self->nation_names})
  6         24  
129             {
130 27 100       59 return $_ if(uc $_ eq uc $nation);
131             }
132 2         8 return undef;
133             }
134             sub check_nation_name
135             {
136 112     112 0 128 my $self = shift;
137 112         118 my $name = shift;
138 112         108 return grep {$_ eq $name} @{$self->nation_names};
  564         1124  
  112         223  
139             }
140             sub get_prev_year
141             {
142 0     0 0 0 my $self = shift;
143 0         0 return prev_turn($self->current_year);
144             }
145              
146             sub load_nations_data
147             {
148 16     16 0 37 my $self = shift;
149 16         36 my $datafile = shift;
150 16         167 my $file = $self->data_directory . "/" . $datafile;
151 16 50       1006 open(my $nations_file, "<", $file) || die $!;
152 16         33 my $area;
153             my %nations_data;
154 16         453 for(<$nations_file>)
155             {
156 88         124 my $n = $_;
157 88         100 chomp $n;
158 88 100       331 if(! ($n =~ /^#/))
159             {
160 72         215 my ($name, $code, $size, $government) = split(',', $n);
161 72 100       179 if($government eq 'd')
    50          
162             {
163 58         78 $government = 'democracy';
164             }
165             elsif($government eq 'D')
166             {
167 14         32 $government = 'dictatorship';
168             }
169 72         332 $nations_data{$name} = { code => $code,
170             area => $area,
171             size => $size,
172             government => $government ,
173             }
174              
175             }
176             else
177             {
178 16         66 $n =~ /^# (.*)$/;
179 16         64 $area = $1;
180             }
181             }
182 16         304 return %nations_data;
183             }
184              
185             #Initial values, randomly generated
186             sub init_random
187             {
188 16     16 0 5914 my $self = shift;
189 16         36 my $datafile = shift;
190 16         31 my $bordersfile = shift;
191 16         75 my %nations_data = $self->load_nations_data($datafile);
192 16         48 my $flags = shift;
193              
194 16         32 my $trades = 1;
195 16         36 my $diplomacy = 1;
196 16         34 my $alliances = 1;
197 16 100       85 if($flags)
198             {
199             $trades = $flags->{'trades'}
200 15 100       111 if(exists $flags->{'trades'});
201             $diplomacy = $flags->{'diplomacy'}
202 15 100       50 if(exists $flags->{'diplomacy'});
203             $alliances = $flags->{'alliances'}
204 15 50       64 if(exists $flags->{'alliances'});
205              
206             }
207              
208 16         94 $self->delete_log();
209 16         134 $self->dice->delete_log();
210 16         55 my @nation_names = ();
211 16         90 foreach my $n (keys %nations_data)
212             {
213 72         171 push @nation_names, $n;
214 72 100       2535 say "Working on $n" if ! $self->silent;
215 72         1543 my $export_quote = $self->random10(MIN_EXPORT_QUOTE, MAX_EXPORT_QUOTE, "Export quote $n");
216 72 100       1479 say " export quote: $export_quote" if ! $self->silent;
217 72         1998 my $government_strength = $self->random10(MIN_GOVERNMENT_STRENGTH, MAX_GOVERNMENT_STRENGTH, "Government strenght $n");
218 72 100       1406 say " government strength: $government_strength" if ! $self->silent;
219              
220 72         1828 my $executive = BalanceOfPower::Executive->new( actor => $n );
221 72         708 $executive->init($self);
222 72         2213 push @{$self->nations}, BalanceOfPower::Nation->new(
223             name => $n,
224             code => $nations_data{$n}->{code},
225             executive => $executive,
226             area => $nations_data{$n}->{area},
227             size => $nations_data{$n}->{size},
228             government => $nations_data{$n}->{government},
229             export_quote => $export_quote,
230             government_strength => $government_strength,
231 72         105 available_stocks => START_STOCKS->[$nations_data{$n}->{size}],
232             log_dir => $self->log_dir,
233             log_name => $self->log_name,
234             log_on_stdout => $self->log_on_stdout);
235 72         2628 $self->nation_codes->{$nations_data{$n}->{code}} = $n;
236             }
237 16         102 $self->nation_names(\@nation_names);
238 16         106 $self->load_borders($bordersfile);
239 16 100       82 if($trades)
240             {
241 2 50       203 say "Trades generation..." if ! $self->silent;
242 2         22 $self->init_trades();
243             }
244             else
245             {
246 14 100       700 say "Trades generation skipped" if ! $self->silent;
247             }
248 16 50       68 if($diplomacy)
249             {
250 16 100       375 say "Diplomacy generation..." if ! $self->silent;
251 16         115 $self->init_diplomacy();
252             }
253             else
254             {
255 0 0       0 say "Diplomacy generation skipped" if ! $self->silent;
256             }
257 16 100       67 if($alliances)
258             {
259 1 50       34 say "Alliances generation..." if ! $self->silent;
260 1         5 $self->init_random_alliances();
261             }
262             else
263             {
264 15 100       756 say "Alliances generation skipped" if ! $self->silent;
265             }
266             }
267              
268             #Group function for all the steps involved in a turn
269             sub pre_decisions_elaborations
270             {
271 46     46 0 2526 my $self = shift;
272 46         91 my $t = shift;
273 46         168 $self->init_year($t);
274 46         309 $self->war_current_year();
275 46         313 $self->player_start_turn();
276 46         245 $self->civil_war_current_year();
277 46         155 $self->war_debts();
278 46         347 $self->crisis_generator();
279             }
280             sub post_decisions_elaborations
281             {
282 45     45 0 88 my $self = shift;
283 45         212 $self->execute_stock_orders();
284 45         154 $self->execute_decisions();
285 45         158 $self->economy();
286 45         173 $self->civil_warfare();
287 45         141 $self->warfare();
288 45         131 $self->internal_conflict();
289 45         226 $self->player_targets();
290 45         176 $self->register_global_data();
291 45         156 $self->collect_events();
292             }
293              
294              
295              
296             sub elaborate_turn
297             {
298 19     19 0 444 my $self = shift;
299 19         26 my $t = shift;
300 19         70 $self->pre_decisions_elaborations($t);
301 19         78 $self->decisions();
302 19         84 $self->post_decisions_elaborations();
303             }
304              
305              
306              
307             #To automatically generate turns
308             sub autopilot
309             {
310 1     1 0 7 my $self = shift;
311 1         1 my $start = shift;
312 1         2 my $stop = shift;
313 1         5 $self->autoplay(1);
314 1         3 for($start..$stop)
315             {
316 1         1 my $y = $_;
317 1         6 foreach my $t (get_year_turns($y))
318             {
319 4         18 $self->elaborate_turn($t);
320             }
321             }
322 1         9 $self->autoplay(0);
323             }
324              
325              
326             # Configure current year
327             # Give production to countries. Countries split it between export and domestic and, if allowed, raise the debt in case of necessity
328             # Wealth reset
329             # Production and debt recorded
330             sub init_year
331             {
332 47     47 0 95 my $self = shift;
333 47         77 my $turn = shift;
334 47 100       144 if(! $turn)
335             {
336 4         41 $turn = next_turn($self->current_year);
337             }
338             #$self->log("--- $turn ---");
339 47 100       1425 say $turn if $self->autoplay();
340 47         168 $self->current_year($turn);
341 47         81 foreach my $n (@{$self->nations})
  47         166  
342             {
343 188         493 $n->current_year($turn);
344 188         351 $n->wealth(0);
345 188         461 my $prod = $self->calculate_production($n);
346 188         565 $n->production($prod);
347 188         528 my $prestige = $self->calculate_prestige($n);
348 188         458 $n->prestige($prestige);
349 188         438 my $pu = PRODUCTION_UNITS->[$n->size];
350 188         565 $self->set_statistics_value($n, 'production', $prod);
351 188         720 $self->set_statistics_value($n, 'p/d', int(($prod / $pu) * 100) / 100);
352 188         596 $self->set_statistics_value($n, 'debt', $n->debt);
353 188         401 $self->set_statistics_value($n, 'prestige', $prestige);
354             }
355             }
356              
357              
358              
359             # PRODUCTION MANAGEMENT ###############################
360              
361             #Say the value of starting production used to calculate production for a turn.
362             #Usually is just the value of production the turn before, but if rebels won a civil war it has to be undef to allow a totally random generation of production.
363             sub get_base_production
364             {
365 188     188 0 241 my $self = shift;
366 188         180 my $nation = shift;
367              
368 188         580 my @newgov = $nation->get_events("NEW GOVERNMENT CREATED", prev_turn($nation->current_year));
369 188         553 my $previous_production = $self->get_statistics_value(prev_turn($nation->current_year), $nation->name, 'production');
370            
371 188 50       457 return () if(@newgov > 0);
372 188 100       443 return () if(! $previous_production);
373            
374 136         203 my @prods = ();
375 136         527 for(my $i = 0; $i < PRODUCTION_UNITS->[$nation->size]; $i++)
376             {
377 544         1069 push @prods, $self->get_statistics_value(prev_turn($nation->current_year), $nation->name, 'production' . $i);
378             }
379 136         395 return @prods;
380             }
381             sub calculate_production
382             {
383 188     188 0 228 my $self = shift;
384 188         194 my $n = shift;
385 188         400 my @prev_prods = $self->get_base_production($n);
386 188         291 my @next_prods = ();
387 188         213 my $cost_for_retreat = 0;
388 188         435 my @retreats = $n->get_events("RETREAT FROM", prev_turn($n->current_year));
389 188         261 my $global_production = 0;
390 188         660 for(my $i = 0; $i < PRODUCTION_UNITS->[$n->size]; $i++)
391             {
392 752 100       1360 if(@prev_prods > 0)
393             {
394 544         13891 $next_prods[$i] = $prev_prods[$i] + $self->random10(MIN_DELTA_PRODUCTION, MAX_DELTA_PRODUCTION, "Delta production" . $i . " " . $n->name);
395             }
396             else
397             {
398 208         5491 $next_prods[$i] = $self->random10(MIN_STARTING_PRODUCTION, MAX_STARTING_PRODUCTION, "Starting production" . $i . " " . $n->name);
399             }
400              
401             #DEFEAT COST MANAGEMENT
402 752 50       1980 if(@retreats)
403             {
404 0         0 $next_prods[$i] -= ATTACK_FAILED_PRODUCTION_MALUS;
405 0         0 $cost_for_retreat += ATTACK_FAILED_PRODUCTION_MALUS;
406             }
407 752 50       1668 $next_prods[$i] = 0 if($next_prods[$i] < 0);
408 752 100       1508 $next_prods[$i] = MAX_PRODUCTION if($next_prods[$i] > MAX_PRODUCTION);
409              
410 752         2971 $self->set_statistics_value($n, 'production' . $i, $next_prods[$i]);
411 752         2733 $global_production += $next_prods[$i];
412             }
413 188 50       402 if($cost_for_retreat)
414             {
415 0         0 $self->send_event("COST FOR DEFEAT ON PRODUCTION: " . $cost_for_retreat);
416             }
417 188         425 return $global_production;
418             }
419              
420              
421             #Conquered nations give to the conqueror a quote of their production at start of the turn
422             sub war_debts
423             {
424 46     46 0 78 my $self = shift;
425 46         210 for($self->influences->all())
426             {
427 4         10 $self->loot($_);
428             }
429 46         206 $self->situation_clock();
430             }
431              
432             sub loot
433             {
434 4     4 0 6 my $self = shift;
435 4         5 my $influence = shift;
436 4         10 my $n2 = $influence->node2;
437 4         7 my $n1 = $influence->node1;
438 4         13 my $quote = $influence->get_loot_quote();
439 4 50 33     17 return if(! $quote || $quote == 0);
440 0         0 my $nation = $self->get_nation($n2);
441 0         0 my $receiver = $self->get_nation($n1);
442 0 0       0 my $amount_domestic = $nation->production_for_domestic >= $quote ? $quote : $nation->production_for_domestic;
443 0 0       0 my $amount_export = $nation->production_for_export >= $quote ? $quote : $nation->production_for_export;
444 0         0 $nation->subtract_production('domestic', $amount_domestic);
445 0         0 $nation->subtract_production('export', $amount_export);
446 0         0 $nation->register_event("PAY LOOT TO " . $receiver->name . ": $amount_domestic + $amount_export");
447 0         0 $receiver->subtract_production('domestic', -1 * $amount_domestic);
448 0         0 $receiver->subtract_production('export', -1 * $amount_export);
449 0         0 $receiver->register_event("ACQUIRE LOOT FROM " . $nation->name . ": $amount_domestic + $amount_export");
450             }
451              
452              
453              
454             # PRODUCTION MANAGEMENT END ###############################################
455              
456             # PRESTIGE MANAGEMENT #####################################################
457              
458             sub calculate_prestige
459             {
460 188     188 0 272 my $self = shift;
461 188         191 my $nation = shift;
462 188         353 my $nation_name = $nation->name;
463 188         213 my $prestige = 0;
464 188         660 my @routes = $self->routes_for_node($nation_name);
465 188         259 $prestige += @routes;
466 188         625 my @supported = $self->supporter($nation_name);
467 188         246 $prestige += @supported;
468 188         654 my @influenced = $self->has_influence($nation_name);
469 188         295 $prestige += @influenced * INFLUENCE_PRESTIGE_BONUS;
470 188         224 my $bonus = 0;
471 188         687 my @ordered_best_w = $self->order_statistics(prev_turn($nation->current_year), 'w/d');
472 188 100       546 if(@ordered_best_w >= BEST_WEALTH_FOR_PRESTIGE)
473             {
474 122         302 for(my $i = 0; $i < BEST_WEALTH_FOR_PRESTIGE; $i++)
475             {
476 610 100       1502 if($ordered_best_w[$i]->{nation} eq $nation_name)
477             {
478 120         133 $bonus += BEST_WEALTH_FOR_PRESTIGE_BONUS;
479 120         889 $self->broadcast_event({ code => 'bestwealth',
480             text => "ONE OF THE FIRST " . BEST_WEALTH_FOR_PRESTIGE . " NATIONS FOR WEALTH WAS " . $nation_name,
481             involved => [ $nation_name ],
482             values => [] },
483             $nation_name);
484             }
485             }
486             }
487 188         680 my @ordered_best_p = $self->order_statistics(prev_turn($nation->current_year), 'progress');
488 188 100       490 if(@ordered_best_p >= BEST_PROGRESS_FOR_PRESTIGE)
489             {
490 122         302 for(my $i = 0; $i < BEST_PROGRESS_FOR_PRESTIGE; $i++)
491             {
492 610 100       1497 if($ordered_best_p[$i]->{nation} eq $nation_name)
493             {
494 120         138 $bonus += BEST_PROGRESS_FOR_PRESTIGE_BONUS;
495 120         802 $self->broadcast_event({ code => 'bestprogress',
496             text => "ONE OF THE FIRST " . BEST_PROGRESS_FOR_PRESTIGE . " NATIONS FOR PROGRESS WAS " . $nation_name,
497             involved => [$nation_name],
498             values => [] },
499             $nation_name);
500             }
501             }
502             }
503            
504 188         315 $prestige += $bonus;
505 188         712 my @wins = $nation->get_events("WAR BETWEEN .* AND .* WON BY ". $nation_name, prev_turn($nation->current_year));
506 188 50       477 if(@wins > 0)
507             {
508 0         0 $prestige += WAR_PRESTIGE_BONUS;
509             }
510 188         908 return $prestige;
511             }
512              
513             # PRESTIGE MANAGEMENT END #####################################################
514              
515             # DECISIONS ###############################################################
516              
517             # Decisions are collected and executed
518             sub execute_decisions
519             {
520 45     45 0 66 my $self = shift;
521 45         116 my @decisions = @{$self->ia_orders};
  45         167  
522 45         78 my @route_adders = ();
523             #foreach my $d (@decisions)
524 45         60 foreach my $n (@{$self->nation_names})
  45         151  
525             {
526 177         401 my $command = $self->control($n);
527 177 50       340 if(! $command)
528             {
529 177 100       221 my @nation_orders = grep { $_ =~ /^$n: / } @decisions; if(@nation_orders > 0)
  320         2514  
  177         324  
530             {
531 64         252 $nation_orders[0] =~ /^(.*): (.*)$/;
532 64         198 $command = $2;
533             }
534             else
535             {
536 113         219 next;
537             }
538             }
539 64         143 my $nation = $self->get_nation($n);
540 64 100       703 if($command =~ /^DELETE TRADEROUTE (.*)->(.*)$/)
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
541             {
542 1         6 $self->delete_route($1, $2);
543             }
544             elsif($command =~ /^ADD ROUTE$/)
545             {
546 3         12 push @route_adders, $nation->name;
547             }
548             elsif($command =~ /^LOWER DISORDER$/)
549             {
550 1         7 $nation->lower_disorder($self);
551             }
552             elsif($command =~ /^BUILD TROOPS$/)
553             {
554 19         72 $nation->build_troops();
555             }
556             elsif($command =~ /^BOOST PRODUCTION$/)
557             {
558 6         20 $nation->boost_production();
559             }
560             elsif($command =~ /DECLARE WAR TO (.*)$/)
561             {
562 6         15 my $attacker = $nation;
563 6         20 my $defender = $self->get_nation($1);
564 6 50 66     33 if(! $self->war_busy($attacker->name) && ! $self->war_busy($defender->name))
565             {
566 4         26 $self->create_war($attacker, $defender);
567             }
568             }
569             elsif($command =~ /^MILITARY SUPPORT (.*)$/)
570             {
571 3         7 my $supporter = $nation;
572 3         13 my $supported = $self->get_nation($1);
573 3 50       23 if($supported->accept_military_support($supporter->name, $self))
574             {
575 3         21 $self->start_military_support($supporter, $supported);
576             }
577             else
578             {
579 0         0 $self->broadcast_event({ code => 'suprefused',
580             text => $supported->name . " REFUSED MILITARY SUPPORT FROM " . $supporter->name,
581             involved => [$supporter->name, $supported->name] }, $supporter->name, $supported->name);
582             }
583             }
584             elsif($command =~ /^RECALL MILITARY SUPPORT (.*)$/)
585             {
586 1         2 my $supporter = $nation;
587 1         4 my $supported = $self->get_nation($1);
588 1         5 $self->stop_military_support($supporter, $supported);
589             }
590             elsif($command =~ /^AID INSURGENTS IN (.*)$/)
591             {
592 8         22 my $victim = $self->get_nation($1);
593 8         34 $self->aid_insurgents($nation, $victim);
594             }
595             elsif($command =~ /^TREATY (.*) WITH (.*)$/)
596             {
597 1         3 my $nation2 = $self->get_nation($2);
598 1         7 $self->stipulate_treaty($nation, $nation2, $1);
599             }
600             elsif($command =~ /^ECONOMIC AID FOR (.*)$/)
601             {
602 1         8 my $nation2 = $self->get_nation($1);
603 1         4 $self->economic_aid($nation, $nation2);
604             }
605             elsif($command =~ /^REBEL MILITARY SUPPORT (.*)$/)
606             {
607 1         10 my $nation2 = $self->get_nation($1);
608 1         8 my $rebsup = $self->rebel_supported($nation2->name);
609 1 50       8 if($self->at_civil_war($nation2->name))
610             {
611 1         7 $self->start_rebel_military_support($nation, $nation2);
612             }
613             else
614             {
615 0         0 say "ERROR: NO CIVIL WAR " . $nation2->name;
616             }
617             }
618             elsif($command =~ /^DIPLOMATIC PRESSURE ON (.*)$/)
619             {
620 4         10 my $n2 = $1;
621 4 50       23 if($nation->prestige >= DIPLOMATIC_PRESSURE_PRESTIGE_COST)
622             {
623 4         17 my $under_infl = $self->is_under_influence($n2);
624 4   50     35 $under_infl ||= "";
625 4 50       31 if($under_infl ne $nation->name)
626             {
627 4         30 $self->diplomatic_pressure($nation->name, $n2);
628             }
629             else
630             {
631 0         0 $self->broadcast_event("DIPLOMATIC PRESSURE ON $n2 BY " . $nation->name . " IMPOSSIBLE! $n2 IS UNDER INFLUENCE OF " . $nation->name, $nation->name, $n2);
632             }
633             }
634             }
635             elsif($command =~ /^RECALL REBEL MILITARY SUPPORT (.*)$/)
636             {
637 1         5 my $supported = $self->get_nation($1);
638 1         8 $self->stop_rebel_military_support($nation, $supported);
639             }
640             elsif($command =~ /^MILITARY AID FOR (.*)$/)
641             {
642 2         9 my $nation2 = $self->get_nation($1);
643 2         9 $self->military_aid($nation, $nation2);
644             }
645             elsif($command =~ /^PROGRESS$/)
646             {
647 6         32 $nation->grow();
648 6         73 $self->broadcast_event({ code => 'progress',
649             text => "INVESTMENT IN PROGRESS FOR " . $nation->name,
650             involved => [$nation->name] }, $nation->name);
651             }
652 64         282 $self->set_statistics_value($nation, 'order', $command);
653             }
654 45         265 $self->empty_control_orders();
655 45         136 $self->manage_route_adding(@route_adders);
656             }
657             sub empty_control_orders
658             {
659 45     45 0 71 my $self = shift;
660 45         72 foreach my $p (@{$self->players})
  45         137  
661             {
662 10         39 $p->empty_control_orders();
663             }
664             }
665              
666             sub manage_route_adding
667             {
668 45     45 0 70 my $self = shift;
669 45         75 my @route_adders = @_;
670 45 100       164 if(@route_adders > 1)
671             {
672 1         24 @route_adders = $self->shuffle("Route adders", @route_adders);
673 1         3 my $done = 0;
674 1         15 while(! $done)
675             {
676 1         4 my $node1 = shift @route_adders;
677 1 50       6 if($self->suitable_route_creator($node1))
678             {
679 1 50       3 if(@route_adders == 0)
680             {
681 0         0 $self->broadcast_event( { code => "tradelack",
682             text => "TRADEROUTE CREATION FAILED FOR LACK OF PARTNERS FOR $node1",
683             involved => [$node1] }, $node1);
684 0         0 $done = 1;
685             }
686             else
687             {
688 1         2 my $complete = 0;
689 1         2 foreach my $second (@route_adders)
690             {
691 1 50       5 if($self->suitable_new_route($node1, $second))
692             {
693 1         3 @route_adders = grep { $_ ne $second } @route_adders;
  1         3  
694 1         6 $self->generate_traderoute($node1, $second, 1);
695 1         3 $complete = 1;
696             }
697 1 50       4 last if $complete;
698             }
699 1 50       5 if($complete == 0)
700             {
701 0         0 $self->broadcast_event( { code => "tradelack",
702             text => "TRADEROUTE CREATION FAILED FOR LACK OF PARTNERS FOR $node1",
703             involved => [$node1] }, $node1);
704             }
705             }
706             }
707             else
708             {
709 0         0 $self->broadcast_event("TRADEROUTE CREATION NOT POSSIBLE FOR $node1", $node1);
710             }
711 1 50       9 $done = 1 if(@route_adders == 0);
712             }
713             }
714             }
715             sub decisions
716             {
717 21     21 0 39 my $self = shift;
718 21         38 my @decisions = ();
719 21         28 foreach my $nation (@{$self->nations})
  21         93  
720             {
721 65         79 my $decision;
722 65         215 $decision = $nation->decision($self);
723 65 100       147 if($decision)
724             {
725 43         110 push @decisions, $decision;
726             }
727             }
728 21         125 $self->ia_orders(\@decisions);
729             }
730             sub control
731             {
732 177     177 0 265 my $self = shift;
733 177         191 my $nation = shift;
734 177         164 my $quote = -1;
735 177         159 my $winner = undef;
736 177         221 my @losers = ();
737 177         151 my $winner_command;
738 177         161 foreach my $player (@{$self->players})
  177         366  
739             {
740 28         92 my $player_command = $player->get_control_order($nation);
741 28 50       88 if($player_command)
742             {
743 0 0       0 if($player->stocks($nation) > $quote)
744             {
745 0 0       0 if($winner)
746             {
747 0         0 push @losers, $winner;
748             }
749 0         0 $winner = $player;
750 0         0 $quote = $player->stocks($nation);
751 0         0 $winner_command = $player_command;
752             }
753             else
754             {
755 0         0 push @losers, $player;
756             }
757             }
758             }
759 177 50       314 if($winner)
760             {
761 0         0 $winner->add_influence(-1 * INFLUENCE_COST, $nation);
762 0         0 $winner->register_event("ORDER FOR $nation IS EXECUTED: $winner_command");
763 0         0 for(@losers)
764             {
765 0         0 $_->register_event("ORDER FOR $nation NOT EXECUTED! " . $winner->name . " MORE POWERFUL");
766             }
767 0         0 return $winner_command;
768             }
769             else
770             {
771 177         288 return undef;
772             }
773             }
774              
775             # DECISIONS END ###########################################################
776              
777             # ECONOMY #################################################################
778              
779             # Calculate internal wealth converting domestic production to wealth
780             # Active trade routes one by one trying to generate wealth from each of them
781             # Convert remain as generating internal wealth
782             sub economy
783             {
784 45     45 0 60 my $self = shift;
785 45         58 foreach my $n (@{$self->nations})
  45         160  
786             {
787 177         541 $n->calculate_internal_wealth();
788 177         601 $n->calculate_trading($self);
789 177         509 $n->convert_remains();
790 177 100       688 if($self->at_war($n->name))
791             {
792 20         60 $n->war_cost();
793             }
794 177 100       752 if($self->at_civil_war($n->name))
795             {
796 17         64 $n->civil_war_cost();
797             }
798              
799 177         349 my $wealth = $n->wealth;
800 177         381 my $pu = PRODUCTION_UNITS->[$n->size];
801 177         714 my $prod = $self->get_statistics_value($self->current_year, $n->name, 'production');
802              
803 177         625 $self->set_statistics_value($n, 'wealth', $n->wealth);
804 177         661 $self->set_statistics_value($n, 'w/d', int(($wealth / $pu) * 100) / 100);
805 177 50       331 if($prod != 0)
806             {
807 177         521 $self->set_statistics_value($n, 'growth', int(($wealth / $prod) * 100) / 100 );
808             }
809             else
810             {
811 0         0 $self->set_statistics_value($n, 'growth', 'X' );
812             }
813             }
814             }
815             sub economic_aid
816             {
817 1     1 0 2 my $self = shift;
818 1         2 my $nation1 = shift;
819 1         2 my $nation2 = shift;
820 1         6 $nation1->subtract_production('export', ECONOMIC_AID_COST);
821 1         6 $nation2->receive_aid($nation1->name);
822 1         18 $self->broadcast_event({ code => 'economicaid' ,
823             text => "ECONOMIC AID FROM " . $nation1->name . " TO " . $nation2->name,
824             involved => [$nation1->name, $nation2->name] }, $nation1->name, $nation2->name);
825 1         13 $self->change_diplomacy($nation1->name, $nation2->name, ECONOMIC_AID_DIPLOMACY_FACTOR, "ECONOMIC AID FROM " . $nation1->name);
826              
827             }
828              
829             # ECONOMY END #############################################################
830              
831             # INTERNAL DISORDER #######################################################
832              
833             sub internal_conflict
834             {
835 45     45 0 71 my $self = shift;
836 45         71 foreach my $n (@{$self->nations})
  45         141  
837             {
838 177 100       589 if(! $self->get_civil_war($n->name))
839             {
840 162         399 $n->calculate_disorder($self);
841 162 100       306 if($n->internal_disorder_status eq 'Civil war')
842             {
843 1         7 $self->start_civil_war($n);
844             }
845             }
846 177         625 $self->set_statistics_value($n, 'internal disorder', $n->internal_disorder);
847             }
848             }
849              
850             sub aid_insurgents
851             {
852 8     8 0 10 my $self = shift;
853 8         13 my $nation1 = shift;
854 8         11 my $nation2 = shift;
855 8 50 33     56 if($nation1->production_for_export >= AID_INSURGENTS_COST && $nation2->internal_disorder_status ne 'Civil war')
856             {
857 8         101 $self->broadcast_event({ code => 'insurgentsaid' ,
858             text => "AIDS FOR INSURGENTS OF " . $nation2->name . " FROM " . $nation1->name,
859             involved => [$nation1->name, $nation2->name] },
860             $nation1->name, $nation2->name);
861 8         32 $nation1->subtract_production('export', AID_INSURGENTS_COST);
862 8         32 $nation2->add_internal_disorder(INSURGENTS_AID, $self);
863             }
864             }
865              
866              
867             # INTERNAL DISORDER END ######################################################
868              
869             # WAR ######################################################################
870              
871             sub war_busy
872             {
873 240     240 0 287 my $self = shift;
874 240         244 my $n = shift;
875 240   100     607 return $self->at_civil_war($n) || $self->at_war($n);
876             }
877              
878             sub warfare
879             {
880 45     45 0 67 my $self = shift;
881 45         199 $self->fight_wars();
882 45         60 foreach my $n (@{$self->nations})
  45         170  
883             {
884 177         459 $self->set_statistics_value($n, 'army', $n->army);
885             }
886             }
887              
888             sub civil_warfare
889             {
890 45     45 0 92 my $self = shift;
891 45         62 foreach my $cw (@{$self->civil_wars})
  45         139  
892             {
893 17         62 my $winner = $cw->fight($self);
894 17 100       65 if($winner)
895             {
896 2         20 $cw->win($winner, $self);
897 2         16 $self->delete_civil_war($cw->nation_name);
898             }
899             }
900             }
901              
902             sub military_aid
903             {
904 2     2 0 6 my $self = shift;
905 2         12 my $nation1 = shift;
906 2         4 my $nation2 = shift;
907 2         10 $nation1->subtract_production('export', MILITARY_AID_COST);
908 2         7 $nation2->add_army(ARMY_UNIT);
909 2         35 $self->broadcast_event({ code => 'militaryaid',
910             text => "MILITARY AID FROM " . $nation1->name . " TO " . $nation2->name,
911             involved => [$nation1->name, $nation2->name] }, $nation1->name, $nation2->name);
912 2         23 $self->change_diplomacy($nation1->name, $nation2->name, MILITARY_AID_DIPLOMACY_FACTOR, "MILITARY AID FROM " . $nation1->name );
913             }
914              
915             sub war_report
916             {
917 13     13 0 38 my $self = shift;
918 13         30 my $message = shift;
919 13         23 my $nation = shift;
920 13         83 my @wars = $self->get_wars($nation);
921 13         53 for(@wars)
922             {
923 1         3 $_->register_event($message);
924             }
925             }
926              
927             sub civil_war_report
928             {
929 10     10 0 19 my $self = shift;
930 10         20 my $message = shift;
931 10         18 my $nation = shift;
932 10         55 my $cw = $self->get_civil_war($nation);
933 10 100       55 $cw->register_event($message) if $cw;
934             }
935              
936              
937             # WAR END ##################################################################
938              
939             # TREATIES #################################################################
940              
941             sub stipulate_treaty
942             {
943 1     1 0 1 my $self = shift;
944 1         2 my $nation1 = shift;
945 1         2 my $nation2 = shift;
946 1         2 my $type = shift;
947 1         8 my $present_treaty = $self->exists_treaty($nation1->name, $nation2->name);
948 1         10 my $diplomatic_status = $self->diplomacy_status($nation1->name, $nation2->name);
949 1 50       5 if($diplomatic_status eq 'HATE')
950             {
951 0         0 $self->broadcast_event({ code => 'hatetreaty',
952             text => "TREATY BETWEEN " . $nation1->name . " AND " . $nation2->name . " NOT POSSIBLE BECAUSE OF HATE",
953             involved => [$nation1->name, $nation2->name] }, $nation1->name, $nation2->name);
954 0         0 return;
955             }
956 1 50 33     6 if($self->get_treaties_for_nation($nation1->name) >= $nation1->treaty_limit ||
      33        
957             $self->get_treaties_for_nation($nation2->name) >= $nation2->treaty_limit &&
958             ! $present_treaty)
959             {
960 0         0 $self->broadcast_event( { code => 'limittreaty',
961             text => "TREATY BETWEEN " . $nation1->name . " AND " . $nation2->name . " NOT POSSIBLE BECAUSE ONE NATION HAS ALREADY REACHED MAXIMUM ALLOWED TREATIES",
962             involved => [$nation1->name, $nation2->name] }, $nation1->name, $nation2->name);
963 0         0 return;
964             }
965 1 50       6 if($nation1->prestige >= TREATY_PRESTIGE_COST)
966             {
967 1 50 33     6 if($present_treaty && $present_treaty->type ne 'alliance')
968             {
969 0         0 $self->create_treaty($nation1->name, $nation2->name, 'alliance');
970 0         0 $self->broadcast_event({ code => 'alliancetreatynew',
971             text => "ALLIANCE BETWEEN " . $nation1->name . " AND " . $nation2->name,
972             involved => [$nation1->name, $nation2->name] }, $nation1->name, $nation2->name);
973             }
974             else
975             {
976 1 50       5 if($type eq 'COM')
    0          
977             {
978 1 50       3 if(! $present_treaty)
979             {
980 1 50       8 if($self->route_exists($nation1->name, $nation2->name))
981             {
982 1         10 $self->create_treaty($nation1->name, $nation2->name, 'commercial');
983 1         18 $self->broadcast_event({ code => "comtreatynew",
984             text => "COMMERCIAL TREATY BETWEEN " . $nation1->name . " AND " . $nation2->name,
985             involved => [$nation1->name, $nation2->name] }, $nation1->name, $nation2->name);
986             }
987             else
988             {
989 0         0 $self->broadcast_event({ code => "uselesstreaty",
990             text => "COMMERCIAL TREATY BETWEEN " . $nation1->name . " AND " . $nation2->name . " MADE USELESS BY ROUTE CANCELATION",
991             involved => [$nation1->name, $nation2->name] }, $nation1->name, $nation2->name);
992             }
993             }
994             }
995             elsif($type eq 'NAG')
996             {
997 0         0 $self->create_treaty($nation1->name, $nation2->name, 'no aggression');
998 0         0 $self->broadcast_event({ code => 'nagtreatynew',
999             text => "NO AGGRESSION TREATY BETWEEN " . $nation1->name . " AND " . $nation2->name,
1000             involved => [$nation1->name, $nation2->name] }, $nation1->name, $nation2->name);
1001             }
1002             }
1003             }
1004             }
1005              
1006              
1007              
1008             # TRATIES END ##############################################################
1009              
1010             # TRAVELS ##################################################################
1011              
1012             sub make_travel_plan
1013             {
1014 5     5 0 43 my $self = shift;
1015 5         13 my $from = shift;
1016 5         15 my @already = ();
1017 5         10 my %plan;
1018 5         21 $plan{'ground'} = {};
1019 5         14 $plan{'air'} = {};
1020 5         34 my @for_commerce = $self->route_destinations_for_node($from);
1021            
1022 5         67 my @at_borders = $self->near_nations($from, 1);
1023 5         22 foreach my $n(@for_commerce)
1024             {
1025 10 50       45 if(! grep { $_ eq $n } @already)
  3         20  
1026             {
1027 10         26 my $youcan = 'OK';
1028 10 100 100     40 $youcan = 'KO' if($self->war_busy($from) || $self->war_busy($n));
1029 10         54 $plan{'air'}->{$n}->{status} = $youcan;
1030 10         62 my $cost = $self->distance($from, $n) * AIR_TRAVEL_COST_FOR_DISTANCE;
1031 10 100       37 $cost = AIR_TRAVEL_CAP_COST if $cost > AIR_TRAVEL_CAP_COST;
1032 10 100       39 $plan{'air'}->{$n}->{cost} = $cost if($youcan eq 'OK');
1033 10 100       45 push @already, $n if $youcan eq 'OK';
1034             }
1035             }
1036 5         19 foreach my $n(@at_borders)
1037             {
1038 10 100       24 if(! grep { $_ eq $n } @already)
  14         44  
1039             {
1040 9         36 $plan{'ground'}->{$n}->{status} = 'OK';
1041 9         37 $plan{'ground'}->{$n}->{cost} = GROUND_TRAVEL_COST;
1042 9         30 push @already, $n;
1043             }
1044             }
1045 5         79 return %plan;
1046             }
1047              
1048             # END TRAVELS ########################################################################
1049              
1050             # MISSIONS ###########################################################################
1051              
1052             sub generate_mission
1053             {
1054 0     0 0 0 my $self = shift;
1055 0         0 my $type = shift;
1056 0         0 my @nations = @{$self->nation_names};
  0         0  
1057 0         0 my %out;
1058            
1059 0 0       0 if($type eq 'parcel')
1060             {
1061 0         0 @nations = $self->shuffle("Nations for mission - assignment", @nations);
1062 0         0 $out{'assignment'} = $nations[0];
1063 0         0 @nations = $self->shuffle("Nations for mission - from", @nations);
1064 0         0 $out{'from'} = $nations[0];
1065 0         0 @nations = $self->shuffle("Nations for mission - to", @nations);
1066 0 0       0 $out{'to'} = $nations[0] ne $out{'from'} ? $nations[0] : $nations[1];
1067 0         0 my $time = $self->random(0, 2, "Time available for mission");
1068 0         0 $out{'expire'} = next_turn($self->current_year);
1069 0         0 for(my $i = 0; $i < $time; $i++)
1070             {
1071 0         0 $out{'expire'} = next_turn($out{'expire'});
1072             }
1073             $out{'reward'}->{'friendship'}->{'assignment'} = $self->random(FRIENDSHIP_RANGE_FOR_MISSION->{$type}->[0],
1074 0         0 FRIENDSHIP_RANGE_FOR_MISSION->{$type}->[1],
1075             "Friendship for mission - assignment");
1076             $out{'reward'}->{'friendship'}->{'from'} = $self->random(FRIENDSHIP_RANGE_FOR_MISSION->{$type}->[0],
1077 0         0 FRIENDSHIP_RANGE_FOR_MISSION->{$type}->[1],
1078             "Friendship for mission - from");
1079             $out{'reward'}->{'friendship'}->{'to'} = $self->random(FRIENDSHIP_RANGE_FOR_MISSION->{$type}->[0],
1080 0         0 FRIENDSHIP_RANGE_FOR_MISSION->{$type}->[1],
1081             "Friendship for mission - to");
1082 0         0 my $tot_friendship = $out{'reward'}->{'friendship'}->{'assignment'} + $out{'reward'}->{'friendship'}->{'from'} + $out{'reward'}->{'friendship'}->{'to'};
1083 0         0 my $money_bonus = $tot_friendship * BONUS_FACTOR_FOR_BAD_FRIENSHIP;
1084 0         0 $out{'reward'}->{'money'} = $self->random(MONEY_RANGE_FOR_MISSION->{$type}->[0] - $money_bonus, MONEY_RANGE_FOR_MISSION->{$type}->[1], "Money for mission");
1085             }
1086             else
1087             {
1088 0         0 die "Wrong type of mission";
1089             }
1090 0         0 return %out;
1091             }
1092              
1093              
1094              
1095              
1096              
1097              
1098              
1099              
1100              
1101              
1102              
1103              
1104              
1105              
1106              
1107             # END MISSIONS #######################################################################
1108              
1109             sub register_global_data
1110             {
1111 45     45 0 80 my $self = shift;
1112 45         191 my $crises = $self->get_all_crises();
1113 45         200 my $wars = $self->wars->all();
1114 45         168 $self->set_statistics_value(undef, 'crises', $crises);
1115 45         131 $self->set_statistics_value(undef, 'wars', $wars);
1116             }
1117              
1118             sub collect_events
1119             {
1120 45     45 0 63 my $self = shift;
1121 45         67 foreach my $n (@{$self->nations})
  45         161  
1122             {
1123 177         423 $self->set_statistics_value($n, 'progress', $n->progress);
1124             }
1125 45         84 foreach my $p (@{$self->players})
  45         190  
1126             {
1127 10         69 my $status = $self->player_stocks_status($p->name);
1128 10         40 $self->set_statistics_value($p, 'stock value', $status->{'stock_value'}, 'player');
1129 10         30 $self->set_statistics_value($p, 'money', $status->{'money'}, 'player');
1130 10         59 $self->set_statistics_value($p, 'total value', $status->{'total_value'}, 'player');
1131             }
1132             }
1133              
1134             ### Commands
1135              
1136             sub build_commands
1137             {
1138 4     4 0 29 my $self = shift;
1139 4         70 my $commands = BalanceOfPower::Commands->new( world => $self, log_name => 'bop-commands.log', log_active => $self->log_active, log_dir => $self->log_dir );
1140 4         8493 return $commands;
1141             }
1142              
1143             ### Logs
1144              
1145             sub set_log_dir
1146             {
1147 0     0 0   my $self = shift;
1148 0           my $log_dir = shift;
1149              
1150 0           $self->log_dir($log_dir);
1151 0           $self->dice->log_dir($log_dir);
1152              
1153 0           for(@{$self->nations})
  0            
1154             {
1155 0           $_->log_dir($log_dir);
1156             }
1157 0           for(@{$self->players})
  0            
1158             {
1159 0           $_->log_dir($log_dir);
1160             }
1161             }
1162              
1163              
1164             1;