Issues (30)

src/Team.php (1 issue)

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