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.400115';
3 13     13   6575 use strict;
  13         21  
  13         363  
4 13     13   134 use v5.10;
  13         39  
5              
6 13     13   53 use Moo::Role;
  13         20  
  13         85  
7              
8 13     13   3220 use Term::ANSIColor;
  13         28  
  13         855  
9              
10 13     13   74 use BalanceOfPower::Constants ':all';
  13         23  
  13         7250  
11 13     13   87 use BalanceOfPower::Printer;
  13         22  
  13         297  
12 13     13   4521 use BalanceOfPower::Relations::Crisis;
  13         29  
  13         388  
13 13     13   4527 use BalanceOfPower::Relations::War;
  13         34  
  13         40183  
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 257     257 0 242 my $self = shift;
59 257         249 my $nation1 = shift;
60 257         233 my $nation2 = shift;
61 257   50     773 my $hostile = shift || 1;
62 257 100       600 if($self->border_exists($nation1, $nation2))
63             {
64 92         406 return { how => 'border', who => undef };
65             }
66 165         425 my @supported = $self->supporter($nation1);
67 165         279 for(@supported)
68             {
69 12         42 my $nation_supported = $_->destination($nation1);
70 12         54 my $treaty = $self->exists_treaty_by_type($nation_supported, $nation2, 'no aggression');
71 12 100 33     54 if(! $hostile || ! $treaty)
72             {
73 11 100       38 if(! $self->war_busy($nation_supported))
74             {
75 7 50       27 if($nation_supported eq $nation2)
76             {
77 0         0 return { how => 'supporting', who => undef};
78             }
79 7 100       23 if($self->border_exists($nation_supported, $nation2))
80             {
81 5         46 return { how => 'support', who => $nation_supported};
82             }
83             }
84             else
85             {
86             }
87             }
88             }
89 160         530 my @empire = $self->empire($nation1);
90 160         227 for(@empire)
91             {
92 164         152 my $ally = $_;
93 164 50       394 if(! $self->war_busy($ally))
94             {
95 164 100       324 return { how => 'linked', who => undef } if $ally eq $nation2;
96 163 100       370 return { how => 'control', who => $ally } if $self->border_exists($ally, $nation2);
97             }
98             }
99 157         494 return 0;
100             }
101              
102             sub war_current_year
103             {
104 46     46 0 80 my $self = shift;
105 46         316 for($self->wars->all)
106             {
107 6         51 $_->current_year($self->current_year);
108             }
109             }
110              
111             sub create_war
112             {
113 7     7 0 17 my $self = shift;
114 7   50     56 my $attacker = shift || "";
115 7   50     29 my $defender = shift || "";
116              
117 7 50       51 if(! $self->war_exists($attacker->name, $defender->name))
118             {
119 7         138 $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         51 my @attacker_coalition = $self->empire($attacker->name);
123 7         17 @attacker_coalition = grep { ! $self->war_busy($_) } @attacker_coalition;
  8         30  
124 7         42 my @defender_coalition = $self->empire($defender->name);
125 7         29 @defender_coalition = grep { ! $self->war_busy($_) } @defender_coalition;
  8         30  
126            
127             #Allies management
128 7         51 my @attacker_allies = $self->get_allies($attacker->name);
129 7         41 my @defender_allies = $self->get_allies($defender->name);
130 7         27 for(@attacker_allies)
131             {
132 1         6 my $ally_name = $_->destination($attacker->name);
133 1         5 my $ally = $self->get_nation( $ally_name );
134 1 50       7 if($ally->good_prey($defender, $self, ALLY_CONFLICT_LEVEL_FOR_INVOLVEMENT, 0 ))
135             {
136 1 50       3 if(! grep { $_ eq $ally_name } @attacker_coalition)
  1         5  
137             {
138 1         2 push @attacker_coalition, $ally_name;
139 1         8 $ally->register_event("JOIN WAR AS ALLY OF " . $attacker->name ." AGAINST " . $defender->name);
140             }
141             }
142             }
143 7         31 for(@defender_allies)
144             {
145 1         6 my $ally_name = $_->destination($defender->name);
146 1         6 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       3 if(! grep { $_ eq $ally_name } @defender_coalition)
  1         7  
150             {
151 1         3 push @defender_coalition, $ally_name;
152 1         15 $ally->register_event("JOIN WAR AS ALLY OF " . $defender->name ." AGAINST " . $attacker->name);
153             }
154             }
155             }
156              
157 7         63 my @attacker_targets = $self->get_group_borders(\@attacker_coalition, \@defender_coalition);
158 7         34 my @defender_targets = $self->get_group_borders(\@defender_coalition, \@attacker_coalition);
159 7         16 my @war_couples;
160             my @couples_factions;
161 0         0 my %used;
162 7         22 for(@attacker_coalition, @defender_coalition)
163             {
164 18         43 $used{$_} = 0;
165             }
166             #push @war_couples, [$attacker->name, $defender->name];
167 7         29 $used{$attacker->name} = 1;
168 7         23 $used{$defender->name} = 1;
169 7         11 my $faction = 1;
170 7         11 my $done = 0;
171 7         11 my $faction0_done = 0;
172 7         10 my $faction1_done = 0;
173 7         24 while(! $done)
174             {
175 18         23 my @potential_attackers;
176 18 100       72 if($faction == 0)
    50          
177             {
178 8         20 @potential_attackers = grep { $used{$_} == 0 } @attacker_coalition;
  9         27  
179             }
180             elsif($faction == 1)
181             {
182 10         19 @potential_attackers = grep { $used{$_} == 0 } @defender_coalition;
  12         39  
183             }
184 18 100       53 if(@potential_attackers == 0)
185             {
186 15 100 66     143 if($faction0_done == 1 && $faction == 1 ||
      100        
      66        
187             $faction1_done == 1 && $faction == 0)
188             {
189 7         11 $done = 1;
190 7         14 last;
191             }
192             else
193             {
194 8 100       21 if($faction == 0)
195             {
196 2         4 $faction0_done = 1;
197 2         4 $faction = 1;
198             }
199             else
200             {
201 6         16 $faction1_done = 1;
202 6         11 $faction = 0;
203             }
204 8         20 next;
205             }
206            
207             }
208 3         75 @potential_attackers = $self->shuffle("War creation. Choosing attackers", @potential_attackers);
209 3         6 my $attack_now = $potential_attackers[0];
210 3         14 my $defend_now = undef;
211 3         4 my $free_level = 0;
212 3         6 my $searching = 1;
213 3         20 while($searching)
214             {
215 3         5 my @potential_defenders;
216 3 100       18 if($faction == 0)
    50          
217             {
218 1         3 @potential_defenders = grep { ! $self->exists_treaty_by_type($_, $attack_now, 'no aggression') } @defender_coalition;
  1         6  
219 1 50       5 if(@potential_defenders == 0)
220             {
221 1         2 @attacker_coalition = grep { ! $attack_now eq $_ } @attacker_coalition;
  2         5  
222 1         15 $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         3 last;
226             }
227             }
228             elsif($faction == 1)
229             {
230 2         5 @potential_defenders = grep { ! $self->exists_treaty_by_type($_, $attack_now, 'no aggression') } @attacker_coalition;
  3         13  
231 2 100       9 if(@potential_defenders == 0)
232             {
233 1         3 @defender_coalition = grep { ! $attack_now eq $_ } @defender_coalition;
  2         4  
234 1         15 $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         3 @potential_defenders = grep { $used{$_} <= $free_level } @potential_defenders;
  2         6  
241 1 50       4 if(@potential_defenders > 0)
242             {
243 1         18 @potential_defenders = $self->shuffle("War creation. Choosing defenders", @potential_defenders);
244 1         2 $defend_now = $potential_defenders[0];
245 1         5 $searching = 0;
246             }
247             else
248             {
249 0         0 $free_level++;
250             }
251             }
252 3 100       9 if($defend_now)
253             {
254 1         3 push @war_couples, [$attack_now, $defend_now];
255 1         2 push @couples_factions, $faction;
256 1         3 $used{$defend_now} += 1;
257             }
258 3         8 $used{$attack_now} += 1;
259 3 100       10 if($faction == 0)
260             {
261 1         3 $faction = 1;
262             }
263             else
264             {
265 2         5 $faction = 0;
266             }
267             }
268 7         15 my %attacker_leaders;
269 7         16 my $war_id = time;
270 7         170 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         186 $war = $self->war_starting_report($war);
280 7         35 $self->add_war($war);
281 7         46 $attacker_leaders{$defender->name} = $attacker->name;
282 7         140 $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         19 my $faction_counter = 0;
287 7         51 foreach my $c (@war_couples)
288             {
289 1         2 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         3 $leader = $c->[0];
297 1         2 $attacker_leaders{$c->[1]} = $c->[0];
298             }
299 1         3 my $faction1;
300             my $faction2;
301 1 50       2 if($couples_factions[$faction_counter] == 0)
302             {
303 0         0 $faction1 = 0;
304 0         0 $faction2 = 1;
305             }
306             else
307             {
308 1         2 $faction1 = 1;
309 1         2 $faction2 = 0;
310             }
311 1         3 my $node1 = $c->[0];
312 1         2 my $node2 = $c->[1];
313 1         40 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         18 $war = $self->war_starting_report($war);
322 1         5 $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 18 my $self = shift;
333 8         14 my $war = shift;
334 8         31 my $node1 = $war->node1;
335 8         119 my $node2 = $war->node2;
336 8         63 $war->register_event("Starting army for " . $node1 . ": " . $self->get_nation($node1)->army);
337 8         47 $war->register_event("Progress of " . $node1 . ": " . $self->get_nation($node1)->progress);
338 8         39 my $sup1 = $self->supported($node1);
339 8 50       28 if($sup1)
340             {
341 0         0 $war->register_event("$node1 is supported by " . $sup1->node1 . ": " . $sup1->army);
342             }
343 8         44 $war->register_event("Starting army for " . $node2 . ": " . $self->get_nation($node2)->army);
344 8         55 $war->register_event("Progress of " . $node2 . ": " . $self->get_nation($node2)->progress);
345 8         40 my $sup2 = $self->supported($node2);
346 8 100       33 if($sup2)
347             {
348 2         23 $war->register_event("$node2 is supported by " . $sup2->node1 . ": " . $sup2->army);
349             }
350 8         24 return $war;
351             }
352              
353             sub army_for_war
354             {
355 22     22 0 33 my $self = shift;
356 22         27 my $nation = shift;
357 22         89 my $supported = $self->supported($nation->name);
358 22         64 my $army = $nation->army;
359 22 100       56 if($supported)
360             {
361 2         12 $army += $supported->army;
362             }
363 22         44 return $army;
364             }
365              
366             sub damage_from_battle
367             {
368 22     22 0 32 my $self = shift;
369 22         25 my $nation = shift;
370 22         23 my $damage = shift;
371 22         26 my $attacker = shift;
372 22         79 my $supported = $self->supported($nation->name);
373 22 100 100     124 if($supported && $self->exists_treaty_by_type($attacker->name, $supported->node1, 'no aggression'))
374             {
375 1         2 $supported = undef;
376             }
377 22         30 my $flip = 0;
378 22         27 my $army_damage = 0;
379 22         59 while($damage > 0)
380             {
381 63 100       100 if($flip == 0)
382             {
383 32 100 66     87 if($supported && $supported->army > 0)
384             {
385 2         17 $supported->casualities(1);
386 2         2 $damage--;
387             }
388 32         75 $flip = 1;
389             }
390             else
391             {
392 31         32 $army_damage++;
393 31         36 $damage--;
394 31         70 $flip = 0;
395             }
396             }
397 22         91 $nation->add_army(-1 * $army_damage);
398 22 100       137 if($supported)
399             {
400 1 50       5 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         87 $self->military_support_garbage_collector();
409             }
410              
411             sub fight_wars
412             {
413 46     46 0 78 my $self = shift;
414 46         60 my %losers;
415 46         100 my %war_bonds_issued = ();
416 46         219 foreach my $w ($self->wars->all())
417             {
418 11 50       66 if(! exists $war_bonds_issued{$w->node1})
419             {
420 11         86 $self->issue_war_bonds($w->node1);
421 11         82 $war_bonds_issued{$w->node1} = 1;
422             }
423 11 50       54 if(! exists $war_bonds_issued{$w->node2})
424             {
425 11         54 $self->issue_war_bonds($w->node2);
426 11         32 $war_bonds_issued{$w->node2} = 1;
427             }
428              
429             #As Risiko
430 11         214 $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         76 my $attacker = $self->get_nation($w->node1);
435 11         48 my $defender = $self->get_nation($w->node2);
436 11         42 my $attacker_army = $self->army_for_war($attacker);
437 11         45 my $defender_army = $self->army_for_war($defender);
438 11 50       43 my $attack = $attacker_army >= ARMY_FOR_BATTLE ? ARMY_FOR_BATTLE : $attacker_army;
439 11 50       43 my $defence = $defender_army >= ARMY_FOR_BATTLE ? ARMY_FOR_BATTLE : $defender_army;
440 11         17 my $attacker_damage = 0;
441 11         17 my $defender_damage = 0;
442 11 50       32 my $counter = $attack < $defence ? $attack : $defence;
443 11         59 my $progress_delta = $attacker->progress - $defender->progress;
444 11         17 my $attacker_progress_bonus;
445             my $defender_progress_bonus;
446 11 50       41 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         23 $attacker_progress_bonus = 0;
454 11         30 $defender_progress_bonus = $progress_delta * PROGRESS_BATTLE_FACTOR * -1;
455             }
456 11         45 for(my $i = 0; $i < $counter; $i++)
457             {
458 33         956 my $att = int(($self->random(1, 60, "War risiko: throw for attacker " . $attacker->name) + $attacker_progress_bonus) / 10) + 1;
459 33         913 my $def = int(($self->random(1, 60, "War risiko: throw for defender " . $defender->name) + $defender_progress_bonus) / 10) + 1;
460              
461 33 100       103 if($att > $def)
462             {
463 13         39 $defender_damage++;
464             }
465             else
466             {
467 20         73 $attacker_damage++;
468             }
469             }
470 11 50       69 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       66 if(my $sup = $self->supported($defender->name))
479             {
480 2         13 my $supporter_n = $sup->start($defender->name);
481 2 100       16 if(! $self->exists_treaty_by_type($attacker->name, $supporter_n, 'no aggression'))
482             {
483 1         9 $self->change_diplomacy($attacker->name, $supporter_n, -1 * DIPLOMACY_MALUS_FOR_SUPPORT, "WAR WITH " . $defender->name);
484             }
485             }
486              
487 11         45 $self->damage_from_battle($attacker, $attacker_damage, $defender);
488 11         73 $self->damage_from_battle($defender, $defender_damage, $attacker);
489 11         105 $attacker->register_event("CASUALITIES IN WAR WITH " . $defender->name . ": $attacker_damage");
490 11         99 $defender->register_event("CASUALITIES IN WAR WITH " . $attacker->name . ": $defender_damage");
491 11 100       108 if($attacker->army == 0)
    100          
492             {
493 1         7 $losers{$attacker->name} = 1;
494             }
495             elsif($defender->army == 0)
496             {
497 1         7 $losers{$defender->name} = 1;
498             }
499             }
500 46         198 for(keys %losers)
501             {
502 2         6 $self->lose_war($_);
503             }
504             }
505              
506             sub lose_war
507             {
508 7     7 0 16 my $self = shift;
509 7         14 my $loser = shift;
510 7         16 my $internal_disorder = shift;
511 7   100     29 $internal_disorder ||= 0;
512 7         28 my @wars = $self->get_wars($loser);
513 7         27 my $retreat_penality = 0;
514 7         16 my @conquerors = ();
515 7         15 my $conquerors_leader = "";
516 7         15 my $occupied = 0;
517 7         19 foreach my $w (@wars)
518             {
519 2         3 my $other;
520             my $winner_role;
521 2 100       12 if($w->node1 eq $loser)
    50          
522             {
523             #Loser is the attacker
524 1         1 $retreat_penality = 1;
525 1         4 $other = $w->node2;
526 1         2 $winner_role = "[DEFENDER]";
527 1         7 $self->send_event("RETREAT FROM " . $other, $loser);
528             }
529             elsif($w->node2 eq $loser)
530             {
531             #Loser is the defender
532 1         3 $other = $w->node1;
533 1         5 push @conquerors, $w->node1;
534 1         5 $self->delete_crisis($loser, $other);
535 1         4 $conquerors_leader = $w->attack_leader;
536 1         2 $winner_role = "[ATTACKER]";
537 1         6 $self->get_nation($loser)->internal_disorder(AFTER_CONQUERED_INTERNAL_DISORDER);
538             }
539 2         8 my $ending_line = "WAR BETWEEN $other AND $loser WON BY $other $winner_role";
540              
541 2         18 $self->broadcast_event({ code => 'warend',
542             text => $ending_line,
543             involved => [$other, $loser],
544             values => [$w->war_id, $winner_role] }, $other, $loser);
545 2         4 my $history_line = "";
546 2         9 $self->cash_war_bonds($other);
547 2         6 $self->discard_war_bonds($loser);
548 2         6 $history_line .= "$other $winner_role won the war";
549 2         8 $self->delete_war($other, $loser, $history_line);
550             }
551 7 100       30 if(@conquerors > 0)
552             {
553 1         2 $occupied = 1;
554 1         5 $self->occupy($loser, \@conquerors, $conquerors_leader, $internal_disorder);
555             }
556 7         23 return $occupied;
557             }
558              
559             sub delete_war
560             {
561 3     3 0 13 my $self = shift;
562 3         5 my $nation1 = shift;
563 3         4 my $nation2 = shift;
564 3         5 my $ending_line = shift;
565 3         11 my $war = $self->war_exists($nation1, $nation2);
566 3         29 $war->end_date($self->current_year);
567 3         11 $war->register_event($ending_line);
568 3         3 push @{$self->memorial}, $war;
  3         13  
569 3         11 $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;