File Coverage

lib/BalanceOfPower/World.pm
Criterion Covered Total %
statement 463 541 85.5
branch 150 202 74.2
condition 15 26 57.6
subroutine 46 49 93.8
pod 0 38 0.0
total 674 856 78.7


line stmt bran cond sub pod time code
1             package BalanceOfPower::World;
2             $BalanceOfPower::World::VERSION = '0.400110';
3 13     13   16490 use strict;
  13         15  
  13         264  
4 13     13   93 use v5.10;
  13         22  
5              
6 13     13   5327 use Moo;
  13         115352  
  13         49  
7 13     13   19129 use Data::Dumper;
  13         88459  
  13         622  
8 13     13   59 use Cwd 'abs_path';
  13         12  
  13         441  
9 13     13   44 use File::Path 'make_path';
  13         14  
  13         571  
10              
11 13     13   3659 use BalanceOfPower::Constants ':all';
  13         19  
  13         6098  
12 13     13   3392 use BalanceOfPower::Utils qw(prev_turn next_turn);
  13         18  
  13         745  
13 13     13   3552 use BalanceOfPower::Nation;
  13         29  
  13         364  
14 13     13   3426 use BalanceOfPower::Dice;
  13         24  
  13         336  
15 13     13   4771 use BalanceOfPower::Commands;
  13         23  
  13         59515  
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 1390     1390 0 7773 my $self = shift;
105 1390         1138 my $nation = shift;
106 1390 50       2124 if(! $nation)
107             {
108 0         0 say "Nation is undef";
109 0         0 return undef;
110             }
111 1390         955 my @nations = grep { $_->name eq $nation } @{$self->nations};
  6848         9066  
  1390         2252  
