Completed
Push — master ( ce79d3...3d8890 )
by Jakub
02:31
created

CombatBase::setTeams()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 5
cts 5
cp 1
c 0
b 0
f 0
rs 9.4285
cc 2
eloc 5
nc 2
nop 2
crap 2
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->items, $combat->team2->items);
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 1
        $effect->onApply($character, $effect);
214
      }
215
    }
216 1
  }
217
  
218
  /**
219
   * Set skills' cooldowns
220
   */
221
  public function setSkillsCooldowns(CombatBase $combat): void {
222
    /** @var Character[] $characters */
223 1
    $characters = array_merge($combat->team1->items, $combat->team2->items);
224 1
    foreach($characters as $character) {
225 1
      foreach($character->skills as $skill) {
226 1
        $skill->resetCooldown();
227
      }
228
    }
229 1
  }
230
  
231
  /**
232
   * Decrease skills' cooldowns
233
   */
234
  public function decreaseSkillsCooldowns(CombatBase $combat): void {
235
    /** @var Character[] $characters */
236 1
    $characters = array_merge($combat->team1->items, $combat->team2->items);
237 1
    foreach($characters as $character) {
238 1
      foreach($character->skills as $skill) {
239 1
        $skill->decreaseCooldown();
240
      }
241
    }
242 1
  }
243
  
244
  /**
245
   * Remove combat effects from character at the end of the combat
246
   */
247
  public function removeCombatEffects(CombatBase $combat): void {
248
    /** @var Character[] $characters */
249 1
    $characters = array_merge($combat->team1->items, $combat->team2->items);
250 1
    foreach($characters as $character) {
251 1
      foreach($character->effects as $effect) {
252 1
        if($effect->duration === CharacterEffect::DURATION_COMBAT OR is_int($effect->duration)) {
253 1
          $character->removeEffect($effect->id);
254
        }
255
      }
256
    }
257 1
  }
258
  
259
  /**
260
   * Add winner to the log
261
   */
262
  public function logCombatResult(CombatBase $combat): void {
263 1
    $combat->log->round = 5000;
264
    $params = [
265 1
      "team1name" => $combat->team1->name, "team1damage" => $combat->damage[1],
266 1
      "team2name" => $combat->team2->name, "team2damage" => $combat->damage[2],
267
    ];
268 1
    if($combat->winner === 1) {
269
      $params["winner"] = $combat->team1->name;
270
    } else {
271 1
      $params["winner"] = $combat->team2->name;
272
    }
273 1
    $combat->log->logText("combat.log.combatEnd", $params);
274 1
  }
275
  
276
  /**
277
   * Log start of a round
278
   */
279
  public function logRoundNumber(CombatBase $combat): void {
280 1
    $combat->log->round = ++$this->round;
281 1
  }
282
  
283
  /**
284
   * Decrease duration of effects and recalculate stats
285
   */
286
  public function recalculateStats(CombatBase $combat): void {
287
    /** @var Character[] $characters */
288 1
    $characters = array_merge($combat->team1->items, $combat->team2->items);
289 1
    foreach($characters as $character) {
290 1
      $character->recalculateStats();
291 1
      if($character->hitpoints > 0) {
292 1
        $character->calculateInitiative();
293
      }
294
    }
295 1
  }
296
  
297
  /**
298
   * Reset characters' initiative
299
   */
300
  public function resetInitiative(CombatBase $combat): void {
301
    /** @var Character[] $characters */
302 1
    $characters = array_merge($combat->team1->items, $combat->team2->items);
303 1
    foreach($characters as $character) {
304 1
      $character->resetInitiative();
305
    }
306 1
  }
307
  
308
  /**
309
   * Select random character from the team
310
   */
311
  protected function selectRandomCharacter(Team $team): ?Character {
312 1
    if(count($team->aliveMembers) === 0) {
313
      return NULL;
314 1
    } elseif(count($team) === 1) {
315 1
      return $team[0];
316
    }
317
    $roll = rand(0, count($team->aliveMembers) - 1);
318
    return $team->aliveMembers[$roll];
319
  }
320
  
321
  /**
322
   * Select target for attack
323
   */
