Completed
Push — master ( dc8269...170882 )
by Jakub
01:58
created

Character   F

Complexity

Total Complexity 91

Size/Duplication

Total Lines 560
Duplicated Lines 0 %

Test Coverage

Coverage 92.39%

Importance

Changes 0
Metric Value
wmc 91
eloc 235
dl 0
loc 560
ccs 170
cts 184
cp 0.9239
rs 2
c 0
b 0
f 0

116 Methods

Rating   Name   Duplication   Size   Complexity  
getSkills() 0 2 ?
A hp$3 ➔ getRace() 0 2 1
A hp$3 ➔ getStrengthBase() 0 2 1
A hp$3 ➔ getOccupation() 0 2 1
getDamageBase() 0 2 ?
setPositionColumn() 0 2 ?
getCharisma() 0 2 ?
getIntelligenceBase() 0 2 ?
A hp$3 ➔ getSpecialization() 0 2 1
getDodgeBase() 0 2 ?
addEffect() 0 3 ?
A hp$3 ➔ getEquipment() 0 2 1
getDexterity() 0 2 ?
A hp$3 ➔ getMaxHitpoints() 0 2 1
A hp$3 ➔ getSkills() 0 2 1
A hp$3 ➔ getId() 0 2 1
A hp$3 ➔ getStrength() 0 2 1
harm() 0 2 ?
A hp$3 ➔ resetInitiative() 0 2 1
getIntelligence() 0 2 ?
getActivePet() 0 7 ?
getHitpoints() 0 2 ?
A hp$3 ➔ getEffects() 0 2 1
getMaxHitpoints() 0 2 ?
getPositionColumn() 0 2 ?
A hp$3 ➔ damageStat() 0 8 4
A hp$3 ➔ getIntelligenceBase() 0 2 1
A hp$3 ➔ getCharisma() 0 2 1
A hp$3 ➔ getInitiativeFormula() 0 2 1
A hp$3 ➔ getIntelligence() 0 2 1
getPets() 0 2 ?
A hp$3 ➔ addEffect() 0 3 1
A hp$3 ➔ getDefense() 0 2 1
A hp$3 ➔ getConstitutionBase() 0 2 1
setStats() 0 36 ?
getInitiative() 0 2 ?
getItem() 0 6 ?
getId() 0 2 ?
getName() 0 2 ?
getStrengthBase() 0 2 ?
A hp$3 ➔ applyEffectProviders() 0 10 3
C hp$3 ➔ recalculateStats() 0 38 12
A hp$3 ➔ getConstitution() 0 2 1
A hp$3 ➔ getHitpoints() 0 2 1
A hp$3 ➔ getActivePet() 0 7 2
A hp$3 ➔ getHit() 0 2 1
A hp$3 ➔ removeEffect() 0 9 3
getLevel() 0 2 ?
getSpecialization() 0 2 ?
getStrength() 0 2 ?
A hp$3 ➔ heal() 0 2 1
getPositionRow() 0 2 ?
getDefenseBase() 0 2 ?
getOccupation() 0 2 ?
A hp$3 ➔ getInitiativeFormulaParser() 0 2 1
A hp$3 ➔ getEffectProviders() 0 2 1
getEffects() 0 2 ?
setInitiativeFormulaParser() 0 5 ?
A hp$3 ➔ getPositionColumn() 0 2 1
setPositionRow() 0 2 ?
A hp$3 ➔ __construct() 0 31 4
removeEffect() 0 9 ?
getCharismaBase() 0 2 ?
getInitiativeBase() 0 2 ?
A hp$3 ➔ getPets() 0 2 1
A hp$3 ➔ getPositionRow() 0 2 1
A hp$3 ➔ getDexterity() 0 2 1
isStunned() 0 2 ?
applyEffectProviders() 0 10 ?
A hp$3 ➔ getMaxHitpointsBase() 0 2 1
A hp$3 ➔ getDamageBase() 0 2 1
A hp$3 ➔ getItem() 0 6 2
recalculateStats() 0 38 ?
A hp$3 ➔ getPet() 0 6 2
A hp$3 ➔ getLevel() 0 2 1
A hp$3 ➔ setPositionColumn() 0 2 1
getHit() 0 2 ?
getDamage() 0 2 ?
A hp$3 ➔ recalculateSecondaryStats() 0 19 5
getConstitutionBase() 0 2 ?
A hp$3 ➔ getName() 0 2 1
__construct() 0 31 ?
A hp$3 ➔ getDefenseBase() 0 2 1
resetInitiative() 0 2 ?
getGender() 0 2 ?
getDodge() 0 2 ?
getEquipment() 0 2 ?
getMaxHitpointsBase() 0 2 ?
heal() 0 2 ?
A hp$3 ➔ setInitiativeFormulaParser() 0 5 2
getInitiativeFormulaParser() 0 2 ?
getPet() 0 6 ?
getHitBase() 0 2 ?
getDexterityBase() 0 2 ?
A hp$3 ➔ getHitBase() 0 2 1
A hp$3 ➔ getDexterityBase() 0 2 1
getDefense() 0 2 ?
A hp$3 ➔ setPositionRow() 0 2 1
A hp$3 ➔ getDodge() 0 2 1
A hp$3 ➔ getGender() 0 2 1
getRace() 0 2 ?
getEffectProviders() 0 2 ?
damageStat() 0 8 ?
A hp$3 ➔ getDamage() 0 2 1
A hp$3 ➔ setStats() 0 36 5
A hp$3 ➔ getDodgeBase() 0 2 1
recalculateSecondaryStats() 0 19 ?
A hp$3 ➔ getUsableSkills() 0 2 1
getInitiativeFormula() 0 2 ?
getUsableSkills() 0 2 ?
A hp$3 ➔ getInitiativeBase() 0 2 1
A hp$3 ➔ getInitiative() 0 2 1
getConstitution() 0 2 ?
A hp$3 ➔ isStunned() 0 2 1
A hp$3 ➔ getCharismaBase() 0 2 1
A hp$3 ➔ harm() 0 2 1

