Completed
Push — master ( 3d8890...b672a0 )
by Jakub
02:05
created

CombatBase::chooseAction()   B

Complexity

Conditions 8
Paths 7

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 8.1867

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 12
cts 14
cp 0.8571
rs 7.7777
c 0
b 0
f 0
cc 8
eloc 14
nc 7
nop 2
crap 8.1867
1
<?php
2
declare(strict_types=1);
3
4
namespace HeroesofAbenez\Combat;
5
6
use Nexendrie\Utils\Numbers,
7
    Nexendrie\Utils\Constants;
8
9
/**
10
 * Handles combat
11
 * 
12
 * @author Jakub Konečný
13
 * @property-read CombatLogger $log Log from the combat
14
 * @property-read int $winner Team which won the combat/0 if there is no winner yet
15
 * @property-read int $round Number of current round
16
 * @property-read int $roundLimit
17
 * @property-read Team $team1
18
 * @property-read Team $team2
19
 * @property-read int $team1Damage
20
 * @property-read int $team2Damage
21
 * @property ISuccessCalculator $successCalculator
22
 * @property callable $victoryCondition To evaluate the winner of combat. Gets combat as parameter, should return winning team (1/2) or 0 if there is not winner (yet)
0 ignored issues
show
introduced by
Line exceeds 120 characters; contains 166 characters
Loading history...
23
 * @property callable $healers To determine characters that are supposed to heal their team. Gets team1 and team2 as parameters, should return Team
0 ignored issues
show
introduced by
Line exceeds 120 characters; contains 147 characters
Loading history...
24
 * @method void onCombatStart(CombatBase $combat)
25
 * @method void onCombatEnd(CombatBase $combat)
26
 * @method void onRoundStart(CombatBase $combat)
27
 * @method void onRound(CombatBase $combat)
28
 * @method void onRoundEnd(CombatBase $combat)
29
 * @method void onAttack(Character $attacker, Character $defender)
30
 * @method void onSkillAttack(Character $attacker, Character $defender, CharacterAttackSkill $skill)
31
 * @method void onSkillSpecial(Character $character1, Character $target, CharacterSpecialSkill $skill)
32
 * @method void onHeal(Character $healer, Character $patient)
33
 */