324
  protected function selectAttackTarget(Character $attacker): ?Character {
325 1
    $enemyTeam = $this->getEnemyTeam($attacker);
326 1
    $target = $this->findLowestHpCharacter($enemyTeam);
327 1
    if(!is_null($target)) {
328 1
      return $target;
329
    }
330 1
    return $this->selectRandomCharacter($enemyTeam);
331
  }
332
  
333
  /**
334
   * Find character with lowest hp in the team
335
   */
336
  protected function findLowestHpCharacter(Team $team, int $threshold = NULL): ?Character {
337 1
    $lowestHp = PHP_INT_MAX;
338 1
    $lowestIndex = PHP_INT_MIN;
339 1
    if(is_null($threshold)) {
340 1
      $threshold = static::LOWEST_HP_THRESHOLD;
341
    }
342 1
    foreach($team->aliveMembers as $index => $member) {
343 1
      if($member->hitpoints <= $member->maxHitpoints * $threshold AND $member->hitpoints < $lowestHp) {
344 1
        $lowestHp = $member->hitpoints;
345 1
        $lowestIndex = $index;
346
      }
347
    }
348 1
    if($lowestIndex === PHP_INT_MIN) {
349 1
      return NULL;
350
    }
351 1
    return $team->aliveMembers[$lowestIndex];
352
  }
353
  
354
  /**
355
   * Select target for healing
356
   */
357
  protected function selectHealingTarget(Character $healer): ?Character {
358 1
    return $this->findLowestHpCharacter($this->getTeam($healer));
359
  }
360
  
361
  protected function findHealers(): Team {
362 1
    $healers = call_user_func($this->healers, $this->team1, $this->team2);
363 1
    if($healers instanceof Team) {
364 1
      return $healers;
365
    }
366
    return new Team("healers");
367
  }
368
  
369
  protected function doAttackSkill(Character $character, CharacterAttackSkill $skill): void {
370 1
    for($i = 1; $i <= $skill->skill->strikes; $i++) {
371 1
      $this->onSkillAttack($character, $this->selectAttackTarget($character), $skill);
372
    }
373 1
  }
374
  
375
  protected function doSpecialSkill(Character $character, CharacterSpecialSkill $skill): void {
376 1
    switch($skill->skill->target) {
377 1
      case SkillSpecial::TARGET_ENEMY:
378
        $this->onSkillSpecial($character, $this->selectAttackTarget($character), $skill);
379
        break;
380 1
      case SkillSpecial::TARGET_SELF:
381 1
        $this->onSkillSpecial($character, $character, $skill);
382 1
        break;
383
      case SkillSpecial::TARGET_PARTY:
384
        $team = $this->getTeam($character);
385
        foreach($team as $target) {
386
          $this->onSkillSpecial($character, $target, $skill);
387
        }
388
        break;
389
      case SkillSpecial::TARGET_ENEMY_PARTY:
390
        $team = $this->getEnemyTeam($character);
391
        foreach($team as $target) {
392
          $this->onSkillSpecial($character, $target, $skill);
393
        }
394
        break;
395
    }
396 1
  }
397
  
398
  protected function chooseAction(CombatBase $combat, Character $character): ?string {
399 1
    if($character->hitpoints < 1) {
400
      return NULL;
401 1
    } elseif(in_array($character, $combat->findHealers()->items, true) AND !is_null($combat->selectHealingTarget($character))) {
0 ignored issues
show
introduced by
Line exceeds 120 characters; contains 128 characters
Loading history...
402 1
      return CombatAction::ACTION_HEALING;
403
    }
404 1
    $attackTarget = $combat->selectAttackTarget($character);
405 1
    if(is_null($attackTarget)) {
406
      return NULL;
407
    }
408 1
    if(count($character->usableSkills) > 0) {
409 1
      $skill = $character->usableSkills[0];
410 1
      if($skill instanceof CharacterAttackSkill) {
411 1
        return CombatAction::ACTION_SKILL_ATTACK;
412 1
      } elseif($skill instanceof  CharacterSpecialSkill) {
413 1
        return CombatAction::ACTION_SKILL_SPECIAL;
414
      }
415
    }
416 1
    return CombatAction::ACTION_ATTACK;
417
  }
418
  
419
  protected function getAllowedActions(): array {
420 1
    $allowedActions = Constants::getConstantsValues(CombatAction::class, "ACTION_");
421 1
    return array_values(array_filter($allowedActions, function(string $value) {
422 1
      return ($value !== CombatAction::ACTION_POISON);
423 1
    }));
424
  }
