File Coverage

lib/BalanceOfPower/Role/Warlord.pm
Criterion Covered Total %
statement 295 330 89.3
branch 77 106 72.6
condition 18 26 69.2
subroutine 17 19 89.4
pod 0 11 0.0
total 407 492 82.7


line stmt bran cond sub pod time code
1             package BalanceOfPower::Role::Warlord;
2             $BalanceOfPower::Role::Warlord::VERSION = '0.400110';
3 13     13   4765 use strict;
  13         18  
  13         276  
4 13     13   89 use v5.10;
  13         34  
5              
6 13     13   39 use Moo::Role;
  13         12  
  13         59  
7              
8 13     13   2427 use Term::ANSIColor;
  13         24  
  13         567  
9              
10 13     13   45 use BalanceOfPower::Constants ':all';
  13         15  
  13         5377  
11 13     13   62 use BalanceOfPower::Printer;
  13         15  
  13         208  
12 13     13   3281 use BalanceOfPower::Relations::Crisis;
  13         19  
  13         294  
13 13     13   3395 use BalanceOfPower::Relations::War;
  13         22  
  13         29277  
14              
15             requires 'empire';
16             requires 'border_exists';
17             requires 'get_nation';
18             requires 'get_hates';
19             requires 'occupy';
20             requires 'broadcast_event';
21             requires 'send_event';
22             requires 'get_group_borders';
23             requires 'get_allies';
24             requires 'supported';
25             requires 'supporter';
26             requires 'military_support_garbage_collector';
27             requires 'random';
28             requires 'change_diplomacy';
29             requires 'get_crises';
30             requires 'delete_crisis';
31             requires 'discard_war_bonds';
32             requires 'cash_war_bonds';
33             requires 'war_busy';
34              
35             has wars => (
36             is => 'ro',
37             default => sub { BalanceOfPower::Relations::RelPack->new() },
38             handles => { at_war => 'first_link_for_node',
39             add_war => 'add_link',
40             get_wars => 'links_for_node',
41             war_exists => 'exists_link',
42             delete_war_link => 'delete_link',
43             get_attackers => 'links_for_node2'
44             }
45             );
46              
47             has memorial => (
48             is => 'rw',
49             default => sub { [] }
50             );
51              
52              
53              
54              
55              
56             sub in_military_range
57             {
58 205     205 0 159 my $self = shift;
59 205         157 my $nation1 = shift;
60 205         141 my $nation2 = shift;
61 205   50     532 my $hostile = shift || 1;
62 205 100       452 if($self->border_exists($nation1, $nation2))
63             {
64 81         292 return { how => 'border', who => undef };
65             }
66 124         251 my @supported = $self->supporter($nation1);
67 124         156 for(@supported)
68             {
69 12         31 my $nation_supported = $_->destination($nation1);
70 12         32 my $treaty = $self->exists_treaty_by_type($nation_supported, $nation2, 'no aggression');
71 12 100 33     46 if(! $hostile || ! $treaty)
72             {
73 11 100       94 if(! $self->war_busy($nation_supported))
74             {
75 7 50       14 if($nation_supported eq $nation2)
76             {
77 0         0 return { how => 'supporting', who => undef};
78             }
79 7 100       16 if($self->border_exists($nation_supported, $nation2))
80             {
81 5         33 return { how => 'support', who => $nation_supported};
82             }
83             }
84             else
85             {
86             }
87             }
88             }
89 119         255 my @empire = $self->empire($nation1);
90 119         136 for(@empire)
91             {
92 123         84 my $ally = $_;
93 123 50       242 if(! $self->war_busy($ally))
94             {
95 123 100       191 return { how => 'linked', who => undef } if $ally eq $nation2;
96 122 100       235 return { how => 'control', who => $ally } if $self->border_exists($ally, $nation2);
97             }
98             }
99 116         297 return 0;
100             }
101              
102             sub war_current_year
103             {
104 46     46 0 46 my $self = shift;
105 46         173 for($self->wars->all)
106             {
107 6         32 $_->current_year($self->current_year);
108             }
109             }
110              
111             sub create_war
112             {
113 7     7 0 10 my $self = shift;
114 7   50     19 my $attacker = shift || "";
115 7   50     20 my $defender = shift || "";
116              
117 7 50       116 if(! $self->war_exists($attacker->name, $defender->name))
118             {
119 7         83 $self->broadcast_event({ code => 'crisisescalate',
120             text => "CRISIS BETWEEN " . $attacker->name . " AND " . $defender->name . " BECAME WAR",
121             involved => [$attacker->name, $defender->name] }, $attacker->name, $defender->name);
122 7         35 my @attacker_coalition = $self->empire($attacker->name);
123 7         15 @attacker_coalition = grep { ! $self->war_busy($_) } @attacker_coalition;
  8         23  
124 7         26 my @defender_coalition = $self->empire($defender->name);
125 7         11 @defender_coalition = grep { ! $self->war_busy($_) } @defender_coalition;
  8         18  
126            
127             #Allies management
128 7         36 my @attacker_allies = $self->get_allies($attacker->name);
129 7         27 my @defender_allies = $self->get_allies($defender->name);
130 7         16 for(@attacker_allies)
131             {
132 1         5 my $ally_name = $_->destination($attacker->name);
133 1         3 my $ally = $self->get_nation( $ally_name );
134 1 50       4 if($ally->good_prey($defender, $self, ALLY_CONFLICT_LEVEL_FOR_INVOLVEMENT, 0 ))
135             {
136 1 50       2 if(! grep { $_ eq $ally_name } @attacker_coalition)
  1         4  
137             {
138 1         1 push @attacker_coalition, $ally_name;
139 1         6 $ally->register_event("JOIN WAR AS ALLY OF " . $attacker->name ." AGAINST " . $defender->name);
140             }
141             }
142             }
143 7         13 for(@defender_allies)
144             {
145 1         5 my $ally_name = $_->destination($defender->name);
146 1         4 my $ally = $self->get_nation( $ally_name );
147 1 50       4 if($ally->good_prey($attacker, $self, ALLY_CONFLICT_LEVEL_FOR_INVOLVEMENT, 0 ))
148             {
149 1 50       2 if(! grep { $_ eq $ally_name } @defender_coalition)
  1         5  
150             {
151 1         1 push @defender_coalition, $ally_name;
152 1         7 $ally->register_event("JOIN WAR AS ALLY OF " . $defender->name ." AGAINST " . $attacker->name);
153             }
154             }
155             }
156              
157 7         28 my @attacker_targets = $self->get_group_borders(\@attacker_coalition, \@defender_coalition);
158 7         21 my @defender_targets = $self->get_group_borders(\@defender_coalition, \@attacker_coalition);
159 7         11 my @war_couples;
160             my @couples_factions;
161 0         0 my %used;
162 7         14 for(@attacker_coalition, @defender_coalition)
163             {
164 18         25 $used{$_} = 0;
165             }
166             #push @war_couples, [$attacker->name, $defender->name];
167 7         22 $used{$attacker->name} = 1;
168 7         19 $used{$defender->name} = 1;
169 7         11 my $faction = 1;
170 7         6 my $done = 0;
171 7         8 my $faction0_done = 0;
172 7         9 my $faction1_done = 0;
173 7         23 while(! $done)
174             {
175 18         15 my @potential_attackers;
176 18 100       50 if($faction == 0)
    50          
177             {
178 8         17 @potential_attackers = grep { $used{$_} == 0 } @attacker_coalition;
  9         19  
179             }
180             elsif($faction == 1)
181             {
182 10         10 @potential_attackers = grep { $used{$_} == 0 } @defender_coalition;
  12         32  
183             }
184 18 100       34 if(@potential_attackers == 0)
185             {
186 15 100 66     75 if($faction0_done == 1 && $faction == 1 ||
      100        
      66        
187             $faction1_done == 1 && $faction == 0)
188             {
189 7         10 $done = 1;
190 7         10 last;
191             }
192             else
193             {
194 8 100       19 if($faction == 0)
195             {
196 2         2 $faction0_done = 1;
197 2         2 $faction = 1;
198             }
199             else
200             {
201 6         7 $faction1_done = 1;
202 6         8 $faction = 0;
203             }
204 8         16 next;
205             }
206            
207             }
208 3         48 @potential_attackers = $self->shuffle("War creation. Choosing attackers", @potential_attackers);
209 3         5 my $attack_now = $potential_attackers[0];
210 3         3 my $defend_now = undef;
211 3         4 my $free_level = 0;
212 3         5 my $searching = 1;
213 3         8 while($searching)
214             {
215 3         2 my @potential_defenders;
216 3 100       12 if($faction == 0)
    50          
217             {
218 1         2 @potential_defenders = grep { ! $self->exists_treaty_by_type($_, $attack_now, 'no aggression') } @defender_coalition;
  1         4  
219 1 50       3 if(@potential_defenders == 0)
220             {
221 1         1 @attacker_coalition = grep { ! $attack_now eq $_ } @attacker_coalition;
  2         4  
222 1         10 $self->broadcast_event({ code => 'nopartecipatewar',
223             text => "NO POSSIBILITY TO PARTECIPATE TO WAR LINKED TO WAR BETWEEN " . $attacker->name . " AND " .$defender->name . " FOR $attack_now",
224             involved => [$attack_now, $attacker->name, $defender->name] }, $attack_now);
225 1         2 last;
226             }
227             }
228             elsif($faction == 1)
229             {
230 2         3 @potential_defenders = grep { ! $self->exists_treaty_by_type($_, $attack_now, 'no aggression') } @attacker_coalition;
  3         9  
231 2 100       6 if(@potential_defenders == 0)
232             {
233 1         1 @defender_coalition = grep { ! $attack_now eq $_ } @defender_coalition;
  2         4  
234 1         12 $self->broadcast_event({ code => 'nopartecipatewar',
235             text => "NO POSSIBILITY TO PARTECIPATE TO WAR LINKED TO WAR BETWEEN " . $attacker->name . " AND " .$defender->name . " FOR $attack_now",
236             involved => [$attack_now, $attacker->name, $defender->name] }, $attack_now);
237 1         2 last;
238             }
239             }
240 1         2 @potential_defenders = grep { $used{$_} <= $free_level } @potential_defenders;
  2         5  
241 1 50       2 if(@potential_defenders > 0)
242             {
243 1         15 @potential_defenders = $self->shuffle("War creation. Choosing defenders", @potential_defenders);
244 1         2 $defend_now = $potential_defenders[0];
245 1         3 $searching = 0;
246             }
247             else
248             {
249 0         0 $free_level++;
250             }
251             }
252 3 100       9 if($defend_now)
253             {
254 1         2 push @war_couples, [$attack_now, $defend_now];
255 1         2 push @couples_factions, $faction;
256 1         2 $used{$defend_now} += 1;
257             }
258 3         5 $used{$attack_now} += 1;
259 3 100       8 if($faction == 0)
260             {
261 1         2 $faction = 1;
262             }
263             else
264             {
265 2         5 $faction = 0;
266             }
267             }
268 7         9 my %attacker_leaders;
269 7         9 my $war_id = time;
270 7         119 my $war = BalanceOfPower::Relations::War->new(node1 => $attacker->name,
271             node2 => $defender->name,
272             attack_leader => $attacker->name,
273             war_id => $war_id,
274             node1_faction => 0,
275             node2_faction => 1,
276             start_date => $self->current_year,
277             log_active => 0,
278             );
279 7         121 $war = $self->war_starting_report($war);
280 7         23 $self->add_war($war);
281 7         24 $attacker_leaders{$defender->name} = $attacker->name;
282 7         85 $self->broadcast_event({ code => 'warstart',
283             text => "WAR BETWEEN " . $attacker->name . " AND " .$defender->name . " STARTED",
284             involved => [$attacker->name, $defender->name],
285             values => [$war_id] }, $attacker->name, $defender->name);
286 7         11 my $faction_counter = 0;
287 7         32 foreach my $c (@war_couples)
288             {
289 1         1 my $leader;
290 1 50       5 if(exists $attacker_leaders{$c->[1]})
291             {
292 0         0 $leader = $attacker_leaders{$c->[1]}
293             }
294             else
295             {
296 1         2 $leader = $c->[0];
297 1         2 $attacker_leaders{$c->[1]} = $c->[0];
298             }
299 1         1 my $faction1;
300             my $faction2;
301 1 50       3 if($couples_factions[$faction_counter] == 0)
302             {
303 0         0 $faction1 = 0;
304 0         0 $faction2 = 1;
305             }
306             else
307             {
308 1         1 $faction1 = 1;
309 1         1 $faction2 = 0;
310             }
311 1         2 my $node1 = $c->[0];
312 1         2 my $node2 = $c->[1];
313 1         25 my $war = BalanceOfPower::Relations::War->new(node1 => $node1,
314             node2 => $node2,
315             attack_leader => $leader,
316             war_id => $war_id,
317             node1_faction => $faction1,
318             node2_faction => $faction2,
319             start_date => $self->current_year,
320             log_active => 0);
321 1         16 $war = $self->war_starting_report($war);
322 1         3 $self->add_war($war);
323 1         16 $self->broadcast_event( { code => 'warlinkedstart',
324             text => "WAR BETWEEN " . $node1 . " AND " . $node2 . " STARTED (LINKED TO WAR BETWEEN " . $attacker->name . " AND " .$defender->name . ")",
325             involved => [$node1, $node2],
326             values => [$war_id, $attacker->name, $defender->name, $faction1, $faction2] }, $node1, $node2 );
327             }
328             }
329             }
330             sub war_starting_report
331             {
332 8     8 0 11 my $self = shift;
333 8         11 my $war = shift;
334 8         22 my $node1 = $war->node1;
335 8         20 my $node2 = $war->node2;
336 8         33 $war->register_event("Starting army for " . $node1 . ": " . $self->get_nation($node1)->army);
337 8         29 $war->register_event("Progress of " . $node1 . ": " . $self->get_nation($node1)->progress);
338 8         27 my $sup1 = $self->supported($node1);
339 8 50       21 if($sup1)
340             {
341 0         0 $war->register_event("$node1 is supported by " . $sup1->node1 . ": " . $sup1->army);
342             }
343 8         31 $war->register_event("Starting army for " . $node2 . ": " . $self->get_nation($node2)->army);
344 8         30 $war->register_event("Progress of " . $node2 . ": " . $self->get_nation($node2)->progress);
345 8         20 my $sup2 = $self->supported($node2);
346 8 100       25 if($sup2)
347             {
348 2         15 $war->register_event("$node2 is supported by " . $sup2->node1 . ": " . $sup2->army);
349             }
350 8         16 return $war;
351             }
352              
353             sub army_for_war
354             {
355 22     22 0 29 my $self = shift;
356 22         19 my $nation = shift;
357 22         63 my $supported = $self->supported($nation->name);
358 22         44 my $army = $nation->army;
359 22 100       46 if($supported)
360             {
361 2         6 $army += $supported->army;
362             }
363 22         34 return $army;
364             }
365              
366             sub damage_from_battle
367             {
368 22     22 0 20 my $self = shift;
369 22         19 my $nation = shift;
370 22         17 my $damage = shift;
371 22         24 my $attacker = shift;
372 22         55 my $supported = $self->supported($nation->name);
373 22 100 100     67 if($supported && $self->exists_treaty_by_type($attacker->name, $supported->node1, 'no aggression'))
374             {
375 1         8 $supported = undef;
376             }
377 22         19 my $flip = 0;
378 22         22 my $army_damage = 0;
379 22         44 while($damage > 0)
380             {
381 63 100       71 if($flip == 0)
382             {
383 32 100 66     63 if($supported && $supported->army > 0)
384             {
385 2         5 $supported->casualities(1);
386 2         2 $damage--;
387             }
388 32         45 $flip = 1;
389             }
390             else
391             {
392 31         26 $army_damage++;
393 31         25 $damage--;
394 31         43 $flip = 0;
395             }
396             }
397 22         85 $nation->add_army(-1 * $army_damage);
398 22 100       38 if($supported)
399             {
400 1 50       4 if($supported->army <= 0)
401             {
402 0         0 $self->broadcast_event({ code => 'supdestroyed',
403             text => "MILITARY SUPPORT TO " . $supported->node2 . " BY " . $supported->node1 . " DESTROYED",
404             involved => [$supported->node1, $supported->node2] }, $supported->node1, $supported->node2);
405 0         0 $self->war_report("Military support to ". $supported->node2 . " by " . $supported->node1 . " destroyed", $supported->node2);
406             }
407             }
408 22         62 $self->military_support_garbage_collector();
409             }
410              
411             sub fight_wars
412             {
413 46     46 0 47 my $self = shift;
414 46         43 my %losers;
415 46         67 my %war_bonds_issued = ();
416 46         161 foreach my $w ($self->wars->all())
417             {
418 11 50       44 if(! exists $war_bonds_issued{$w->node1})
419             {
420 11         52 $self->issue_war_bonds($w->node1);
421 11         25 $war_bonds_issued{$w->node1} = 1;
422             }
423 11 50       35 if(! exists $war_bonds_issued{$w->node2})
424             {
425 11         29 $self->issue_war_bonds($w->node2);
426 11         21 $war_bonds_issued{$w->node2} = 1;
427             }
428              
429             #As Risiko
430 11         148 $self->broadcast_event({ code => 'wargoon',
431             text => "WAR BETWEEN " . $w->node1 . " AND " . $w->node2 . " GO ON",
432             involved => [$w->node1, $w->node2],
433             values => [$w->war_id] }, $w->node1, $w->node2);
434 11         51 my $attacker = $self->get_nation($w->node1);
435 11         34 my $defender = $self->get_nation($w->node2);
436 11         32 my $attacker_army = $self->army_for_war($attacker);
437 11         22 my $defender_army = $self->army_for_war($defender);
438 11 50       28 my $attack = $attacker_army >= ARMY_FOR_BATTLE ? ARMY_FOR_BATTLE : $attacker_army;
439 11 50       26 my $defence = $defender_army >= ARMY_FOR_BATTLE ? ARMY_FOR_BATTLE : $defender_army;
440 11         12 my $attacker_damage = 0;
441 11         11 my $defender_damage = 0;
442 11 50       21 my $counter = $attack < $defence ? $attack : $defence;
443 11         34 my $progress_delta = $attacker->progress - $defender->progress;
444 11         12 my $attacker_progress_bonus;
445             my $defender_progress_bonus;
446 11 50       26 if($progress_delta > 0)
447             {
448 0         0 $attacker_progress_bonus = $progress_delta * PROGRESS_BATTLE_FACTOR;
449 0         0 $defender_progress_bonus = 0;
450             }
451             else
452             {
453 11         14 $attacker_progress_bonus = 0;
454 11         33 $defender_progress_bonus = $progress_delta * PROGRESS_BATTLE_FACTOR * -1;
455             }
456 11         37 for(my $i = 0; $i < $counter; $i++)
457             {
458 33         613 my $att = int(($self->random(1, 60, "War risiko: throw for attacker " . $attacker->name) + $attacker_progress_bonus) / 10) + 1;
459 33         645 my $def = int(($self->random(1, 60, "War risiko: throw for defender " . $defender->name) + $defender_progress_bonus) / 10) + 1;
460              
461 33 100       72 if($att > $def)
462             {
463 13         32 $defender_damage++;
464             }
465             else
466             {
467 20         49 $attacker_damage++;
468             }
469             }
470 11 50       49 if(my $sup = $self->supported($attacker->name))
471             {
472 0         0 my $supporter_n = $sup->start($attacker->name);
473 0 0       0 if(! $self->exists_treaty_by_type($defender->name, $supporter_n, 'no aggression'))
474             {
475 0         0 $self->change_diplomacy($defender->name, $supporter_n, -1 * DIPLOMACY_MALUS_FOR_SUPPORT, "WAR WITH " . $attacker->name);
476             }
477             }
478 11 100       41 if(my $sup = $self->supported($defender->name))
479             {
480 2         11 my $supporter_n = $sup->start($defender->name);
481 2 100       9 if(! $self->exists_treaty_by_type($attacker->name, $supporter_n, 'no aggression'))
482             {
483 1         7 $self->change_diplomacy($attacker->name, $supporter_n, -1 * DIPLOMACY_MALUS_FOR_SUPPORT, "WAR WITH " . $defender->name);
484             }
485             }
486              
487 11         35 $self->damage_from_battle($attacker, $attacker_damage, $defender);
488 11         44 $self->damage_from_battle($defender, $defender_damage, $attacker);
489 11         79 $attacker->register_event("CASUALITIES IN WAR WITH " . $defender->name . ": $attacker_damage");
490 11         65 $defender->register_event("CASUALITIES IN WAR WITH " . $attacker->name . ": $defender_damage");
491 11 100       73 if($attacker->army == 0)
    100          
492             {
493 1         6 $losers{$attacker->name} = 1;
494             }
495             elsif($defender->army == 0)
496             {
497 1         4 $losers{$defender->name} = 1;
498             }
499             }
500 46         135 for(keys %losers)
501             {
502 2         5 $self->lose_war($_);
503             }
504             }
505              
506             sub lose_war
507             {
508 7     7 0 10 my $self = shift;
509 7         8 my $loser = shift;
510 7         8 my $internal_disorder = shift;
511 7   100     22 $internal_disorder ||= 0;
512 7         24 my @wars = $self->get_wars($loser);
513 7         12 my $retreat_penality = 0;
514 7         11 my @conquerors = ();
515 7         9 my $conquerors_leader = "";
516 7         12 my $occupied = 0;
517 7         16 foreach my $w (@wars)
518             {
519 2         1 my $other;
520             my $winner_role;
521 2 100       12 if($w->node1 eq $loser)
    50          
522             {
523             #Loser is the attacker
524 1         2 $retreat_penality = 1;
525 1         3 $other = $w->node2;
526 1         1 $winner_role = "[DEFENDER]";
527 1         5 $self->send_event("RETREAT FROM " . $other, $loser);
528             }
529             elsif($w->node2 eq $loser)
530             {
531             #Loser is the defender
532 1         7 $other = $w->node1;
533 1         3 push @conquerors, $w->node1;
534 1         5 $self->delete_crisis($loser, $other);
535 1         5 $conquerors_leader = $w->attack_leader;
536 1         2 $winner_role = "[ATTACKER]";
537 1         3 $self->get_nation($loser)->internal_disorder(AFTER_CONQUERED_INTERNAL_DISORDER);
538             }
539 2         10 my $ending_line = "WAR BETWEEN $other AND $loser WON BY $other $winner_role";
540              
541 2         14 $self->broadcast_event({ code => 'warend',
542             text => $ending_line,
543             involved => [$other, $loser],
544             values => [$w->war_id, $winner_role] }, $other, $loser);
545 2         3 my $history_line = "";
546 2         7 $self->cash_war_bonds($other);
547 2         6 $self->discard_war_bonds($loser);
548 2         4 $history_line .= "$other $winner_role won the war";
549 2         7 $self->delete_war($other, $loser, $history_line);
550             }
551 7 100       18 if(@conquerors > 0)
552             {
553 1         2 $occupied = 1;
554 1         5 $self->occupy($loser, \@conquerors, $conquerors_leader, $internal_disorder);
555             }
556 7         18 return $occupied;
557             }
558              
559             sub delete_war
560             {
561 3     3 0 13 my $self = shift;
562 3         2 my $nation1 = shift;
563 3         6 my $nation2 = shift;
564 3         4 my $ending_line = shift;
565 3         8 my $war = $self->war_exists($nation1, $nation2);
566 3         14 $war->end_date($self->current_year);
567 3         8 $war->register_event($ending_line);
568 3         4 push @{$self->memorial}, $war;
  3         10  
569 3         9 $self->delete_war_link($nation1, $nation2);
570             }
571              
572              
573              
574             sub dump_memorial
575             {
576 0     0 0   my $self = shift;
577 0           my $io = shift;
578 0           my $indent = shift;
579 0           foreach my $w (@{$self->memorial})
  0            
580             {
581 0           print {$io} $w->dump($io, $indent);
  0            
582             }
583             }
584             sub load_memorial
585             {
586 0     0 0   my $self = shift;
587 0           my $data = shift;
588            
589 0           $data .= "EOF";
590 0           my $war_data = "";
591 0           my @memorial;
592 0           my @lines = split "\n", $data;
593 0           foreach my $l (@lines)
594             {
595 0 0         if($l !~ /^\s/)
596             {
597 0 0         if($war_data)
598             {
599 0           push @memorial, BalanceOfPower::Relations::War->load($war_data);
600 0           $war_data = $l . "\n";
601             }
602             else
603             {
604 0           $war_data = $l . "\n";
605             }
606             }
607             else
608             {
609 0           $war_data .= $l . "\n";
610             }
611             }
612 0           return \@memorial;
613             }
614              
615              
616             1;