File Coverage

lib/BalanceOfPower/World.pm
Criterion Covered Total %
statement 461 517 89.1
branch 152 198 76.7
condition 14 26 53.8
subroutine 46 48 95.8
pod 0 37 0.0
total 673 826 81.4


line stmt bran cond sub pod time code
1             package BalanceOfPower::World;
2             $BalanceOfPower::World::VERSION = '0.400105';
3 13     13   17826 use strict;
  13         18  
  13         282  
4 13     13   104 use v5.10;
  13         29  
5              
6 13     13   5699 use Moo;
  13         126805  
  13         60  
7 13     13   22376 use Data::Dumper;
  13         101970  
  13         878  
8 13     13   76 use Cwd 'abs_path';
  13         16  
  13         514  
9 13     13   52 use File::Path 'make_path';
  13         14  
  13         667  
10              
11 13     13   4036 use BalanceOfPower::Constants ':all';
  13         23  
  13         6819  
12 13     13   4170 use BalanceOfPower::Utils qw(prev_turn next_turn);
  13         23  
  13         790  
13 13     13   4305 use BalanceOfPower::Nation;
  13         31  
  13         444  
14 13     13   3583 use BalanceOfPower::Dice;
  13         22  
  13         328  
15 13     13   4876 use BalanceOfPower::Commands;
  13         34  
  13         56637  
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 1343     1343 0 7900 my $self = shift;
105 1343         1420 my $nation = shift;
106 1343 50       2157 if(! $nation)
107             {
108 0         0 say "Nation is undef";
109 0         0 return undef;
110             }
111 1343         972 my @nations = grep { $_->name eq $nation } @{$self->nations};
  6607         9216  
  1343         2821  