How to fix   Complexity   

Complex Class

Complex classes like Character often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Character, and based on these observations, apply Extract Interface, too.

1
<?php
2
declare(strict_types=1);
3
4
namespace HeroesofAbenez\Combat;
5
6
use Nexendrie\Utils\Numbers;
7
use Symfony\Component\OptionsResolver\OptionsResolver;
8
use Nexendrie\Utils\Collection;
9
10
/**
11
 * Structure for single character
12
 *
13
 * @author Jakub Konečný
14
 * @property-read int|string $id
15
 * @property-read string $name
16
 * @property-read string $gender
17
 * @property-read string $race
18
 * @property-read string $occupation
19
 * @property-read int $level
20
 * @property-read int $strength
21
 * @property-read int $strengthBase
22
 * @property-read int $dexterity
23
 * @property-read int $dexterityBase
24
 * @property-read int $constitution
25
 * @property-read int $constitutionBase
26
 * @property-read int $intelligence
27
 * @property-read int $intelligenceBase
28
 * @property-read int $charisma
29
 * @property-read int $charismaBase
30
 * @property-read int $maxHitpoints
31
 * @property-read int $maxHitpointsBase
32
 * @property-read int $hitpoints
33
 * @property-read int $damage
34
 * @property-read int $damageBase
35
 * @property-read int $hit
36
 * @property-read int $hitBase
37
 * @property-read int $dodge
38
 * @property-read int $dodgeBase
39
 * @property-read int $initiative
40
 * @property-read int $initiativeBase
41
 * @property-read string $initiativeFormula
42
 * @property-read int $defense
43
 * @property-read int $defenseBase
44
 * @property-read Equipment[]|Collection $equipment
45
 * @property-read Pet[]|Collection $pets
46
 * @property-read BaseCharacterSkill[]|Collection $skills
47
 * @property-read int|null $activePet
48
 * @property-read CharacterEffect[] $effects
49
 * @property ICharacterEffectsProvider[]|Collection $effectProviders
50
 * @property-read bool $stunned
51
 * @property-read BaseCharacterSkill[] $usableSkills
52
 * @property IInitiativeFormulaParser $initiativeFormulaParser
53
 * @property int $positionRow
54
 * @property int $positionColumn
55
 */