425
  
426
  /**
427
   * Main stage of a round
428
   */
429
  public function mainStage(CombatBase $combat): void {
430
    /** @var Character[] $characters */
431 1
    $characters = array_merge($combat->team1->usableMembers, $combat->team2->usableMembers);
432 1
    usort($characters, function(Character $a, Character $b) {
433 1
      return -1 * strcmp((string) $a->initiative, (string) $b->initiative);
434 1
    });
435 1
    foreach($characters as $character) {
436 1
      $action = $combat->chooseAction($combat, $character);
437 1
      if(!in_array($action, $this->getAllowedActions(), true)) {
438
        continue;
439
      }
440
      switch($action) {
441 1
        case CombatAction::ACTION_ATTACK:
442 1
          $combat->onAttack($character, $combat->selectAttackTarget($character));
443 1
          break;
444 1
        case CombatAction::ACTION_SKILL_ATTACK:
445
          /** @var CharacterAttackSkill $skill */
446 1
          $skill = $character->usableSkills[0];
447 1
          $combat->doAttackSkill($character, $skill);
448 1
          break;
449 1
        case CombatAction::ACTION_SKILL_SPECIAL:
450
          /** @var CharacterSpecialSkill $skill */
451 1
          $skill = $character->usableSkills[0];
452 1
          $combat->doSpecialSkill($character, $skill);
453 1
          break;
454 1
        case CombatAction::ACTION_HEALING:
455 1
          $combat->onHeal($character, $combat->selectHealingTarget($character));
456 1
          break;
457
      }
458
    }
459 1
  }
460
  
461
  /**
462
   * Start next round
463
   * 
464
   * @return int Winning team/0
465
   */
466
  protected function startRound(): int {
467 1
    $this->onRoundStart($this);
468 1
    return $this->getWinner();
469
  }
470
  
471
  /**
472
   * Do a round
473
   */
474
  protected function doRound(): void {
475 1
    $this->onRound($this);
476 1
  }
477
  
478
  /**
479
   * End round
480
   * 
481
   * @return int Winning team/0
482
   */
483
  protected function endRound(): int {
484 1
    $this->onRoundEnd($this);
485 1
    return $this->getWinner();
486
  }
487
  
488
  /**
489
   * Executes the combat
490
   * 
491
   * @return int Winning team
492
   */
493
  public function execute(): int {
494 1
    if(!isset($this->team1)) {
495 1
      throw new InvalidStateException("Teams are not set.");
496
    }
497 1
    $this->onCombatStart($this);
498 1
    while($this->round <= $this->roundLimit) {
499 1
      if($this->startRound() > 0) {
500 1
        break;
501
      }
502 1
      $this->doRound();
503 1
      if($this->endRound() > 0) {
504
        break;
505
      }
506
    }
507 1
    $this->onCombatEnd($this);
508 1
    return $this->getWinner();
509
  }
510
  
511
  /**
512
   * Calculate hit chance for attack/skill attack
513
   */
514
  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...
515 1
    $hitChance = $this->successCalculator->calculateHitChance($character1, $character2, $skill);
516 1
    return Numbers::range($hitChance, ISuccessCalculator::MIN_HIT_CHANCE, ISuccessCalculator::MAX_HIT_CHANCE);
517
  }
518
  
519
  /**
520
   * Check whether action succeeded
521
   */
522
  protected function hasHit(int $hitChance): bool {
523 1
    return $this->successCalculator->hasHit($hitChance);
524
  }
525
  
526
  /**
527
   * Do an attack
528
   * Hit chance = Attacker's hit - Defender's dodge, but at least 15%
529
   * Damage = Attacker's damage - defender's defense
530
   */
531
  public function attackHarm(Character $attacker, Character $defender): void {
532 1
    $result = [];
533 1
    $hitChance = $this->calculateHitChance($attacker, $defender);
534 1
    $result["result"] = $this->hasHit($hitChance);
535 1
    $result["amount"] = 0;
536 1
    if($result["result"]) {
537 1
      $amount = $attacker->damage - $defender->defense;
538 1
      $result["amount"] = Numbers::range($amount, 0, $defender->hitpoints);
539
    }
540 1
    if($result["amount"] > 0) {
541 1
      $defender->harm($result["amount"]);
542
    }
543 1
    $result["action"] = CombatAction::ACTION_ATTACK;
544 1
    $result["name"] = "";
545 1
    $result["character1"] = $attacker;
546 1
    $result["character2"] = $defender;
547 1
    $this->results = $result;
548 1
  }