34 1
class CombatBase {
35 1
  use \Nette\SmartObject;
36
  
37
  protected const LOWEST_HP_THRESHOLD = 0.5;
38
  
39
  /** @var Team First team */
40
  protected $team1;
41
  /** @var Team Second team */
42
  protected $team2;
43
  /** @var CombatLogger */
44
  protected $log;
45
  /** @var int Number of current round */
46
  protected $round = 0;
47
  /** @var int Round limit */
48
  protected $roundLimit = 30;
49
  /** @var array Dealt damage by team */
50
  protected $damage = [1 => 0, 2 => 0];
51
  /** @var callable[] */
52
  public $onCombatStart = [];
53
  /** @var callable[] */
54
  public $onCombatEnd = [];
55
  /** @var callable[] */
56
  public $onRoundStart = [];
57
  /** @var callable[] */
58
  public $onRound = [];
59
  /** @var callable[] */
60
  public $onRoundEnd = [];
61
  /** @var callable[] */
62
  public $onAttack = [];
63
  /** @var callable[] */
64
  public $onSkillAttack = [];
65
  /** @var callable[] */
66
  public $onSkillSpecial = [];
67
  /** @var callable[] */
68
  public $onHeal = [];
69
  /** @var array|NULL Temporary variable for results of an action */
70
  protected $results;
71
  /** @var callable */
72
  protected $victoryCondition;
73
  /** @var callable */
74
  protected $healers;
75
  /** @var ISuccessCalculator */
76
  protected $successCalculator;
77
  
78
  public function __construct(CombatLogger $logger, ?ISuccessCalculator $successCalculator = NULL) {
79 1
    $this->log = $logger;
80 1
    $this->onCombatStart[] = [$this, "applyEffectProviders"];
81 1
    $this->onCombatStart[] = [$this, "setSkillsCooldowns"];
82 1
    $this->onCombatEnd[] = [$this, "removeCombatEffects"];
83 1
    $this->onCombatEnd[] = [$this, "logCombatResult"];
84 1
    $this->onCombatEnd[] = [$this, "resetInitiative"];
85 1
    $this->onRoundStart[] = [$this ,"recalculateStats"];
86 1
    $this->onRoundStart[] = [$this, "logRoundNumber"];
87 1
    $this->onRoundStart[] = [$this, "applyPoison"];
88 1
    $this->onRound[] = [$this, "mainStage"];
89 1
    $this->onRoundEnd[] = [$this, "decreaseSkillsCooldowns"];
90 1
    $this->onRoundEnd[] = [$this, "resetInitiative"];
91 1
    $this->onAttack[] = [$this, "attackHarm"];
92 1
    $this->onAttack[] = [$this, "logDamage"];
93 1
    $this->onAttack[] = [$this, "logResults"];
94 1
    $this->onSkillAttack[] = [$this, "useAttackSkill"];
95 1
    $this->onSkillAttack[] = [$this, "logDamage"];
96 1
    $this->onSkillAttack[] = [$this, "logResults"];
97 1
    $this->onSkillSpecial[] = [$this, "useSpecialSkill"];
98 1
    $this->onSkillSpecial[] = [$this, "logResults"];
99 1
    $this->onHeal[] = [$this, "heal"];
100 1
    $this->onHeal[] = [$this, "logResults"];
101 1
    $this->victoryCondition = [VictoryConditions::class, "moreDamage"];
102 1
    $this->successCalculator = $successCalculator ?? new RandomSuccessCalculator();
103 1
    $this->healers = function(): Team {
104
      return new Team("healers");
105
    };
106 1
  }
107
  
108
  public function getRound(): int {
109 1
    return $this->round;
110
  }
111
  
112
  public function getRoundLimit(): int {
113 1
    return $this->roundLimit;
114
  }
115
  
116
  /**
117
   * Set teams
118
   */
119
  public function setTeams(Team $team1, Team $team2): void {
120 1
    if(isset($this->team1)) {
121 1
      throw new ImmutableException("Teams has already been set.");
122
    }
123 1
    $this->team1 = & $team1;
124 1
    $this->team2 = & $team2;
125 1
    $this->log->setTeams($team1, $team2);
126 1
  }
127
  
128
  /**
129
   * Set participants for duel
130
   * Creates teams named after the member
131
   */
132
  public function setDuelParticipants(Character $player, Character $opponent): void {
133 1
    $team1 = new Team($player->name);
134 1
    $team1[] = $player;
135 1
    $team2 = new Team($opponent->name);
136 1
    $team2[] = $opponent;
137 1
    $this->setTeams($team1, $team2);
138 1
  }
139
  
140
  public function getTeam1(): Team {
141 1
    return $this->team1;
142
  }
143
  
144
  public function getTeam2(): Team {
145 1
    return $this->team2;
146
  }
147
  
148
  public function getVictoryCondition(): callable {
149 1
    return $this->victoryCondition;
150
  }
151
  
152
  public function setVictoryCondition(callable $victoryCondition) {
153 1
    $this->victoryCondition = $victoryCondition;
154 1
  }
155
  
156
  public function getHealers(): callable {
157
    return $this->healers;
158
  }
159
  
160
  public function setHealers(callable $healers) {
161 1
    $this->healers = $healers;
162 1
  }
163
  
164
  public function getTeam1Damage(): int {
165 1
    return $this->damage[1];
166
  }
167
  
168
  public function getTeam2Damage(): int {
169 1
    return $this->damage[2];
170
  }
171
  
172
  public function getSuccessCalculator(): ISuccessCalculator {
173 1
    return $this->successCalculator;
174
  }
175
  
176
  public function setSuccessCalculator(ISuccessCalculator $successCalculator): void {
177 1
    $this->successCalculator = $successCalculator;
178 1
  }
179
  
180
  /**
181
   * Get winner of combat
182
   * 
183
   * @staticvar int $result
184
   * @return int Winning team/0
185
   */
186
  public function getWinner(): int {
187 1
    static $result = 0;
188 1
    if($result === 0) {
189 1
      $result = call_user_func($this->victoryCondition, $this);
190 1
      $result = Numbers::range($result, 0, 2);
191
    }
192 1
    return $result;
193
  }
194
  
195
  protected function getTeam(Character $character): Team {
196 1
    return $this->team1->hasMember($character->id) ? $this->team1 : $this->team2;
197
  }
198
  
199
  protected function getEnemyTeam(Character $character): Team {
200 1
    return $this->team1->hasMember($character->id) ? $this->team2 : $this->team1;
201
  }
202
  
203
  public function applyEffectProviders(CombatBase $combat): void {
204
    /** @var Character[] $characters */
205 1
    $characters = array_merge($combat->team1->toArray(), $combat->team2->toArray());
206 1
    foreach($characters as $character) {
207 1
      foreach($character->effectProviders as $item) {
208 1
        $effect = $item->toCombatEffect();
209 1
        if(is_null($effect)) {
210
          continue;
211
        }
212 1
        $character->addEffect($effect);
213
      }
214
    }
215 1
  }
216
  
217
  /**
218
   * Set skills' cooldowns
219
   */
220
  public function setSkillsCooldowns(CombatBase $combat): void {
221
    /** @var Character[] $characters */
222 1
    $characters = array_merge($combat->team1->toArray(), $combat->team2->toArray());
223 1
    foreach($characters as $character) {
224 1
      foreach($character->skills as $skill) {
225 1
        $skill->resetCooldown();
226
      }
227
    }
228 1
  }
229
  
230
  /**
231
   * Decrease skills' cooldowns
232
   */
233
  public function decreaseSkillsCooldowns(CombatBase $combat): void {
234
    /** @var Character[] $characters */
235 1
    $characters = array_merge($combat->team1->toArray(), $combat->team2->toArray());
236 1
    foreach($characters as $character) {
237 1
      foreach($character->skills as $skill) {
238 1
        $skill->decreaseCooldown();
239
      }
240
    }
241 1
  }
242
  
243
  /**
244
   * Remove combat effects from character at the end of the combat
245
   */
246
  public function removeCombatEffects(CombatBase $combat): void {
247
    /** @var Character[] $characters */
248 1
    $characters = array_merge($combat->team1->toArray(), $combat->team2->toArray());
249 1
    foreach($characters as $character) {
250 1
      foreach($character->effects as $effect) {
251 1
        if($effect->duration === CharacterEffect::DURATION_COMBAT OR is_int($effect->duration)) {
252 1
          $character->removeEffect($effect->id);
253
        }
254
      }
255
    }
256 1
  }
257
  
258
  /**
259
   * Add winner to the log
260
   */
261
  public function logCombatResult(CombatBase $combat): void {
262 1
    $combat->log->round = 5000;
263
    $params = [
264 1
      "team1name" => $combat->team1->name, "team1damage" => $combat->damage[1],
265 1
      "team2name" => $combat->team2->name, "team2damage" => $combat->damage[2],
266
    ];
267 1
    if($combat->winner === 1) {
268 1
      $params["winner"] = $combat->team1->name;
269
    } else {
270 1
      $params["winner"] = $combat->team2->name;
271
    }
272 1
    $combat->log->logText("combat.log.combatEnd", $params);
273 1
  }
274
  
275
  /**
276
   * Log start of a round
277
   */
278
  public function logRoundNumber(CombatBase $combat): void {
279 1
    $combat->log->round = ++$this->round;
280 1
  }
281
  
282
  /**
283
   * Decrease duration of effects and recalculate stats
284
   */
285
  public function recalculateStats(CombatBase $combat): void {
286
    /** @var Character[] $characters */
287 1
    $characters = array_merge($combat->team1->toArray(), $combat->team2->toArray());
288 1
    foreach($characters as $character) {
289 1
      $character->recalculateStats();
290
    }
291 1
  }
292
  
293
  /**
294
   * Reset characters' initiative
295
   */
296
  public function resetInitiative(CombatBase $combat): void {
297
    /** @var Character[] $characters */
298 1
    $characters = array_merge($combat->team1->toArray(), $combat->team2->toArray());
299 1
    foreach($characters as $character) {
300 1
      $character->resetInitiative();
301
    }
302 1
  }
303
  
304
  /**
305
   * Select random character from the team
306
   */
307
  protected function selectRandomCharacter(Team $team): ?Character {
308 1
    if(count($team->aliveMembers) === 0) {
309
      return NULL;
310 1
    } elseif(count($team) === 1) {
311 1
      return $team[0];
312
    }
313
    $roll = rand(0, count($team->aliveMembers) - 1);
314
    return $team->aliveMembers[$roll];
315
  }
316
  
317
  /**
318
   * Select target for attack
319
   */
320
  protected function selectAttackTarget(Character $attacker): ?Character {
321 1
    $enemyTeam = $this->getEnemyTeam($attacker);
322 1
    $target = $this->findLowestHpCharacter($enemyTeam);
323 1
    if(!is_null($target)) {
324 1
      return $target;
325
    }
326 1
    return $this->selectRandomCharacter($enemyTeam);
327
  }
328
  
329
  /**
330
   * Find character with lowest hp in the team
331
   */
332
  protected function findLowestHpCharacter(Team $team, int $threshold = NULL): ?Character {
333 1
    $lowestHp = PHP_INT_MAX;
334 1
    $lowestIndex = PHP_INT_MIN;
335 1
    if(is_null($threshold)) {
336 1
      $threshold = static::LOWEST_HP_THRESHOLD;
337
    }
338 1
    foreach($team->aliveMembers as $index => $member) {
339 1
      if($member->hitpoints <= $member->maxHitpoints * $threshold AND $member->hitpoints < $lowestHp) {
340 1
        $lowestHp = $member->hitpoints;
341 1
        $lowestIndex = $index;
342
      }
343
    }
344 1
    if($lowestIndex === PHP_INT_MIN) {
345 1
      return NULL;
346
    }
347 1
    return $team->aliveMembers[$lowestIndex];
348
  }
349
  
350
  /**
351
   * Select target for healing
352
   */
353
  protected function selectHealingTarget(Character $healer): ?Character {
354 1
    return $this->findLowestHpCharacter($this->getTeam($healer));
355
  }
356
  
357
  protected function findHealers(): Team {
358 1
    $healers = call_user_func($this->healers, $this->team1, $this->team2);
359 1
    if($healers instanceof Team) {
360 1
      return $healers;
361
    }
362
    return new Team("healers");
363
  }
364
  
365
  protected function doAttackSkill(Character $character, CharacterAttackSkill $skill): void {
366 1
    for($i = 1; $i <= $skill->skill->strikes; $i++) {
367 1
      $this->onSkillAttack($character, $this->selectAttackTarget($character), $skill);
368
    }
369 1
  }
370
  
371
  protected function doSpecialSkill(Character $character, CharacterSpecialSkill $skill): void {
372 1
    switch($skill->skill->target) {
373 1
      case SkillSpecial::TARGET_ENEMY:
374
        $this->onSkillSpecial($character, $this->selectAttackTarget($character), $skill);
375
        break;
376 1
      case SkillSpecial::TARGET_SELF:
377 1
        $this->onSkillSpecial($character, $character, $skill);
378 1
        break;
379
      case SkillSpecial::TARGET_PARTY:
380
        $team = $this->getTeam($character);
381
        foreach($team as $target) {
382
          $this->onSkillSpecial($character, $target, $skill);
383
        }
384
        break;
385
      case SkillSpecial::TARGET_ENEMY_PARTY:
386
        $team = $this->getEnemyTeam($character);
387
        foreach($team as $target) {
388
          $this->onSkillSpecial($character, $target, $skill);
389
        }
390
        break;
391
    }
392 1
  }
393
  
394
  protected function chooseAction(CombatBase $combat, Character $character): ?string {
395 1
    if($character->hitpoints < 1) {
396
      return NULL;
397 1
    } elseif(in_array($character, $combat->findHealers()->toArray(), true) AND !is_null($combat->selectHealingTarget($character))) {
0 ignored issues
show
introduced by
Line exceeds 120 characters; contains 132 characters
Loading history...
398 1
      return CombatAction::ACTION_HEALING;
399
    }
400 1
    $attackTarget = $combat->selectAttackTarget($character);
401 1
    if(is_null($attackTarget)) {
402
      return NULL;
403
    }
404 1
    if(count($character->usableSkills) > 0) {
405 1
      $skill = $character->usableSkills[0];
406 1
      if($skill instanceof CharacterAttackSkill) {
407 1
        return CombatAction::ACTION_SKILL_ATTACK;
408 1
      } elseif($skill instanceof  CharacterSpecialSkill) {
409 1
        return CombatAction::ACTION_SKILL_SPECIAL;
410
      }
411
    }
412 1
    return CombatAction::ACTION_ATTACK;
413
  }
414
  
415
  protected function getAllowedActions(): array {
416 1
    $allowedActions = Constants::getConstantsValues(CombatAction::class, "ACTION_");
417 1
    return array_values(array_filter($allowedActions, function(string $value) {
418 1
      return ($value !== CombatAction::ACTION_POISON);
419 1
    }));
420
  }
421
  
422
  /**
423
   * Main stage of a round
424
   */
425
  public function mainStage(CombatBase $combat): void {
426
    /** @var Character[] $characters */
427 1
    $characters = array_merge($combat->team1->usableMembers, $combat->team2->usableMembers);
428 1
    usort($characters, function(Character $a, Character $b) {
429 1
      return -1 * strcmp((string) $a->initiative, (string) $b->initiative);
430 1
    });
431 1
    foreach($characters as $character) {
432 1
      $action = $combat->chooseAction($combat, $character);
433 1
      if(!in_array($action, $this->getAllowedActions(), true)) {
434
        continue;
435
      }
436
      switch($action) {
437 1
        case CombatAction::ACTION_ATTACK:
438 1
          $combat->onAttack($character, $combat->selectAttackTarget($character));
439 1
          break;
440 1
        case CombatAction::ACTION_SKILL_ATTACK:
441
          /** @var CharacterAttackSkill $skill */
442 1
          $skill = $character->usableSkills[0];
443 1
          $combat->doAttackSkill($character, $skill);
444 1
          break;
445 1
        case CombatAction::ACTION_SKILL_SPECIAL:
446
          /** @var CharacterSpecialSkill $skill */
447 1
          $skill = $character->usableSkills[0];
448 1
          $combat->doSpecialSkill($character, $skill);
449 1
          break;
450 1
        case CombatAction::ACTION_HEALING:
451 1
          $combat->onHeal($character, $combat->selectHealingTarget($character));
452 1
          break;
453
      }
454
    }
455 1
  }
456
  
457
  /**
458
   * Start next round
459
   * 
460
   * @return int Winning team/0
461
   */
462
  protected function startRound(): int {
463 1
    $this->onRoundStart($this);
464 1
    return $this->getWinner();
465
  }
466
  
467
  /**
468
   * Do a round
469
   */
470
  protected function doRound(): void {
471 1
    $this->onRound($this);
472 1
  }
473
  
474
  /**
475
   * End round
476
   * 
477
   * @return int Winning team/0
478
   */
479
  protected function endRound(): int {
480 1
    $this->onRoundEnd($this);
481 1
    return $this->getWinner();
482
  }
483
  
484
  /**
485
   * Executes the combat
486
   * 
487
   * @return int Winning team
488
   */
489
  public function execute(): int {
490 1
    if(!isset($this->team1)) {
491 1
      throw new InvalidStateException("Teams are not set.");
492
    }
493 1
    $this->onCombatStart($this);
494 1
    while($this->round <= $this->roundLimit) {
495 1
      if($this->startRound() > 0) {
496 1
        break;
497
      }
498 1
      $this->doRound();
499 1
      if($this->endRound() > 0) {
500
        break;
501
      }
502
    }
503 1
    $this->onCombatEnd($this);
504 1
    return $this->getWinner();
505
  }
506
  
507
  /**
508
   * Calculate hit chance for attack/skill attack
509
   */
510
  protected function calculateHitChance(Character $character1, Character $character2, CharacterAttackSkill $skill = NULL): int {
0 ignored issues
show
introduced by
Line exceeds 120 characters; contains 128 characters
Loading history...
511 1
    $hitChance = $this->successCalculator->calculateHitChance($character1, $character2, $skill);
512 1
    return Numbers::range($hitChance, ISuccessCalculator::MIN_HIT_CHANCE, ISuccessCalculator::MAX_HIT_CHANCE);
513
  }
514
  
515
  /**
516
   * Check whether action succeeded
517
   */
518
  protected function hasHit(int $hitChance): bool {
519 1
    return $this->successCalculator->hasHit($hitChance);
520
  }
521
  
522
  /**
523
   * Do an attack
524
   * Hit chance = Attacker's hit - Defender's dodge, but at least 15%
525
   * Damage = Attacker's damage - defender's defense
526
   */
527
  public function attackHarm(Character $attacker, Character $defender): void {
528 1
    $result = [];
529 1
    $hitChance = $this->calculateHitChance($attacker, $defender);
530 1
    $result["result"] = $this->hasHit($hitChance);
531 1
    $result["amount"] = 0;
532 1
    if($result["result"]) {
533 1
      $amount = $attacker->damage - $defender->defense;
534 1
      $result["amount"] = Numbers::range($amount, 0, $defender->hitpoints);
535
    }
536 1
    if($result["amount"] > 0) {
537 1
      $defender->harm($result["amount"]);
538
    }
539 1
    $result["action"] = CombatAction::ACTION_ATTACK;
540 1
    $result["name"] = "";
541 1
    $result["character1"] = $attacker;
542 1
    $result["character2"] = $defender;
543 1
    $this->results = $result;
544 1
  }
545
  
546
  /**
547
   * Use an attack skill
548
   */
549
  public function useAttackSkill(Character $attacker, Character $defender, CharacterAttackSkill $skill): void {
550 1
    $result = [];
551 1
    $hitChance = $this->calculateHitChance($attacker, $defender, $skill);
552 1
    $result["result"] = $this->hasHit($hitChance);
553 1
    $result["amount"] = 0;
554 1
    if($result["result"]) {
555 1
      $amount = (int) ($attacker->damage - $defender->defense / 100 * $skill->damage);
556 1
      $result["amount"] = Numbers::range($amount, 0, $defender->hitpoints);
557
    }
558 1
    if($result["amount"]) {
559 1
      $defender->harm($result["amount"]);
560
    }
561 1
    $result["action"] = CombatAction::ACTION_SKILL_ATTACK;
562 1
    $result["name"] = $skill->skill->name;
563 1
    $result["character1"] = $attacker;
564 1
    $result["character2"] = $defender;
565 1
    $this->results = $result;
566 1
    $skill->resetCooldown();
567 1
  }
568
  
569
  /**
570
   * Use a special skill
571
   */
572
  public function useSpecialSkill(Character $character1, Character $target, CharacterSpecialSkill $skill): void {
573
    $result = [
574 1
      "result" => true, "amount" => 0, "action" => CombatAction::ACTION_SKILL_SPECIAL, "name" => $skill->skill->name,
575 1
      "character1" => $character1, "character2" => $target,
576
    ];
577 1
    $this->results = $result;
578 1
    $effect = new CharacterEffect([
579 1
      "id" => "skill{$skill->skill->id}Effect",
580 1
      "type" => $skill->skill->type,
581 1
      "stat" => ((in_array($skill->skill->type, SkillSpecial::NO_STAT_TYPES, true)) ? NULL : $skill->skill->stat),
582 1
      "value" => $skill->value,
583 1
      "source" => CharacterEffect::SOURCE_SKILL,
584 1
      "duration" => $skill->skill->duration
585
    ]);
586 1
    $target->addEffect($effect);
587 1
    $skill->resetCooldown();
588 1
  }
589
  
590
  /**
591
   * Heal a character
592
   */
593
  public function heal(Character $healer, Character $patient): void {
594 1
    $result = [];
595 1
    $hitChance = $this->successCalculator->calculateHealingSuccessChance($healer);
596 1
    $hitChance = Numbers::range($hitChance, 0, 100);
597 1
    $result["result"] = $this->successCalculator->hasHit($hitChance);
598 1
    $amount = ($result["result"]) ? (int) ($healer->intelligence / 2) : 0;
599 1
    $result["amount"] = Numbers::range($amount, 0, $patient->maxHitpoints - $patient->hitpoints);
600 1
    if($result["amount"]) {
601 1
      $patient->heal($result["amount"]);
602
    }
603 1
    $result["action"] = CombatAction::ACTION_HEALING;
604 1
    $result["name"] = "";
605 1
    $result["character1"] = $healer;
606 1
    $result["character2"] = $patient;
607 1
    $this->results = $result;
608 1
  }
609
  
610
  /**
611
   * Harm poisoned characters at start of round
612
   */
613
  public function applyPoison(CombatBase $combat): void {
614
    /** @var Character[] $characters */
615 1
    $characters = array_merge($combat->team1->aliveMembers, $combat->team2->aliveMembers);
616 1
    foreach($characters as $character) {
617 1
      foreach($character->effects as $effect) {
618 1
        if($effect->type === SkillSpecial::TYPE_POISON) {
619
          $character->harm($effect->value);
620
          $action = [
621
            "action" => CombatAction::ACTION_POISON, "result" => true, "amount" => $effect->value,
622
            "character1" => $character, "character2" => $character,
623
          ];
624 1
          $combat->log->log($action);
625
        }
626
      }
627
    }
628 1
  }
629
  
630
  /**
631
   * Log results of an action
632
   */
633
  public function logResults(): void {
634 1
    $this->log->log($this->results);
0 ignored issues
show
Bug introduced by
It seems like $this->results can also be of type null; however, parameter $action of HeroesofAbenez\Combat\CombatLogger::log() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

634
    $this->log->log(/** @scrutinizer ignore-type */ $this->results);
Loading history...
635 1
    $this->results = NULL;
636 1
  }
637
  
638
  /**
639
   * Log dealt damage
640
   */
641
  public function logDamage(Character $attacker): void {
642 1
    $team = $this->team1->hasMember($attacker->id) ? 1 : 2;
643 1
    $this->damage[$team] += $this->results["amount"];
644 1
  }
645
  
646
  public function getLog(): CombatLogger {
647 1
    return $this->log;
648
  }
649
}
650
?>