56 1
class Character {
57 1
  use \Nette\SmartObject;
58
  
59
  public const HITPOINTS_PER_CONSTITUTION = 5;
60
  public const STAT_STRENGTH = "strength";
61
  public const STAT_DEXTERITY = "dexterity";
62
  public const STAT_CONSTITUTION = "constitution";
63
  public const STAT_INTELLIGENCE = "intelligence";
64
  public const STAT_CHARISMA = "charisma";
65
  public const STAT_MAX_HITPOINTS = "maxHitpoints";
66
  public const STAT_DAMAGE = "damage";
67
  public const STAT_DEFENSE = "defense";
68
  public const STAT_HIT = "hit";
69
  public const STAT_DODGE = "dodge";
70
  public const STAT_INITIATIVE = "initiative";
71
  public const BASE_STATS = [
72
    self::STAT_STRENGTH, self::STAT_DEXTERITY, self::STAT_CONSTITUTION, self::STAT_INTELLIGENCE, self::STAT_CHARISMA,
73
  ];
74
  public const SECONDARY_STATS = [
75
    self::STAT_MAX_HITPOINTS, self::STAT_DAMAGE, self::STAT_DEFENSE, self::STAT_HIT, self::STAT_DODGE, self::STAT_INITIATIVE,
0 ignored issues
show
introduced by
Line exceeds 120 characters; contains 125 characters
Loading history...
76
  ];
77
  
78
  /** @var int|string */
79
  protected $id;
80
  /** @var string */
81
  protected $name;
82
  /** @var string */
83
  protected $gender = "male";
84
  /** @var string */
85
  protected $race;
86
  /** @var string */
87
  protected $occupation;
88
  /** @var string */
89
  protected $specialization;
90
  /** @var int */
91
  protected $level;
92
  /** @var int */
93
  protected $strength;
94
  /** @var int */
95
  protected $strengthBase;
96
  /** @var int */
97
  protected $dexterity;
98
  /** @var int */
99
  protected $dexterityBase;
100
  /** @var int */
101
  protected $constitution;
102
  /** @var int */
103
  protected $constitutionBase;
104
  /** @var int */
105
  protected $intelligence;
106
  /** @var int */
107
  protected $intelligenceBase;
108
  /** @var int */
109
  protected $charisma;
110
  /** @var int */
111
  protected $charismaBase;
112
  /** @var int */
113
  protected $maxHitpoints;
114
  /** @var int */
115
  protected $maxHitpointsBase;
116
  /** @var int */
117
  protected $hitpoints;
118
  /** @var int */
119
  protected $damage = 0;
120
  /** @var int */
121
  protected $damageBase = 0;
122
  /** @var int */
123
  protected $hit = 0;
124
  /** @var int */
125
  protected $hitBase = 0;
126
  /** @var int */
127
  protected $dodge = 0;
128
  /** @var int */
129
  protected $dodgeBase = 0;
130
  /** @var int */
131
  protected $initiative = 0;
132
  /** @var int */
133
  protected $initiativeBase = 0;
134
  /** @var string */
135
  protected $initiativeFormula;
136
  /** @var IInitiativeFormulaParser */
137
  protected $initiativeFormulaParser;
138
  /** @var float */
139
  protected $defense = 0;
140
  /** @var float */
141
  protected $defenseBase = 0;
142
  /** @var Equipment[]|Collection Character's equipment */
143
  protected $equipment;
144
  /** @var Pet[]|Collection Character's pets */
145
  protected $pets;
146
  /** @var BaseCharacterSkill[]|Collection Character's skills */
147
  protected $skills;
148
  /** @var int|null */
149
  protected $activePet = null;
150
  /** @var CharacterEffect[] Active effects */
151
  protected $effects = [];
152
  /** @var ICharacterEffectsProvider[]|Collection */
153
  protected $effectProviders;
154
  /** @var bool */
155
  protected $stunned = false;
156
  /** @var int */
157
  protected $positionRow = 0;
158
  /** @var int */
159
  protected $positionColumn = 0;
160
  
161
  /**
162
   *
163
   * @param array $stats Stats of the character
164
   * @param Equipment[] $equipment Equipment of the character
165
   * @param Pet[] $pets Pets owned by the character
166
   * @param BaseCharacterSkill[] $skills Skills acquired by the character
167
   */
168
  public function __construct(array $stats, array $equipment = [], array $pets = [], array $skills = [], IInitiativeFormulaParser $initiativeFormulaParser = null) {
0 ignored issues
show
introduced by
Line exceeds 120 characters; contains 164 characters
Loading history...
169 1
    $this->initiativeFormulaParser = $initiativeFormulaParser ?? new InitiativeFormulaParser();
170 1
    $this->effectProviders = new class extends  Collection {
171
      /** @var string */
172
      protected $class = ICharacterEffectsProvider::class;
173
    };
174 1
    $this->equipment = new class extends Collection {
175
      /** @var string */
176
      protected $class = Equipment::class;
177
    };
178 1
    foreach($equipment as $eq) {
179 1
      $this->equipment[] = $this->effectProviders[] = $eq;
180
    }
181 1
    $this->pets = new class extends Collection {
182
      /** @var string */
183
      protected $class = Pet::class;
184
    };
185 1
    foreach($pets as $pet) {
186 1
      $this->pets[] = $this->effectProviders[] = $pet;
187
    }
188 1
    $this->skills = new class extends Collection {
189
      /** @var string */
190
      protected $class = BaseCharacterSkill::class;
191
    };
192 1
    foreach($skills as $skill) {
193 1
      $this->skills[] = $skill;
194
    }
195 1
    $this->equipment->lock();
196 1
    $this->pets->lock();
197 1
    $this->skills->lock();
198 1
    $this->setStats($stats);
199 1
  }
200
  
201
  protected function setStats(array $stats): void {
202 1
    $requiredStats = array_merge(["id", "name", "level", "initiativeFormula",], static::BASE_STATS);
203 1
    $allStats = array_merge($requiredStats, ["occupation", "race", "specialization", "gender",]);
204 1
    $numberStats = static::BASE_STATS;
205 1
    $textStats = ["name", "race", "occupation", "initiativeFormula",];
206 1
    $resolver = new OptionsResolver();
207 1
    $resolver->setDefined($allStats);
208 1
    $resolver->setAllowedTypes("id", ["integer", "string"]);
209 1
    foreach($numberStats as $stat) {
210 1
      $resolver->setAllowedTypes($stat, ["integer", "float"]);
211 1
      $resolver->setNormalizer($stat, function(OptionsResolver $resolver, $value) {
1 ignored issue
show
introduced by
The method parameter $resolver is never used
Loading history...
212 1
        return (int) $value;
213 1
      });
214
    }
215 1
    foreach($textStats as $stat) {
216 1
      $resolver->setNormalizer($stat, function(OptionsResolver $resolver, $value) {
1 ignored issue
show
introduced by
The method parameter $resolver is never used
Loading history...
217 1
        return (string) $value;
218 1
      });
219
    }
220 1
    $resolver->setRequired($requiredStats);
221 1
    $stats = array_filter($stats, function($key) use($allStats) {
222 1
      return in_array($key, $allStats, true);
223 1
    }, ARRAY_FILTER_USE_KEY);
224 1
    $stats = $resolver->resolve($stats);
225 1
    foreach($stats as $key => $value) {
226 1
      if(in_array($key, $numberStats, true)) {
227 1
        $this->$key = $value;
228 1
        $this->{$key . "Base"} = $value;
229
      } else {
230 1
        $this->$key = $value;
231
      }
232
    }
233 1
    $this->hitpoints = $this->maxHitpoints = $this->maxHitpointsBase = $this->constitution * static::HITPOINTS_PER_CONSTITUTION;
0 ignored issues
show
introduced by
Line exceeds 120 characters; contains 128 characters
Loading history...
234 1
    $this->recalculateSecondaryStats();
235 1
    $this->hitBase = $this->hit;
236 1
    $this->dodgeBase = $this->dodge;
237 1
  }
238
  
239
  /**
240
   * @return int|string
241
   */
242
  public function getId() {
243 1
    return $this->id;
244
  }
245
  
246
  public function getName(): string {
247 1
    return $this->name;
248
  }
249
  
250
  public function getGender(): string {
251
    return $this->gender;
252
  }
253
  
254
  public function getRace(): string {
255
    return $this->race;
256
  }
257
  
258
  public function getOccupation(): string {
259
    return $this->occupation;
260
  }
261
  
262
  public function getLevel(): int {
263 1
    return $this->level;
264
  }
265
  
266
  public function getStrength(): int {
267 1
    return $this->strength;
268
  }
269
  
270
  public function getStrengthBase(): int {
271
    return $this->strengthBase;
272
  }
273
  
274
  public function getDexterity(): int {
275 1
    return $this->dexterity;
276
  }
277
  
278
  public function getDexterityBase(): int {
279
    return $this->dexterityBase;
280
  }
281
  
282
  public function getConstitution(): int {
283 1
    return $this->constitution;
284
  }
285
  
286
  public function getConstitutionBase(): int {
287
    return $this->constitutionBase;
288
  }
289
  
290
  public function getCharisma(): int {
291 1
    return $this->charisma;
292
  }
293
  
294
  public function getCharismaBase(): int {
295
    return $this->charismaBase;
296
  }
297
  
298
  public function getMaxHitpoints(): int {
299 1
    return $this->maxHitpoints;
300
  }
301
  
302
  public function getMaxHitpointsBase(): int {
303 1
    return $this->maxHitpointsBase;
304
  }
305
  
306
  public function getHitpoints(): int {
307 1
    return $this->hitpoints;
308
  }
309
  
310
  public function getDamage(): int {
311 1
    return $this->damage;
312
  }
313
  
314
  public function getDamageBase(): int {
315
    return $this->damageBase;
316
  }
317
  
318
  public function getHit(): int {
319 1
    return $this->hit;
320
  }
321
  
322
  public function getHitBase(): int {
323
    return $this->hitBase;
324
  }
325
  
326
  public function getDodge(): int {
327 1
    return $this->dodge;
328
  }
329
  
330
  public function getDodgeBase(): int {
331
    return $this->dodgeBase;
332
  }
333
  
334
  public function getInitiative(): int {
335 1
    return $this->initiative;
336
  }
337
  
338
  public function getInitiativeBase(): int {
339 1
    return $this->initiativeBase;
340
  }
341
  
342
  public function getInitiativeFormula(): string {
343 1
    return $this->initiativeFormula;
344
  }
345
  
346
  public function getDefense(): int {
347 1
    return (int) $this->defense;
348
  }
349
  
350
  public function getDefenseBase(): int {
351
    return (int) $this->defenseBase;
352
  }
353
  
354
  /**
355
   * @return Equipment[]|Collection
356
   */
357
  public function getEquipment(): Collection {
358 1
    return $this->equipment;
359
  }
360
  
361
  /**
362
   * @return Pet[]|Collection
363
   */
364
  public function getPets(): Collection {
365
    return $this->pets;
366
  }
367
  
368
  /**
369
   * @return BaseCharacterSkill[]|Collection
370
   */
371
  public function getSkills(): Collection {
372 1
    return $this->skills;
373
  }
374
  
375
  public function getActivePet(): ?int {
376
    /** @var Pet[] $pets */
377 1
    $pets = $this->pets->getItems(["deployed" => true]);
378 1
    if(count($pets) === 0) {
379 1
      return null;
380
    }
381 1
    return $pets[0]->id;
382
  }
383
  
384
  /**
385
   * @return CharacterEffect[]
386
   */
387
  public function getEffects(): array {
388 1
    return $this->effects;
389
  }
390
  
391
  /**
392
   * @return ICharacterEffectsProvider[]|Collection
393
   */
394
  public function getEffectProviders(): Collection {
395 1
    return $this->effectProviders;
396
  }
397
  
398
  public function isStunned(): bool {
399 1
    return $this->stunned;
400
  }
401
  
402
  public function getSpecialization(): string {
403
    return $this->specialization;
404
  }
405
  
406
  public function getIntelligence(): int {
407 1
    return $this->intelligence;
408
  }
409
  
410
  public function getIntelligenceBase(): int {
411
    return $this->intelligenceBase;
412
  }
413
  
414
  public function getInitiativeFormulaParser(): IInitiativeFormulaParser {
415 1
    return $this->initiativeFormulaParser;
416
  }
417
  
418
  public function setInitiativeFormulaParser(IInitiativeFormulaParser $initiativeFormulaParser): void {
419 1
    $oldParser = $this->initiativeFormulaParser;
420 1
    $this->initiativeFormulaParser = $initiativeFormulaParser;
421 1
    if($oldParser !== $initiativeFormulaParser) {
422 1
      $this->recalculateStats();
423
    }
424 1
  }
425
  
426
  public function getPositionRow(): int {
427 1
    return $this->positionRow;
428
  }
429
  
430
  public function setPositionRow(int $positionRow): void {
431 1
    $this->positionRow = Numbers::range($positionRow, 1, PHP_INT_MAX);
432 1
  }
433
  
434
  public function getPositionColumn(): int {
435 1
    return $this->positionColumn;
436
  }
437
  
438
  public function setPositionColumn(int $positionColumn): void {
439 1
    $this->positionColumn = Numbers::range($positionColumn, 1, PHP_INT_MAX);
440 1
  }
441
  
442
  /**
443
   * @internal
444
   */
445
  public function applyEffectProviders(): void {
446 1
    foreach($this->effectProviders as $item) {
447 1
      $effects = $item->getCombatEffects();
448 1
      array_walk($effects, function(CharacterEffect $effect) {
449
        try {
450 1
          $this->removeEffect($effect->id);
451 1
        } catch(\OutOfBoundsException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
introduced by
Empty CATCH statement detected
Loading history...
452
453
        }
454 1
        $this->addEffect($effect);
455 1
      });
456
    }
457 1
  }
458
  
459
  /**
460
   * Applies new effect on the character
461
   */
462
  public function addEffect(CharacterEffect $effect): void {
463 1
    $this->effects[] = $effect;
464 1
    $effect->onApply($this, $effect);
465 1
  }
466
467
  /**
468
   * Removes specified effect from the character
469
   *
470
   * @throws \OutOfBoundsException
471
   */
472
  public function removeEffect(string $effectId): void {
473 1
    foreach($this->effects as $i => $effect) {
474 1
      if($effect->id == $effectId) {
475 1
        unset($this->effects[$i]);
476 1
        $effect->onRemove($this, $effect);
477 1
        return;
478
      }
479
    }
480 1
    throw new \OutOfBoundsException("Effect to remove was not found.");
481
  }
482
  
483
  /**
484
   * Get specified equipment of the character
485
   *
486
   * @throws \OutOfBoundsException
487
   */
488
  public function getItem(int $itemId): Equipment {
489 1
    $equipment = $this->equipment->getItems(["id" => $itemId]);
490 1
    if(count($equipment) === 0) {
491 1
      throw new \OutOfBoundsException("Item was not found.");
492
    }
493 1
    return $equipment[0];
494
  }
495
  
496
  /**
497
   * Get specified pet
498
   *
499
   * @throws \OutOfBoundsException
500
   */
501
  public function getPet(int $petId): Pet {
502 1
    $pets = $this->pets->getItems(["id" => $petId]);
503 1
    if(count($pets) === 0) {
504 1
      throw new \OutOfBoundsException("Pet was not found.");
505
    }
506 1
    return $pets[0];
507
  }
508
  
509
  /**
510
   * @return BaseCharacterSkill[]
511
   */
512
  public function getUsableSkills(): array {
513 1
    return $this->skills->getItems(["usable" => true]);
514
  }
515
  
516
  /**
517
   * Harm the character
518
   */
519
  public function harm(int $amount): void {
520 1
    $this->hitpoints -= Numbers::range($amount, 0, $this->hitpoints);
521 1
  }
522
  
523
  /**
524
   * Heal the character
525
   */
526
  public function heal(int $amount): void {
527 1
    $this->hitpoints += Numbers::range($amount, 0, $this->maxHitpoints - $this->hitpoints);
528 1
  }
529
  
530
  /**
531
   * Determine which (primary) stat should be used to calculate damage
532
   */
533
  public function damageStat(): string {
534 1
    foreach($this->equipment as $item) {
535 1
      if(!$item->worn OR !$item instanceof Weapon) {
536 1
        continue;
537
      }
538 1
      return $item->damageStat;
539
    }
540 1
    return static::STAT_STRENGTH;
541
  }
542
  
543
  /**
544
   * Recalculate secondary stats from the the primary ones
545
   */
546
  public function recalculateSecondaryStats(): void {
547
    $stats = [
548 1
      static::STAT_DAMAGE => $this->damageStat(), static::STAT_HIT => static::STAT_DEXTERITY,
549 1
      static::STAT_DODGE => static::STAT_DEXTERITY, static::STAT_MAX_HITPOINTS => static::STAT_CONSTITUTION,
550 1
      static::STAT_INITIATIVE => "",
551
    ];
552 1
    foreach($stats as $secondary => $primary) {
553 1
      $gain = $this->$secondary - $this->{$secondary . "Base"};
554 1
      if($secondary === static::STAT_DAMAGE) {
555 1
        $base = (int) round($this->$primary / 2);
556 1
      } elseif($secondary === static::STAT_MAX_HITPOINTS) {
557 1
        $base = $this->$primary * static::HITPOINTS_PER_CONSTITUTION;
558 1
      } elseif($secondary === static::STAT_INITIATIVE) {
559 1
        $base = $this->initiativeFormulaParser->calculateInitiative($this);
560
      } else {
561 1
        $base = $this->$primary * 3;
562
      }
563 1
      $this->{$secondary . "Base"} = $base;
564 1
      $this->$secondary = $base + $gain;
565
    }
566 1
  }
567
  
568
  /**
569
   * Recalculates stats of the character (mostly used during combat)
570
   */
571
  public function recalculateStats(): void {
572 1
    $stats = array_merge(static::BASE_STATS, static::SECONDARY_STATS);
573 1
    $stunned = false;
574 1
    $debuffs = [];
575 1
    foreach($stats as $stat) {
576 1
      $$stat = $this->{$stat . "Base"};
577 1
      $debuffs[$stat] = 0;
578
    }
579 1
    foreach($this->effects as $i => $effect) {
580 1
      $stat = $effect->stat;
581 1
      $type = $effect->type;
582 1
      $duration = $effect->duration;
583 1
      if(is_int($duration) AND $duration < 1) {
584 1
        $this->removeEffect($effect->id);
585 1
        continue;
586
      }
587 1
      if(!in_array($type, SkillSpecial::NO_STAT_TYPES, true)) {
588 1
        $bonus_value = ($effect->valueAbsolute) ? $effect->value : $$stat / 100 * $effect->value;
589
      }
590 1
      if($type == SkillSpecial::TYPE_BUFF) {
591 1
        $$stat += $bonus_value;
592 1
      } elseif($type == SkillSpecial::TYPE_DEBUFF) {
593 1
        $debuffs[$stat] += $bonus_value;
594 1
      } elseif($type == SkillSpecial::TYPE_STUN) {
595 1
        $stunned = true;
596
      }
597 1
      unset($stat, $type, $duration, $bonus_value);
598
    }
599 1
    foreach($debuffs as $stat => $value) {
600 1
      $value = min($value, 80);
601 1
      $bonus_value = $$stat / 100 * $value;
602 1
      $$stat -= $bonus_value;
603
    }
604 1
    foreach($stats as $stat) {
605 1
      $this->$stat = (int) round($$stat);
606
    }
607 1
    $this->recalculateSecondaryStats();
608 1
    $this->stunned = $stunned;
609 1
  }
610
  
611
  /**
612
   * Reset character's initiative
613
   */
614
  public function resetInitiative(): void {
615 1
    $this->initiative = $this->initiativeBase = 0;
616 1
  }
617
}
618
?>