549
  
550
  /**
551
   * Use an attack skill
552
   */
553
  public function useAttackSkill(Character $attacker, Character $defender, CharacterAttackSkill $skill): void {
554 1
    $result = [];
555 1
    $hitChance = $this->calculateHitChance($attacker, $defender, $skill);
556 1
    $result["result"] = $this->hasHit($hitChance);
557 1
    $result["amount"] = 0;
558 1
    if($result["result"]) {
559
      $amount = (int) ($attacker->damage - $defender->defense / 100 * $skill->damage);
560
      $result["amount"] = Numbers::range($amount, 0, $defender->hitpoints);
561
    }
562 1
    if($result["amount"]) {
563
      $defender->harm($result["amount"]);
564
    }
565 1
    $result["action"] = CombatAction::ACTION_SKILL_ATTACK;
566 1
    $result["name"] = $skill->skill->name;
567 1
    $result["character1"] = $attacker;
568 1
    $result["character2"] = $defender;
569 1
    $this->results = $result;
570 1
    $skill->resetCooldown();
571 1
  }
572
  
573
  /**
574
   * Use a special skill
575
   */
576
  public function useSpecialSkill(Character $character1, Character $target, CharacterSpecialSkill $skill): void {
577
    $result = [
578 1
      "result" => true, "amount" => 0, "action" => CombatAction::ACTION_SKILL_SPECIAL, "name" => $skill->skill->name,
579 1
      "character1" => $character1, "character2" => $target,
580
    ];
581 1
    $this->results = $result;
582 1
    $effect = new CharacterEffect([
583 1
      "id" => "skill{$skill->skill->id}Effect",
584 1
      "type" => $skill->skill->type,
585 1
      "stat" => ((in_array($skill->skill->type, SkillSpecial::NO_STAT_TYPES, true)) ? NULL : $skill->skill->stat),
586 1
      "value" => $skill->value,
587 1
      "source" => CharacterEffect::SOURCE_SKILL,
588 1
      "duration" => $skill->skill->duration
589
    ]);
590 1
    $target->addEffect($effect);
591 1
    $effect->onApply($target, $effect);
592 1
    $skill->resetCooldown();
593 1
  }
594
  
595
  /**
596
   * Heal a character
597
   */
598
  public function heal(Character $healer, Character $patient): void {
599 1
    $result = [];
600 1
    $hitChance = $this->successCalculator->calculateHealingSuccessChance($healer);
601 1
    $hitChance = Numbers::range($hitChance, 0, 100);
602 1
    $result["result"] = $this->successCalculator->hasHit($hitChance);
603 1
    $amount = ($result["result"]) ? (int) ($healer->intelligence / 2) : 0;
604 1
    $result["amount"] = Numbers::range($amount, 0, $patient->maxHitpoints - $patient->hitpoints);
605 1
    if($result["amount"]) {
606 1
      $patient->heal($result["amount"]);
607
    }
608 1
    $result["action"] = CombatAction::ACTION_HEALING;
609 1
    $result["name"] = "";
610 1
    $result["character1"] = $healer;
611 1
    $result["character2"] = $patient;
612 1
    $this->results = $result;
613 1
  }
614
  
615
  /**
616
   * Harm poisoned characters at start of round
617
   */
618
  public function applyPoison(CombatBase $combat): void {
619
    /** @var Character[] $characters */
620 1
    $characters = array_merge($combat->team1->aliveMembers, $combat->team2->aliveMembers);
621 1
    foreach($characters as $character) {
622 1
      foreach($character->effects as $effect) {
623 1
        if($effect->type === SkillSpecial::TYPE_POISON) {
624
          $character->harm($effect->value);
625
          $action = [
626
            "action" => CombatAction::ACTION_POISON, "result" => true, "amount" => $effect->value,
627
            "character1" => $character, "character2" => $character,
628
          ];
629 1
          $combat->log->log($action);
630
        }
631
      }
632
    }
633 1
  }
634
  
635
  /**
636
   * Log results of an action
637
   */
638
  public function logResults(): void {
639 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

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