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 string $name |
14
|
|
|
* @property-read Character[] $aliveMembers |
15
|
|
|
* @property-read Character[] $usableMembers |
16
|
|
|
* @property int $maxRowSize |
17
|
|
|
* @property-read int|null $rowToAttack |
18
|
|
|
*/ |
19
|
1 |
|
final class Team extends Collection { |
20
|
|
|
protected const LOWEST_HP_THRESHOLD = 0.5; |
21
|
|
|
|
22
|
|
|
/** @var string */ |
23
|
|
|
protected $class = Character::class; |
24
|
|
|
/** @var string Name of the team */ |
25
|
|
|
protected $name; |
26
|
|
|
/** @var int */ |
27
|
|
|
protected $maxRowSize = 5; |
28
|
|
|
|
29
|
1 |
|
use \Nette\SmartObject; |
30
|
|
|
|
31
|
|
|
public function __construct(string $name) { |
32
|
1 |
|
parent::__construct(); |
33
|
1 |
|
$this->name = $name; |
34
|
1 |
|
} |
35
|
|
|
|
36
|
|
|
public function getName(): string { |
37
|
1 |
|
return $this->name; |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
public function getMaxRowSize(): int { |
41
|
1 |
|
return $this->maxRowSize; |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
public function setMaxRowSize(int $maxRowSize): void { |
45
|
1 |
|
$this->maxRowSize = Numbers::range($maxRowSize, 1, PHP_INT_MAX); |
46
|
1 |
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Get alive members from the team |
50
|
|
|
* |
51
|
|
|
* @return Character[] |
52
|
|
|
*/ |
53
|
|
|
public function getAliveMembers(): array { |
54
|
1 |
|
return $this->getItems(["hitpoints>" => 0,]); |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Get members which can perform an action |
59
|
|
|
* |
60
|
|
|
* @return Character[] |
61
|
|
|
*/ |
62
|
|
|
public function getUsableMembers(): array { |
63
|
1 |
|
return $this->getItems([ |
64
|
1 |
|
"stunned" => false, "hitpoints>" => 0, |
65
|
|
|
]); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* Check whether the team has at least 1 alive member |
70
|
|
|
*/ |
71
|
|
|
public function hasAliveMembers(): bool { |
72
|
1 |
|
return count($this->getAliveMembers()) > 0; |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/**"hitpoints>" => 0 |
76
|
|
|
* Set character's position in team |
77
|
|
|
* |
78
|
|
|
* @param string|int $id |
79
|
|
|
* @throws \OutOfBoundsException |
80
|
|
|
* @throws InvalidCharacterPositionException |
81
|
|
|
*/ |
82
|
|
|
public function setCharacterPosition($id, int $row, int $column): void { |
83
|
1 |
|
if(!$this->hasItems(["id" => $id])) { |
84
|
1 |
|
throw new \OutOfBoundsException("Character $id is not in the team"); |
85
|
1 |
|
} elseif(count($this->getItems(["positionRow" => $row])) >= $this->maxRowSize) { |
86
|
1 |
|
throw new InvalidCharacterPositionException("Row $row is full.", InvalidCharacterPositionException::ROW_FULL); |
87
|
1 |
|
} elseif($this->hasItems(["positionRow" => $row, "positionColumn" => $column])) { |
88
|
1 |
|
throw new InvalidCharacterPositionException("Row $row column $column is occupied.", InvalidCharacterPositionException::POSITION_OCCUPIED); |
|
|
|
|
89
|
|
|
} |
90
|
1 |
|
$character = $this->getItems(["id" => $id])[0]; |
91
|
1 |
|
$character->positionRow = $row; |
92
|
1 |
|
$character->positionColumn = $column; |
93
|
1 |
|
} |
94
|
|
|
|
95
|
|
|
public function getRandomCharacter(): ?Character { |
96
|
1 |
|
$characters = $this->aliveMembers; |
97
|
1 |
|
if(count($characters) === 0) { |
98
|
1 |
|
return null; |
99
|
1 |
|
} elseif(count($characters) === 1) { |
100
|
1 |
|
return $characters[0]; |
101
|
|
|
} |
102
|
1 |
|
$roll = rand(0, count($characters) - 1); |
103
|
1 |
|
return $characters[$roll]; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
public function getLowestHpCharacter(float $threshold = self::LOWEST_HP_THRESHOLD): ?Character { |
107
|
1 |
|
$lowestHp = PHP_INT_MAX; |
108
|
1 |
|
$lowestIndex = PHP_INT_MIN; |
109
|
1 |
|
if(count($this->aliveMembers) === 0) { |
110
|
1 |
|
return null; |
111
|
|
|
} |
112
|
1 |
|
foreach($this->aliveMembers as $index => $member) { |
113
|
1 |
|
if($member->hitpoints <= $member->maxHitpoints * $threshold AND $member->hitpoints < $lowestHp) { |
114
|
1 |
|
$lowestHp = $member->hitpoints; |
115
|
1 |
|
$lowestIndex = $index; |
116
|
|
|
} |
117
|
|
|
} |
118
|
1 |
|
if($lowestIndex === PHP_INT_MIN) { |
119
|
1 |
|
return null; |
120
|
|
|
} |
121
|
1 |
|
return $this->aliveMembers[$lowestIndex]; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
public function getRowToAttack(): ?int { |
125
|
1 |
|
if(count($this->aliveMembers) === 0) { |
126
|
1 |
|
return null; |
127
|
|
|
} |
128
|
1 |
|
for($i = 1; $i <= PHP_INT_MAX; $i++) { |
129
|
1 |
|
if($this->hasItems(["positionRow" => $i, "hitpoints>" => 0,])) { |
130
|
1 |
|
return $i; |
131
|
|
|
} |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
?> |