112 1343 50       1994 if(@nations > 0)
113             {
114 1343         2764 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 5 my $self = shift;
125 6         6 my $nation = shift;
126 6 50       12 return undef if(! $nation);
127 6 50       25 $nation = $self->nation_codes->{uc $nation} if(exists $self->nation_codes->{uc $nation});
128 6         4 for(@{$self->nation_names})
  6         13  
129             {
130 24 100       46 return $_ if(uc $_ eq uc $nation);
131             }
132 2         4 return undef;
133             }
134             sub check_nation_name
135             {
136 112     112 0 102 my $self = shift;
137 112         104 my $name = shift;
138 112         87 return grep {$_ eq $name} @{$self->nation_names};
  564         957  
  112         213  
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 28 my $self = shift;
149 16         23 my $datafile = shift;
150 16         114 my $file = $self->data_directory . "/" . $datafile;
151 16 50       693 open(my $nations_file, "<", $file) || die $!;
152 16         28 my $area;
153             my %nations_data;
154 16         349 for(<$nations_file>)
155             {
156 88         117 my $n = $_;
157 88         108 chomp $n;
158 88 100       180 if(! ($n =~ /^#/))
159             {
160 72         163 my ($name, $code, $size, $government) = split(',', $n);
161 72 100       148 if($government eq 'd')
    50          
162             {
163 58         69 $government = 'democracy';
164             }
165             elsif($government eq 'D')
166             {
167 14         29 $government = 'dictatorship';
168             }
169 72         255 $nations_data{$name} = { code => $code,
170             area => $area,
171             size => $size,
172             government => $government ,
173             }
174              
175             }
176             else
177             {
178 16         46 $n =~ /^# (.*)$/;
179 16         54 $area = $1;
180             }
181             }
182 16         222 return %nations_data;
183             }
184              
185             #Initial values, randomly generated
186             sub init_random
187             {
188 16     16 0 4662 my $self = shift;
189 16         30 my $datafile = shift;
190 16         29 my $bordersfile = shift;
191 16         61 my %nations_data = $self->load_nations_data($datafile);
192 16         39 my $flags = shift;
193              
194 16         26 my $trades = 1;
195 16         23 my $diplomacy = 1;
196 16         24 my $alliances = 1;
197 16 100       65 if($flags)
198             {
199             $trades = $flags->{'trades'}
200 15 100       62 if(exists $flags->{'trades'});
201             $diplomacy = $flags->{'diplomacy'}
202 15 100       51 if(exists $flags->{'diplomacy'});
203             $alliances = $flags->{'alliances'}
204 15 50       46 if(exists $flags->{'alliances'});
205              
206             }
207              
208 16         82 $self->delete_log();
209 16         95 $self->dice->delete_log();
210 16         48 my @nation_names = ();
211 16         78 foreach my $n (keys %nations_data)
212             {
213 72         133 push @nation_names, $n;
214 72 100       4531 say "Working on $n" if ! $self->silent;
215 72         1276 my $export_quote = $self->random10(MIN_EXPORT_QUOTE, MAX_EXPORT_QUOTE, "Export quote $n");
216 72 100       2578 say " export quote: $export_quote" if ! $self->silent;
217 72         1612 my $government_strength = $self->random10(MIN_GOVERNMENT_STRENGTH, MAX_GOVERNMENT_STRENGTH, "Government strenght $n");
218 72 100       2554 say " government strength: $government_strength" if ! $self->silent;
219              
220 72         1874 my $executive = BalanceOfPower::Executive->new( actor => $n );
221 72         566 $executive->init($self);
222 72         1670 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         90 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         2229 $self->nation_codes->{$nations_data{$n}->{code}} = $n;
236             }
237 16         86 $self->nation_names(\@nation_names);
238 16         101 $self->load_borders($bordersfile);
239 16 100       66 if($trades)
240             {
241 2 50       368 say "Trades generation..." if ! $self->silent;
242 2         26 $self->init_trades();
243             }
244             else
245             {
246 14 100       814 say "Trades generation skipped" if ! $self->silent;
247             }
248 16 50       61 if($diplomacy)
249             {
250 16 100       700 say "Diplomacy generation..." if ! $self->silent;
251 16         111 $self->init_diplomacy();
252             }
253             else
254             {
255 0 0       0 say "Diplomacy generation skipped" if ! $self->silent;
256             }
257 16 100       59 if($alliances)
258             {
259 1 50       292 say "Alliances generation..." if ! $self->silent;
260 1         8 $self->init_random_alliances();
261             }
262             else
263             {
264 15 100       879 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 2039 my $self = shift;
272 46         56 my $t = shift;
273 46         130 $self->init_year($t);
274 46         188 $self->war_current_year();
275 46         160 $self->player_start_turn();
276 46         164 $self->civil_war_current_year();
277 46         101 $self->war_debts();
278 46         249 $self->crisis_generator();
279             }
280             sub post_decisions_elaborations
281             {
282 45     45 0 72 my $self = shift;
283 45         178 $self->execute_stock_orders();
284 45         110 $self->execute_decisions();
285 45         98 $self->economy();
286 45         127 $self->civil_warfare();
287 45         113 $self->warfare();
288 45         138 $self->internal_conflict();
289 45         168 $self->player_targets();
290 45         117 $self->register_global_data();
291 45         106 $self->collect_events();
292             }
293              
294              
295              
296             sub elaborate_turn
297             {
298 19     19 0 345 my $self = shift;
299 19         22 my $t = shift;
300 19         41 $self->pre_decisions_elaborations($t);
301 19         53 $self->decisions();
302 19         71 $self->post_decisions_elaborations();
303             }
304              
305              
306              
307             #To automatically generate turns
308             sub autopilot
309             {
310 1     1 0 5 my $self = shift;
311 1         2 my $start = shift;
312 1         1 my $stop = shift;
313 1         4 $self->autoplay(1);
314 1         2 for($start..$stop)
315             {
316 1         1 my $y = $_;
317 1         4 foreach my $t (get_year_turns($y))
318             {
319 4         11 $self->elaborate_turn($t);
320             }
321             }
322 1         6 $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 64 my $self = shift;
333 47         60 my $turn = shift;
334 47 100       114 if(! $turn)
335             {
336 4         17 $turn = next_turn($self->current_year);
337             }
338             #$self->log("--- $turn ---");
339 47 100       1492 say $turn if $self->autoplay();
340 47         130 $self->current_year($turn);
341 47         54 foreach my $n (@{$self->nations})
  47         128  
342             {
343 188         332 $n->current_year($turn);
344 188         258 $n->wealth(0);
345 188         331 my $prod = $self->calculate_production($n);
346 188         425 $n->production($prod);
347 188         416 my $prestige = $self->calculate_prestige($n);
348 188         432 $n->prestige($prestige);
349 188         338 my $pu = PRODUCTION_UNITS->[$n->size];
350 188         418 $self->set_statistics_value($n, 'production', $prod);
351 188         517 $self->set_statistics_value($n, 'p/d', int(($prod / $pu) * 100) / 100);
352 188         411 $self->set_statistics_value($n, 'debt', $n->debt);
353 188         319 $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 157 my $self = shift;
366 188         134 my $nation = shift;
367              
368 188         419 my @newgov = $nation->get_events("NEW GOVERNMENT CREATED", prev_turn($nation->current_year));
369 188         377 my $previous_production = $self->get_statistics_value(prev_turn($nation->current_year), $nation->name, 'production');
370            
371 188 50       400 return () if(@newgov > 0);
372 188 100       327 return () if(! $previous_production);
373            
374 136         163 my @prods = ();
375 136         361 for(my $i = 0; $i < PRODUCTION_UNITS->[$nation->size]; $i++)
376             {
377 544         813 push @prods, $self->get_statistics_value(prev_turn($nation->current_year), $nation->name, 'production' . $i);
378             }
379 136         292 return @prods;
380             }
381             sub calculate_production
382             {
383 188     188 0 162 my $self = shift;
384 188         153 my $n = shift;
385 188         284 my @prev_prods = $self->get_base_production($n);
386 188         187 my @next_prods = ();
387 188         486 my $cost_for_retreat = 0;
388 188         335 my @retreats = $n->get_events("RETREAT FROM", prev_turn($n->current_year));
389 188         170 my $global_production = 0;
390 188         493 for(my $i = 0; $i < PRODUCTION_UNITS->[$n->size]; $i++)
391             {
392 752 100       958 if(@prev_prods > 0)
393             {
394 544         10744 $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         4108 $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       1506 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 100       1192 $next_prods[$i] = 0 if($next_prods[$i] < 0);
408 752 100       1002 $next_prods[$i] = MAX_PRODUCTION if($next_prods[$i] > MAX_PRODUCTION);
409              
410 752         2273 $self->set_statistics_value($n, 'production' . $i, $next_prods[$i]);
411 752         2046 $global_production += $next_prods[$i];
412             }
413 188 50       275 if($cost_for_retreat)
414             {
415 0         0 $self->send_event("COST FOR DEFEAT ON PRODUCTION: " . $cost_for_retreat);
416             }
417 188         309 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 56 my $self = shift;
425 46         121 for($self->influences->all())
426             {
427 4         10 $self->loot($_);
428             }
429 46         146 $self->situation_clock();
430             }
431              
432             sub loot
433             {
434 4     4 0 2 my $self = shift;
435 4         7 my $influence = shift;
436 4         121 my $n2 = $influence->node2;
437 4         7 my $n1 = $influence->node1;
438 4         10 my $quote = $influence->get_loot_quote();
439 4 50 33     13 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 203 my $self = shift;
461 188         142 my $nation = shift;
462 188         257 my $nation_name = $nation->name;
463 188         161 my $prestige = 0;
464 188         498 my @routes = $self->routes_for_node($nation_name);
465 188         187 $prestige += @routes;
466 188         423 my @supported = $self->supporter($nation_name);
467 188         191 $prestige += @supported;
468 188         475 my @influenced = $self->has_influence($nation_name);
469 188         214 $prestige += @influenced * INFLUENCE_PRESTIGE_BONUS;
470 188         158 my $bonus = 0;
471 188         500 my @ordered_best_w = $self->order_statistics(prev_turn($nation->current_year), 'w/d');
472 188 100       371 if(@ordered_best_w >= BEST_WEALTH_FOR_PRESTIGE)
473             {
474 122         227 for(my $i = 0; $i < BEST_WEALTH_FOR_PRESTIGE; $i++)
475             {
476 610 100       1165 if($ordered_best_w[$i]->{nation} eq $nation_name)
477             {
478 120         96 $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         462 my @ordered_best_p = $self->order_statistics(prev_turn($nation->current_year), 'progress');
488 188 100       363 if(@ordered_best_p >= BEST_PROGRESS_FOR_PRESTIGE)
489             {
490 122         241 for(my $i = 0; $i < BEST_PROGRESS_FOR_PRESTIGE; $i++)
491             {
492 610 100       1126 if($ordered_best_p[$i]->{nation} eq $nation_name)
493             {
494 120         100 $bonus += BEST_PROGRESS_FOR_PRESTIGE_BONUS;
495 120         595 $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         194 $prestige += $bonus;
505 188         531 my @wins = $nation->get_events("WAR BETWEEN .* AND .* WON BY ". $nation_name, prev_turn($nation->current_year));
506 188 50       346 if(@wins > 0)
507             {
508 0         0 $prestige += WAR_PRESTIGE_BONUS;
509             }
510 188         656 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 60 my $self = shift;
521 45         54 my @decisions = @{$self->ia_orders};
  45         119  
522 45         63 my @route_adders = ();
523             #foreach my $d (@decisions)
524 45         49 foreach my $n (@{$self->nation_names})
  45         110  
525             {
526 177         293 my $command = $self->control($n);
527 177 50       294 if(! $command)
528             {
529 177 100       173 my @nation_orders = grep { $_ =~ /^$n: / } @decisions; if(@nation_orders > 0)
  320         1849  
  177         277  
530             {
531 64         198 $nation_orders[0] =~ /^(.*): (.*)$/;
532 64         157 $command = $2;
533             }
534             else
535             {
536 113         156 next;
537             }
538             }
539 64         122 my $nation = $self->get_nation($n);
540 64 100       584 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 8         15 push @route_adders, $nation->name;
547             }
548             elsif($command =~ /^LOWER DISORDER$/)
549             {
550 1         4 $nation->lower_disorder($self);
551             }
552             elsif($command =~ /^BUILD TROOPS$/)
553             {
554 11         37 $nation->build_troops();
555             }
556             elsif($command =~ /^BOOST PRODUCTION$/)
557             {
558 6         14 $nation->boost_production();
559             }
560             elsif($command =~ /DECLARE WAR TO (.*)$/)
561             {
562 6         16 my $attacker = $nation;
563 6         13 my $defender = $self->get_nation($1);
564 6 50 66     28 if(! $self->war_busy($attacker->name) && ! $self->war_busy($defender->name))
565             {
566 4         21 $self->create_war($attacker, $defender);
567             }
568             }
569             elsif($command =~ /^MILITARY SUPPORT (.*)$/)
570             {
571 3         6 my $supporter = $nation;
572 3         9 my $supported = $self->get_nation($1);
573 3 50       19 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         7 $self->stop_military_support($supporter, $supported);
589             }
590             elsif($command =~ /^AID INSURGENTS IN (.*)$/)
591             {
592 6         17 my $victim = $self->get_nation($1);
593 6         18 $self->aid_insurgents($nation, $victim);
594             }
595             elsif($command =~ /^TREATY (.*) WITH (.*)$/)
596             {
597 5         11 my $nation2 = $self->get_nation($2);
598 5         16 $self->stipulate_treaty($nation, $nation2, $1);
599             }
600             elsif($command =~ /^ECONOMIC AID FOR (.*)$/)
601             {
602 1         3 my $nation2 = $self->get_nation($1);
603 1         7 $self->economic_aid($nation, $nation2);
604             }
605             elsif($command =~ /^REBEL MILITARY SUPPORT (.*)$/)
606             {
607 1         3 my $nation2 = $self->get_nation($1);
608 1         6 my $rebsup = $self->rebel_supported($nation2->name);
609 1 50       6 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 6         10 my $n2 = $1;
621 6 50       20 if($nation->prestige >= DIPLOMATIC_PRESSURE_PRESTIGE_COST)
622             {
623 6         19 my $under_infl = $self->is_under_influence($n2);
624 6   50     25 $under_infl ||= "";
625 6 50       18 if($under_infl ne $nation->name)
626             {
627 6         21 $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         4 my $supported = $self->get_nation($1);
638 1         5 $self->stop_rebel_military_support($nation, $supported);
639             }
640             elsif($command =~ /^MILITARY AID FOR (.*)$/)
641             {
642 6         15 my $nation2 = $self->get_nation($1);
643 6         23 $self->military_aid($nation, $nation2);
644             }
645             elsif($command =~ /^PROGRESS$/)
646             {
647 1         6 $nation->grow();
648 1         12 $self->broadcast_event({ code => 'progress',
649             text => "INVESTMENT IN PROGRESS FOR " . $nation->name,
650             involved => [$nation->name] }, $nation->name);
651             }
652 64         219 $self->set_statistics_value($nation, 'order', $command);
653             }
654 45         119 $self->empty_control_orders();
655 45         106 $self->manage_route_adding(@route_adders);
656             }
657             sub empty_control_orders
658             {
659 45     45 0 57 my $self = shift;
660 45         47 foreach my $p (@{$self->players})
  45         125  
661             {
662 10         25 $p->empty_control_orders();
663             }
664             }
665              
666             sub manage_route_adding
667             {
668 45     45 0 55 my $self = shift;
669 45         67 my @route_adders = @_;
670 45 100       126 if(@route_adders > 1)
671             {
672 3         64 @route_adders = $self->shuffle("Route adders", @route_adders);
673 3         5 my $done = 0;
674 3         11 while(! $done)
675             {
676 4         8 my $node1 = shift @route_adders;
677 4 50       17 if($self->suitable_route_creator($node1))
678             {
679 4 100       9 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 3         6 my $complete = 0;
689 3         5 foreach my $second (@route_adders)
690             {
691 4 100       14 if($self->suitable_new_route($node1, $second))
692             {
693 3         7 @route_adders = grep { $_ ne $second } @route_adders;
  4         9  
694 3         14 $self->generate_traderoute($node1, $second, 1);
695 3         6 $complete = 1;
696             }
697 4 100       13 last if $complete;
698             }
699 3 50       8 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 4 100       22 $done = 1 if(@route_adders == 0);
712             }
713             }
714             }
715             sub decisions
716             {
717 21     21 0 29 my $self = shift;
718 21         32 my @decisions = ();
719 21         23 foreach my $nation (@{$self->nations})
  21         70  
720             {
721 65         61 my $decision;
722 65         153 $decision = $nation->decision($self);
723 65 100       121 if($decision)
724             {
725 43         86 push @decisions, $decision;
726             }
727             }
728 21         88 $self->ia_orders(\@decisions);
729             }
730             sub control
731             {
732 177     177 0 136 my $self = shift;
733 177         155 my $nation = shift;
734 177         139 my $quote = -1;
735 177         129 my $winner = undef;
736 177         165 my @losers = ();
737 177         125 my $winner_command;
738 177         126 foreach my $player (@{$self->players})
  177         293  
739             {
740 28         62 my $player_command = $player->get_control_order($nation);
741 28 50       52 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       223 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         216 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 58 my $self = shift;
785 45         41 foreach my $n (@{$self->nations})
  45         103  
786             {
787 177         397 $n->calculate_internal_wealth();
788 177         489 $n->calculate_trading($self);
789 177         346 $n->convert_remains();
790 177 100       544 if($self->at_war($n->name))
791             {
792 20         48 $n->war_cost();
793             }
794 177 100       572 if($self->at_civil_war($n->name))
795             {
796 17         34 $n->civil_war_cost();
797             }
798              
799 177         264 my $wealth = $n->wealth;
800 177         287 my $pu = PRODUCTION_UNITS->[$n->size];
801 177         556 my $prod = $self->get_statistics_value($self->current_year, $n->name, 'production');
802              
803 177         390 $self->set_statistics_value($n, 'wealth', $n->wealth);
804 177         491 $self->set_statistics_value($n, 'w/d', int(($wealth / $pu) * 100) / 100);
805 177 50       249 if($prod != 0)
806             {
807 177         401 $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         3 my $nation1 = shift;
819 1         1 my $nation2 = shift;
820 1         6 $nation1->subtract_production('export', ECONOMIC_AID_COST);
821 1         5 $nation2->receive_aid($nation1->name);
822 1         13 $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         11 $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 56 my $self = shift;
836 45         53 foreach my $n (@{$self->nations})
  45         94  
837             {
838 177 100       442 if(! $self->get_civil_war($n->name))
839             {
840 162         316 $n->calculate_disorder($self);
841 162 100       256 if($n->internal_disorder_status eq 'Civil war')
842             {
843 1         3 $self->start_civil_war($n);
844             }
845             }
846 177         481 $self->set_statistics_value($n, 'internal disorder', $n->internal_disorder);
847             }
848             }
849              
850             sub aid_insurgents
851             {
852 6     6 0 10 my $self = shift;
853 6         6 my $nation1 = shift;
854 6         10 my $nation2 = shift;
855 6 50 33     42 if($nation1->production_for_export >= AID_INSURGENTS_COST && $nation2->internal_disorder_status ne 'Civil war')
856             {
857 6         73 $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 6         25 $nation1->subtract_production('export', AID_INSURGENTS_COST);
862 6         17 $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 187     187 0 157 my $self = shift;
874 187         153 my $n = shift;
875 187   100     380 return $self->at_civil_war($n) || $self->at_war($n);
876             }
877              
878             sub warfare
879             {
880 45     45 0 54 my $self = shift;
881 45         155 $self->fight_wars();
882 45         48 foreach my $n (@{$self->nations})
  45         121  
883             {
884 177         361 $self->set_statistics_value($n, 'army', $n->army);
885             }
886             }
887              
888             sub civil_warfare
889             {
890 45     45 0 57 my $self = shift;
891 45         51 foreach my $cw (@{$self->civil_wars})
  45         114  
892             {
893 17         43 my $winner = $cw->fight($self);
894 17 100       42 if($winner)
895             {
896 2         8 $cw->win($winner, $self);
897 2         7 $self->delete_civil_war($cw->nation_name);
898             }
899             }
900             }
901              
902             sub military_aid
903             {
904 6     6 0 10 my $self = shift;
905 6         7 my $nation1 = shift;
906 6         7 my $nation2 = shift;
907 6         23 $nation1->subtract_production('export', MILITARY_AID_COST);
908 6         21 $nation2->add_army(ARMY_UNIT);
909 6         89 $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 6         59 $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 23 my $self = shift;
918 13         24 my $message = shift;
919 13         18 my $nation = shift;
920 13         57 my @wars = $self->get_wars($nation);
921 13         45 for(@wars)
922             {
923 1         4 $_->register_event($message);
924             }
925             }
926              
927             sub civil_war_report
928             {
929 10     10 0 15 my $self = shift;
930 10         19 my $message = shift;
931 10         12 my $nation = shift;
932 10         39 my $cw = $self->get_civil_war($nation);
933 10 100       39 $cw->register_event($message) if $cw;
934             }
935              
936              
937             # WAR END ##################################################################
938              
939             # TREATIES #################################################################
940              
941             sub stipulate_treaty
942             {
943 5     5 0 9 my $self = shift;
944 5         7 my $nation1 = shift;
945 5         5 my $nation2 = shift;
946 5         7 my $type = shift;
947 5         22 my $present_treaty = $self->exists_treaty($nation1->name, $nation2->name);
948 5         23 my $diplomatic_status = $self->diplomacy_status($nation1->name, $nation2->name);
949 5 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 5 50 33     22 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 5 50       19 if($nation1->prestige >= TREATY_PRESTIGE_COST)
966             {
967 5 50 33     15 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 5 100       14 if($type eq 'COM')
    50          
977             {
978 3 50       9 if(! $present_treaty)
979             {
980 3 50       15 if($self->route_exists($nation1->name, $nation2->name))
981             {
982 3         16 $self->create_treaty($nation1->name, $nation2->name, 'commercial');
983 3         34 $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 2         9 $self->create_treaty($nation1->name, $nation2->name, 'no aggression');
998 2         22 $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 16 my $self = shift;
1015 5         6 my $from = shift;
1016 5         8 my @already = ();
1017 5         4 my %plan;
1018 5         12 $plan{'ground'} = {};
1019 5         7 $plan{'air'} = {};
1020 5         15 my @for_commerce = $self->route_destinations_for_node($from);
1021            
1022 5         24 my @at_borders = $self->near_nations($from, 1);
1023 5         10 foreach my $n(@for_commerce)
1024             {
1025 10 50       19 if(! grep { $_ eq $n } @already)
  2         6  
1026             {
1027 10         11 my $youcan = 'OK';
1028 10 100 100     20 $youcan = 'KO' if($self->war_busy($from) || $self->war_busy($n));
1029 10         24 $plan{'air'}->{$n}->{status} = $youcan;
1030 10         22 my $cost = $self->distance($from, $n) * AIR_TRAVEL_COST_FOR_DISTANCE;
1031 10 100       21 $cost = AIR_TRAVEL_CAP_COST if $cost > AIR_TRAVEL_CAP_COST;
1032 10 100       21 $plan{'air'}->{$n}->{cost} = $cost if($youcan eq 'OK');
1033 10 100       22 push @already, $n if $youcan eq 'OK';
1034             }
1035             }
1036 5         16 foreach my $n(@at_borders)
1037             {
1038 10 100       14 if(! grep { $_ eq $n } @already)
  15         24  
1039             {
1040 9         13 $plan{'ground'}->{$n}->{status} = 'OK';
1041 9         12 $plan{'ground'}->{$n}->{cost} = GROUND_TRAVEL_COST;
1042 9         12 push @already, $n;
1043             }
1044             }
1045 5         33 return %plan;
1046             }
1047              
1048             #########################################################################
1049              
1050             sub register_global_data
1051             {
1052 45     45 0 76 my $self = shift;
1053 45         134 my $crises = $self->get_all_crises();
1054 45         140 my $wars = $self->wars->all();
1055 45         111 $self->set_statistics_value(undef, 'crises', $crises);
1056 45         96 $self->set_statistics_value(undef, 'wars', $wars);
1057             }
1058              
1059             sub collect_events
1060             {
1061 45     45 0 57 my $self = shift;
1062 45         46 foreach my $n (@{$self->nations})
  45         109  
1063             {
1064 177         317 $self->set_statistics_value($n, 'progress', $n->progress);
1065             }
1066 45         61 foreach my $p (@{$self->players})
  45         153  
1067             {
1068 10         51 my $status = $self->player_stocks_status($p->name);
1069 10         36 $self->set_statistics_value($p, 'stock value', $status->{'stock_value'}, 'player');
1070 10         23 $self->set_statistics_value($p, 'money', $status->{'money'}, 'player');
1071 10         17 $self->set_statistics_value($p, 'total value', $status->{'total_value'}, 'player');
1072             }
1073             }
1074              
1075             ### Commands
1076              
1077             sub build_commands
1078             {
1079 4     4 0 19 my $self = shift;
1080 4         52 my $commands = BalanceOfPower::Commands->new( world => $self, log_name => 'bop-commands.log', log_active => $self->log_active, log_dir => $self->log_dir );
1081 4         6493 return $commands;
1082             }
1083              
1084             ### Logs
1085              
1086             sub set_log_dir
1087             {
1088 0     0 0   my $self = shift;
1089 0           my $log_dir = shift;
1090              
1091 0           $self->log_dir($log_dir);
1092 0           $self->dice->log_dir($log_dir);
1093              
1094 0           for(@{$self->nations})
  0            
1095             {
1096 0           $_->log_dir($log_dir);
1097             }
1098 0           for(@{$self->players})
  0            
1099             {
1100 0           $_->log_dir($log_dir);
1101             }
1102             }
1103              
1104              
1105             1;