Team::getLowestHpCharacter()   A
last analyzed

Complexity

Conditions 6
Paths 7

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
c 1
b 0
f 0
dl 0
loc 17
rs 9.2222
ccs 11
cts 11
cp 1
cc 6
nc 7
nop 1
crap 6
1
<?php
2
declare(strict_types=1);
3
4
namespace HeroesofAbenez\Combat;
5
6
use Nexendrie\Utils\Collection;
7
use Nexendrie\Utils\Numbers;
8
9
/**
10
 * Structure for a team in combat
11
 *
12
 * @author Jakub Konečný
13
 * @property-read Character[] $aliveMembers
14
 * @property-read Character[] $usableMembers
15
 * @property int $maxRowSize
16
 * @property-read int|null $rowToAttack
17
 */
18 1
final class Team extends Collection
19
{
20
    use \Nette\SmartObject;
21
22
    private const LOWEST_HP_THRESHOLD = 0.5;
23
24
    protected string $class = Character::class;
25
    private int $maxRowSize = 5;
26
27 1
    public function __construct(public readonly string $name)
28
    {
29 1
        parent::__construct();
30 1
    }
31
32
    protected function getMaxRowSize(): int
33
    {
34 1
        return $this->maxRowSize;
35
    }
36
37
    protected function setMaxRowSize(int $maxRowSize): void
38
    {
39 1
        $this->maxRowSize = Numbers::range($maxRowSize, 1, PHP_INT_MAX);
40 1
    }
41
42
    /**
43
     * Get alive members from the team
44
     *
45
     * @return Character[]
46
     */
47
    protected function getAliveMembers(): array
48
    {
49 1
        return $this->getItems(["hitpoints>" => 0,]);
50
    }
51
52
    /**
53
     * Get members which can perform an action
54
     *
55
     * @return Character[]
56
     */
57
    protected function getUsableMembers(): array
58
    {
59 1
        return $this->getItems(["canAct()" => true]);
60
    }
61
62
    /**
63
     * Check whether the team has at least 1 alive member
64
     */
65
    public function hasAliveMembers(): bool
66
    {
67 1
        return count($this->getAliveMembers()) > 0;
68
    }
69
70
    /**
71
     * Set character's position in team
72
     *
73
     * @param string|int $id
74
     * @throws \OutOfBoundsException
75
     * @throws InvalidCharacterPositionException
76
     */
77
    public function setCharacterPosition($id, int $row, int $column): void
78
    {
79 1
        if (!$this->hasItems(["id" => $id])) {
80 1
            throw new \OutOfBoundsException("Character $id is not in the team");
81 1
        } elseif (count($this->getItems(["positionRow" => $row])) >= $this->maxRowSize) {
82 1
            throw new InvalidCharacterPositionException(
83 1
                "Row $row is full.",
84 1
                InvalidCharacterPositionException::ROW_FULL
85
            );
86 1
        } elseif ($this->hasItems(["positionRow" => $row, "positionColumn" => $column])) {
87 1
            throw new InvalidCharacterPositionException(
88 1
                "Row $row column $column is occupied.",
89 1
                InvalidCharacterPositionException::POSITION_OCCUPIED
90
            );
91
        }
92 1
        $character = $this->getItems(["id" => $id])[0];
93 1
        $character->positionRow = $row;
94 1
        $character->positionColumn = $column;
95 1
    }
96
97
    public function getRandomCharacter(): ?Character
98
    {
99 1
        $characters = $this->aliveMembers;
100 1
        if (count($characters) === 0) {
101 1
            return null;
102 1
        } elseif (count($characters) === 1) {
103 1
            return $characters[0];
104
        }
105 1
        $roll = rand(0, count($characters) - 1);
106 1
        return $characters[$roll];
107
    }
108
109
    public function getLowestHpCharacter(float $threshold = self::LOWEST_HP_THRESHOLD): ?Character
110
    {
111 1
        $lowestHp = PHP_INT_MAX;
112 1
        $lowestIndex = PHP_INT_MIN;
113 1
        if (count($this->aliveMembers) === 0) {
114 1
            return null;
115
        }
116 1
        foreach ($this->aliveMembers as $index => $member) {
117 1
            if ($member->hitpoints <= $member->maxHitpoints * $threshold && $member->hitpoints < $lowestHp) {
118 1
                $lowestHp = $member->hitpoints;
119 1
                $lowestIndex = $index;
120
            }
121
        }
122 1
        if ($lowestIndex === PHP_INT_MIN) {
123 1
            return null;
124
        }
125 1
        return $this->aliveMembers[$lowestIndex];
126
    }
127
128
    protected function getRowToAttack(): ?int
129
    {
130 1
        for ($i = 1; $i <= $this->maxRowSize; $i++) {
131 1
            if ($this->hasItems(["positionRow" => $i, "hitpoints>" => 0,])) {
132 1
                return $i;
133
            }
134
        }
135 1
        return null;
136
    }
137
}
138