112 1390 50       1948 if(@nations > 0)
113             {
114 1390         2635 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 6 my $self = shift;
125 6         7 my $nation = shift;
126 6 50       11 return undef if(! $nation);
127 6 50       22 $nation = $self->nation_codes->{uc $nation} if(exists $self->nation_codes->{uc $nation});
128 6         4 for(@{$self->nation_names})
  6         16  
129             {
130 23 100       44 return $_ if(uc $_ eq uc $nation);
131             }
132 2         5 return undef;
133             }
134             sub check_nation_name
135             {
136 112     112 0 94 my $self = shift;
137 112         103 my $name = shift;
138 112         87 return grep {$_ eq $name} @{$self->nation_names};
  564         876  
  112         172  
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 22 my $self = shift;
149 16         26 my $datafile = shift;
150 16         101 my $file = $self->data_directory . "/" . $datafile;
151 16 50       604 open(my $nations_file, "<", $file) || die $!;
152 16         21 my $area;
153             my %nations_data;
154 16         296 for(<$nations_file>)
155             {
156 88         98 my $n = $_;
157 88         82 chomp $n;
158 88 100       151 if(! ($n =~ /^#/))
159             {
160 72         156 my ($name, $code, $size, $government) = split(',', $n);
161 72 100       130 if($government eq 'd')
    50          
162             {
163 58         55 $government = 'democracy';
164             }
165             elsif($government eq 'D')
166             {
167 14         23 $government = 'dictatorship';
168             }
169 72         222 $nations_data{$name} = { code => $code,
170             area => $area,
171             size => $size,
172             government => $government ,
173             }
174              
175             }
176             else
177             {
178 16         44 $n =~ /^# (.*)$/;
179 16         47 $area = $1;
180             }
181             }
182 16         226 return %nations_data;
183             }
184              
185             #Initial values, randomly generated
186             sub init_random
187             {
188 16     16 0 4471 my $self = shift;
189 16         30 my $datafile = shift;
190 16         24 my $bordersfile = shift;
191 16         45 my %nations_data = $self->load_nations_data($datafile);
192 16         32 my $flags = shift;
193              
194 16         24 my $trades = 1;
195 16         18 my $diplomacy = 1;
196 16         20 my $alliances = 1;
197 16 100       78 if($flags)
198             {
199             $trades = $flags->{'trades'}
200 15 100       54 if(exists $flags->{'trades'});
201             $diplomacy = $flags->{'diplomacy'}
202 15 100       38 if(exists $flags->{'diplomacy'});
203             $alliances = $flags->{'alliances'}
204 15 50       40 if(exists $flags->{'alliances'});
205              
206             }
207              
208 16         69 $self->delete_log();
209 16         88 $self->dice->delete_log();
210 16         38 my @nation_names = ();
211 16         68 foreach my $n (keys %nations_data)
212             {
213 72         116 push @nation_names, $n;
214 72 100       3569 say "Working on $n" if ! $self->silent;
215 72         1238 my $export_quote = $self->random10(MIN_EXPORT_QUOTE, MAX_EXPORT_QUOTE, "Export quote $n");
216 72 100       2322 say " export quote: $export_quote" if ! $self->silent;
217 72         1624 my $government_strength = $self->random10(MIN_GOVERNMENT_STRENGTH, MAX_GOVERNMENT_STRENGTH, "Government strenght $n");
218 72 100       2283 say " government strength: $government_strength" if ! $self->silent;
219              
220 72         1465 my $executive = BalanceOfPower::Executive->new( actor => $n );
221 72         512 $executive->init($self);
222 72         1532 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         74 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         1940 $self->nation_codes->{$nations_data{$n}->{code}} = $n;
236             }
237 16         68 $self->nation_names(\@nation_names);
238 16         64 $self->load_borders($bordersfile);
239 16 100       62 if($trades)
240             {
241 2 50       126 say "Trades generation..." if ! $self->silent;
242 2         18 $self->init_trades();
243             }
244             else
245             {
246 14 100       553 say "Trades generation skipped" if ! $self->silent;
247             }
248 16 50       51 if($diplomacy)
249             {
250 16 100       546 say "Diplomacy generation..." if ! $self->silent;
251 16         89 $self->init_diplomacy();
252             }
253             else
254             {
255 0 0       0 say "Diplomacy generation skipped" if ! $self->silent;
256             }
257 16 100       55 if($alliances)
258             {
259 1 50       33 say "Alliances generation..." if ! $self->silent;
260 1         6 $self->init_random_alliances();
261             }
262             else
263             {
264 15 100       738 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 1905 my $self = shift;
272 46         51 my $t = shift;
273 46         99 $self->init_year($t);
274 46         152 $self->war_current_year();
275 46         126 $self->player_start_turn();
276 46         130 $self->civil_war_current_year();
277 46         85 $self->war_debts();
278 46         195 $self->crisis_generator();
279             }
280             sub post_decisions_elaborations
281             {
282 45     45 0 57 my $self = shift;
283 45         141 $self->execute_stock_orders();
284 45         88 $self->execute_decisions();
285 45         158 $self->economy();
286 45         100 $self->civil_warfare();
287 45         93 $self->warfare();
288 45         92 $self->internal_conflict();
289 45         126 $self->player_targets();
290 45         105 $self->register_global_data();
291 45         85 $self->collect_events();
292             }
293              
294              
295              
296             sub elaborate_turn
297             {
298 19     19 0 344 my $self = shift;
299 19         19 my $t = shift;
300 19         36 $self->pre_decisions_elaborations($t);
301 19         50 $self->decisions();
302 19         45 $self->post_decisions_elaborations();
303             }
304              
305              
306              
307             #To automatically generate turns
308             sub autopilot
309             {
310 1     1 0 4 my $self = shift;
311 1         2 my $start = shift;
312 1         0 my $stop = shift;
313 1         3 $self->autoplay(1);
314 1         3 for($start..$stop)
315             {
316 1         1 my $y = $_;
317 1         4 foreach my $t (get_year_turns($y))
318             {
319 4         7 $self->elaborate_turn($t);
320             }
321             }
322 1         4 $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 53 my $self = shift;
333 47         51 my $turn = shift;
334 47 100       102 if(! $turn)
335             {
336 4         17 $turn = next_turn($self->current_year);
337             }
338             #$self->log("--- $turn ---");
339 47 100       1160 say $turn if $self->autoplay();
340 47         117 $self->current_year($turn);
341 47         49 foreach my $n (@{$self->nations})
  47         113  
342             {
343 188         322 $n->current_year($turn);
344 188         234 $n->wealth(0);
345 188         298 my $prod = $self->calculate_production($n);
346 188         410 $n->production($prod);
347 188         362 my $prestige = $self->calculate_prestige($n);
348 188         305 $n->prestige($prestige);
349 188         294 my $pu = PRODUCTION_UNITS->[$n->size];
350 188         388 $self->set_statistics_value($n, 'production', $prod);
351 188         501 $self->set_statistics_value($n, 'p/d', int(($prod / $pu) * 100) / 100);
352 188         457 $self->set_statistics_value($n, 'debt', $n->debt);
353 188         265 $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 129 my $self = shift;
366 188         140 my $nation = shift;
367              
368 188         401 my @newgov = $nation->get_events("NEW GOVERNMENT CREATED", prev_turn($nation->current_year));
369 188         387 my $previous_production = $self->get_statistics_value(prev_turn($nation->current_year), $nation->name, 'production');
370            
371 188 50       325 return () if(@newgov > 0);
372 188 100       317 return () if(! $previous_production);
373            
374 136         135 my @prods = ();
375 136         363 for(my $i = 0; $i < PRODUCTION_UNITS->[$nation->size]; $i++)
376             {
377 544         810 push @prods, $self->get_statistics_value(prev_turn($nation->current_year), $nation->name, 'production' . $i);
378             }
379 136         271 return @prods;
380             }
381             sub calculate_production
382             {
383 188     188 0 149 my $self = shift;
384 188         125 my $n = shift;
385 188         294 my @prev_prods = $self->get_base_production($n);
386 188         187 my @next_prods = ();
387 188         141 my $cost_for_retreat = 0;
388 188         326 my @retreats = $n->get_events("RETREAT FROM", prev_turn($n->current_year));
389 188         174 my $global_production = 0;
390 188         455 for(my $i = 0; $i < PRODUCTION_UNITS->[$n->size]; $i++)
391             {
392 752 100       966 if(@prev_prods > 0)
393             {
394 544         10859 $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         4023 $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       1491 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       1105 $next_prods[$i] = 0 if($next_prods[$i] < 0);
408 752 100       978 $next_prods[$i] = MAX_PRODUCTION if($next_prods[$i] > MAX_PRODUCTION);
409              
410 752         2311 $self->set_statistics_value($n, 'production' . $i, $next_prods[$i]);
411 752         2093 $global_production += $next_prods[$i];
412             }
413 188 50       260 if($cost_for_retreat)
414             {
415 0         0 $self->send_event("COST FOR DEFEAT ON PRODUCTION: " . $cost_for_retreat);
416             }
417 188         304 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 51 my $self = shift;
425 46         182 for($self->influences->all())
426             {
427 4         6 $self->loot($_);
428             }
429 46         109 $self->situation_clock();
430             }
431              
432             sub loot
433             {
434 4     4 0 5 my $self = shift;
435 4         4 my $influence = shift;
436 4         7 my $n2 = $influence->node2;
437 4         3 my $n1 = $influence->node1;
438 4         8 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 189 my $self = shift;
461 188         156 my $nation = shift;
462 188         234 my $nation_name = $nation->name;
463 188         162 my $prestige = 0;
464 188         487 my @routes = $self->routes_for_node($nation_name);
465 188         208 $prestige += @routes;
466 188         407 my @supported = $self->supporter($nation_name);
467 188         162 $prestige += @supported;
468 188         434 my @influenced = $self->has_influence($nation_name);
469 188         209 $prestige += @influenced * INFLUENCE_PRESTIGE_BONUS;
470 188         152 my $bonus = 0;
471 188         522 my @ordered_best_w = $self->order_statistics(prev_turn($nation->current_year), 'w/d');
472 188 100       355 if(@ordered_best_w >= BEST_WEALTH_FOR_PRESTIGE)
473             {
474 122         216 for(my $i = 0; $i < BEST_WEALTH_FOR_PRESTIGE; $i++)
475             {
476 610 100       1183 if($ordered_best_w[$i]->{nation} eq $nation_name)
477             {
478 120         84 $bonus += BEST_WEALTH_FOR_PRESTIGE_BONUS;
479 120         627 $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         452 my @ordered_best_p = $self->order_statistics(prev_turn($nation->current_year), 'progress');
488 188 100       371 if(@ordered_best_p >= BEST_PROGRESS_FOR_PRESTIGE)
489             {
490 122         258 for(my $i = 0; $i < BEST_PROGRESS_FOR_PRESTIGE; $i++)
491             {
492 610 100       1090 if($ordered_best_p[$i]->{nation} eq $nation_name)
493             {
494 120         97 $bonus += BEST_PROGRESS_FOR_PRESTIGE_BONUS;
495 120         579 $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         191 $prestige += $bonus;
505 188         487 my @wins = $nation->get_events("WAR BETWEEN .* AND .* WON BY ". $nation_name, prev_turn($nation->current_year));
506 188 50       340 if(@wins > 0)
507             {
508 0         0 $prestige += WAR_PRESTIGE_BONUS;
509             }
510 188         601 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 40 my $self = shift;
521 45         45 my @decisions = @{$self->ia_orders};
  45         94  
522 45         55 my @route_adders = ();
523             #foreach my $d (@decisions)
524 45         39 foreach my $n (@{$self->nation_names})
  45         94  
525             {
526 177         272 my $command = $self->control($n);
527 177 50       239 if(! $command)
528             {
529 177 100       148 my @nation_orders = grep { $_ =~ /^$n: / } @decisions; if(@nation_orders > 0)
  320         1780  
  177         232  
530             {
531 64         196 $nation_orders[0] =~ /^(.*): (.*)$/;
532 64         137 $command = $2;
533             }
534             else
535             {
536 113         137 next;
537             }
538             }
539 64         122 my $nation = $self->get_nation($n);
540 64 100       518 if($command =~ /^DELETE TRADEROUTE (.*)->(.*)$/)
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
541             {
542 1         5 $self->delete_route($1, $2);
543             }
544             elsif($command =~ /^ADD ROUTE$/)
545             {
546 5         9 push @route_adders, $nation->name;
547             }
548             elsif($command =~ /^LOWER DISORDER$/)
549             {
550 2         9 $nation->lower_disorder($self);
551             }
552             elsif($command =~ /^BUILD TROOPS$/)
553             {
554 14         45 $nation->build_troops();
555             }
556             elsif($command =~ /^BOOST PRODUCTION$/)
557             {
558 6         18 $nation->boost_production();
559             }
560             elsif($command =~ /DECLARE WAR TO (.*)$/)
561             {
562 6         9 my $attacker = $nation;
563 6         16 my $defender = $self->get_nation($1);
564 6 50 66     26 if(! $self->war_busy($attacker->name) && ! $self->war_busy($defender->name))
565             {
566 4         16 $self->create_war($attacker, $defender);
567             }
568             }
569             elsif($command =~ /^MILITARY SUPPORT (.*)$/)
570             {
571 3         4 my $supporter = $nation;
572 3         11 my $supported = $self->get_nation($1);
573 3 50       18 if($supported->accept_military_support($supporter->name, $self))
574             {
575 3         15 $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         3 my $supported = $self->get_nation($1);
588 1         4 $self->stop_military_support($supporter, $supported);
589             }
590             elsif($command =~ /^AID INSURGENTS IN (.*)$/)
591             {
592 5         12 my $victim = $self->get_nation($1);
593 5         13 $self->aid_insurgents($nation, $victim);
594             }
595             elsif($command =~ /^TREATY (.*) WITH (.*)$/)
596             {
597 4         11 my $nation2 = $self->get_nation($2);
598 4         12 $self->stipulate_treaty($nation, $nation2, $1);
599             }
600             elsif($command =~ /^ECONOMIC AID FOR (.*)$/)
601             {
602 1         2 my $nation2 = $self->get_nation($1);
603 1         4 $self->economic_aid($nation, $nation2);
604             }
605             elsif($command =~ /^REBEL MILITARY SUPPORT (.*)$/)
606             {
607 1         2 my $nation2 = $self->get_nation($1);
608 1         5 my $rebsup = $self->rebel_supported($nation2->name);
609 1 50       5 if($self->at_civil_war($nation2->name))
610             {
611 1         5 $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         9 my $n2 = $1;
621 4 50       16 if($nation->prestige >= DIPLOMATIC_PRESSURE_PRESTIGE_COST)
622             {
623 4         13 my $under_infl = $self->is_under_influence($n2);
624 4   50     20 $under_infl ||= "";
625 4 50       17 if($under_infl ne $nation->name)
626             {
627 4         19 $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         3 my $supported = $self->get_nation($1);
638 1         4 $self->stop_rebel_military_support($nation, $supported);
639             }
640             elsif($command =~ /^MILITARY AID FOR (.*)$/)
641             {
642 9         18 my $nation2 = $self->get_nation($1);
643 9         27 $self->military_aid($nation, $nation2);
644             }
645             elsif($command =~ /^PROGRESS$/)
646             {
647 1         6 $nation->grow();
648 1         11 $self->broadcast_event({ code => 'progress',
649             text => "INVESTMENT IN PROGRESS FOR " . $nation->name,
650             involved => [$nation->name] }, $nation->name);
651             }
652 64         186 $self->set_statistics_value($nation, 'order', $command);
653             }
654 45         97 $self->empty_control_orders();
655 45         94 $self->manage_route_adding(@route_adders);
656             }
657             sub empty_control_orders
658             {
659 45     45 0 48 my $self = shift;
660 45         45 foreach my $p (@{$self->players})
  45         99  
661             {
662 10         23 $p->empty_control_orders();
663             }
664             }
665              
666             sub manage_route_adding
667             {
668 45     45 0 52 my $self = shift;
669 45         61 my @route_adders = @_;
670 45 100       116 if(@route_adders > 1)
671             {
672 2         39 @route_adders = $self->shuffle("Route adders", @route_adders);
673 2         5 my $done = 0;
674 2         6 while(! $done)
675             {
676 3         6 my $node1 = shift @route_adders;
677 3 50       11 if($self->suitable_route_creator($node1))
678             {
679 3 100       8 if(@route_adders == 0)
680             {
681 1         6 $self->broadcast_event( { code => "tradelack",
682             text => "TRADEROUTE CREATION FAILED FOR LACK OF PARTNERS FOR $node1",
683             involved => [$node1] }, $node1);
684 1         2 $done = 1;
685             }
686             else
687             {
688 2         3 my $complete = 0;
689 2         4 foreach my $second (@route_adders)
690             {
691 2 50       8 if($self->suitable_new_route($node1, $second))
692             {
693 2         5 @route_adders = grep { $_ ne $second } @route_adders;
  3         5  
694 2         9 $self->generate_traderoute($node1, $second, 1);
695 2         3 $complete = 1;
696             }
697 2 50       9 last if $complete;
698             }
699 2 50       7 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 3 100       16 $done = 1 if(@route_adders == 0);
712             }
713             }
714             }
715             sub decisions
716             {
717 21     21 0 21 my $self = shift;
718 21         30 my @decisions = ();
719 21         20 foreach my $nation (@{$self->nations})
  21         60  
720             {
721 65         44 my $decision;
722 65         150 $decision = $nation->decision($self);
723 65 100       105 if($decision)
724             {
725 43         86 push @decisions, $decision;
726             }
727             }
728 21         71 $self->ia_orders(\@decisions);
729             }
730             sub control
731             {
732 177     177 0 135 my $self = shift;
733 177         126 my $nation = shift;
734 177         128 my $quote = -1;
735 177         124 my $winner = undef;
736 177         146 my @losers = ();
737 177         123 my $winner_command;
738 177         113 foreach my $player (@{$self->players})
  177         264  
739             {
740 28         58 my $player_command = $player->get_control_order($nation);
741 28 50       47 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       211 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         193 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 46 my $self = shift;
785 45         40 foreach my $n (@{$self->nations})
  45         96  
786             {
787 177         370 $n->calculate_internal_wealth();
788 177         427 $n->calculate_trading($self);
789 177         309 $n->convert_remains();
790 177 100       592 if($self->at_war($n->name))
791             {
792 20         45 $n->war_cost();
793             }
794 177 100       506 if($self->at_civil_war($n->name))
795             {
796 17         42 $n->civil_war_cost();
797             }
798              
799 177         256 my $wealth = $n->wealth;
800 177         253 my $pu = PRODUCTION_UNITS->[$n->size];
801 177         518 my $prod = $self->get_statistics_value($self->current_year, $n->name, 'production');
802              
803 177         364 $self->set_statistics_value($n, 'wealth', $n->wealth);
804 177         545 $self->set_statistics_value($n, 'w/d', int(($wealth / $pu) * 100) / 100);
805 177 50       257 if($prod != 0)
806             {
807 177         368 $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         1 my $nation1 = shift;
819 1         1 my $nation2 = shift;
820 1         4 $nation1->subtract_production('export', ECONOMIC_AID_COST);
821 1         4 $nation2->receive_aid($nation1->name);
822 1         12 $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         9 $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 50 my $self = shift;
836 45         42 foreach my $n (@{$self->nations})
  45         87  
837             {
838 177 100       412 if(! $self->get_civil_war($n->name))
839             {
840 162         285 $n->calculate_disorder($self);
841 162 100       244 if($n->internal_disorder_status eq 'Civil war')
842             {
843 1         3 $self->start_civil_war($n);
844             }
845             }
846 177         442 $self->set_statistics_value($n, 'internal disorder', $n->internal_disorder);
847             }
848             }
849              
850             sub aid_insurgents
851             {
852 5     5 0 6 my $self = shift;
853 5         6 my $nation1 = shift;
854 5         6 my $nation2 = shift;
855 5 50 33     31 if($nation1->production_for_export >= AID_INSURGENTS_COST && $nation2->internal_disorder_status ne 'Civil war')
856             {
857 5         48 $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 5         17 $nation1->subtract_production('export', AID_INSURGENTS_COST);
862 5         13 $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 196     196 0 166 my $self = shift;
874 196         146 my $n = shift;
875 196   100     403 return $self->at_civil_war($n) || $self->at_war($n);
876             }
877              
878             sub warfare
879             {
880 45     45 0 47 my $self = shift;
881 45         120 $self->fight_wars();
882 45         36 foreach my $n (@{$self->nations})
  45         110  
883             {
884 177         326 $self->set_statistics_value($n, 'army', $n->army);
885             }
886             }
887              
888             sub civil_warfare
889             {
890 45     45 0 41 my $self = shift;
891 45         45 foreach my $cw (@{$self->civil_wars})
  45         92  
892             {
893 17         41 my $winner = $cw->fight($self);
894 17 100       44 if($winner)
895             {
896 2         8 $cw->win($winner, $self);
897 2         4 $self->delete_civil_war($cw->nation_name);
898             }
899             }
900             }
901              
902             sub military_aid
903             {
904 9     9 0 12 my $self = shift;
905 9         7 my $nation1 = shift;
906 9         8 my $nation2 = shift;
907 9         25 $nation1->subtract_production('export', MILITARY_AID_COST);
908 9         22 $nation2->add_army(ARMY_UNIT);
909 9         80 $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 9         54 $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 22 my $self = shift;
918 13         18 my $message = shift;
919 13         16 my $nation = shift;
920 13         49 my @wars = $self->get_wars($nation);
921 13         40 for(@wars)
922             {
923 1         4 $_->register_event($message);
924             }
925             }
926              
927             sub civil_war_report
928             {
929 10     10 0 14 my $self = shift;
930 10         11 my $message = shift;
931 10         12 my $nation = shift;
932 10         29 my $cw = $self->get_civil_war($nation);
933 10 100       30 $cw->register_event($message) if $cw;
934             }
935              
936              
937             # WAR END ##################################################################
938              
939             # TREATIES #################################################################
940              
941             sub stipulate_treaty
942             {
943 4     4 0 5 my $self = shift;
944 4         5 my $nation1 = shift;
945 4         4 my $nation2 = shift;
946 4         5 my $type = shift;
947 4         18 my $present_treaty = $self->exists_treaty($nation1->name, $nation2->name);
948 4         17 my $diplomatic_status = $self->diplomacy_status($nation1->name, $nation2->name);
949 4 50       13 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 4 50 33     16 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 4 50       14 if($nation1->prestige >= TREATY_PRESTIGE_COST)
966             {
967 4 100 66     17 if($present_treaty && $present_treaty->type ne 'alliance')
968             {
969 1         5 $self->create_treaty($nation1->name, $nation2->name, 'alliance');
970 1         20 $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 3 100       11 if($type eq 'COM')
    50          
977             {
978 2 50       5 if(! $present_treaty)
979             {
980 2 50       13 if($self->route_exists($nation1->name, $nation2->name))
981             {
982 2         14 $self->create_treaty($nation1->name, $nation2->name, 'commercial');
983 2         28 $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 1         5 $self->create_treaty($nation1->name, $nation2->name, 'no aggression');
998 1         14 $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 18 my $self = shift;
1015 5         6 my $from = shift;
1016 5         7 my @already = ();
1017 5         6 my %plan;
1018 5         10 $plan{'ground'} = {};
1019 5         5 $plan{'air'} = {};
1020 5         17 my @for_commerce = $self->route_destinations_for_node($from);
1021            
1022 5         19 my @at_borders = $self->near_nations($from, 1);
1023 5         11 foreach my $n(@for_commerce)
1024             {
1025 10 50       16 if(! grep { $_ eq $n } @already)
  4         8  
1026             {
1027 10         10 my $youcan = 'OK';
1028 10 100 100     17 $youcan = 'KO' if($self->war_busy($from) || $self->war_busy($n));
1029 10         24 $plan{'air'}->{$n}->{status} = $youcan;
1030 10         21 my $cost = $self->distance($from, $n) * AIR_TRAVEL_COST_FOR_DISTANCE;
1031 10 100       20 $cost = AIR_TRAVEL_CAP_COST if $cost > AIR_TRAVEL_CAP_COST;
1032 10 100       20 $plan{'air'}->{$n}->{cost} = $cost if($youcan eq 'OK');
1033 10 100       25 push @already, $n if $youcan eq 'OK';
1034             }
1035             }
1036 5         8 foreach my $n(@at_borders)
1037             {
1038 10 100       11 if(! grep { $_ eq $n } @already)
  15         24  
1039             {
1040 9         15 $plan{'ground'}->{$n}->{status} = 'OK';
1041 9         9 $plan{'ground'}->{$n}->{cost} = GROUND_TRAVEL_COST;
1042 9         12 push @already, $n;
1043             }
1044             }
1045 5         36 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 46 my $self = shift;
1112 45         114 my $crises = $self->get_all_crises();
1113 45         127 my $wars = $self->wars->all();
1114 45         114 $self->set_statistics_value(undef, 'crises', $crises);
1115 45         76 $self->set_statistics_value(undef, 'wars', $wars);
1116             }
1117              
1118             sub collect_events
1119             {
1120 45     45 0 46 my $self = shift;
1121 45         45 foreach my $n (@{$self->nations})
  45         91  
1122             {
1123 177         302 $self->set_statistics_value($n, 'progress', $n->progress);
1124             }
1125 45         47 foreach my $p (@{$self->players})
  45         134  
1126             {
1127 10         41 my $status = $self->player_stocks_status($p->name);
1128 10         26 $self->set_statistics_value($p, 'stock value', $status->{'stock_value'}, 'player');
1129 10         19 $self->set_statistics_value($p, 'money', $status->{'money'}, 'player');
1130 10         19 $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 19 my $self = shift;
1139 4         49 my $commands = BalanceOfPower::Commands->new( world => $self, log_name => 'bop-commands.log', log_active => $self->log_active, log_dir => $self->log_dir );
1140 4         5385 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;