Files
dmtools/src/Entity/Combatant.php
T
FrankandClaude Sonnet 5 cb23d5b9ac Show AC on each tracker row
Combatant snapshots armor_class from the enemy when added (like maxHp),
shown as an "AC n" pill next to the name for quick hit checks. Migration
backfills existing combatants from their enemy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 19:34:24 +02:00

172 lines
3.6 KiB
PHP

<?php
namespace App\Entity;
use App\Repository\CombatantRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
/**
* One line on the running combat HP tracker. Stats are copied from the Enemy
* when added, so editing the Enemy mid-fight doesn't change the tracker.
*/
#[ORM\Entity(repositoryClass: CombatantRepository::class)]
#[ORM\Table(name: 'combatant')]
class Combatant
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\ManyToOne(targetEntity: Encounter::class, inversedBy: 'combatants')]
#[ORM\JoinColumn(name: 'encounter_id', nullable: false, onDelete: 'CASCADE')]
private ?Encounter $encounter = null;
#[ORM\ManyToOne(targetEntity: Enemy::class)]
#[ORM\JoinColumn(name: 'enemy_id', onDelete: 'SET NULL', nullable: true)]
private ?Enemy $enemy = null;
#[ORM\Column(length: 120)]
private string $label = '';
#[ORM\Column(type: Types::SMALLINT)]
private int $armorClass = 10;
#[ORM\Column(type: Types::SMALLINT)]
private int $maxHp = 1;
#[ORM\Column(type: Types::SMALLINT)]
private int $currentHp = 1;
#[ORM\Column(type: Types::SMALLINT)]
private int $position = 0;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
private \DateTimeImmutable $addedAt;
public function __construct()
{
$this->addedAt = new \DateTimeImmutable();
}
public function isDown(): bool
{
return $this->currentHp <= 0;
}
/** 0-100, for the HP bar. */
public function hpPercent(): int
{
if ($this->maxHp <= 0) {
return 0;
}
return (int) round(max(0, min($this->currentHp, $this->maxHp)) / $this->maxHp * 100);
}
public function applyDamage(int $amount): void
{
$this->currentHp = max(0, $this->currentHp - abs($amount));
}
public function applyHealing(int $amount): void
{
$this->currentHp = min($this->maxHp, $this->currentHp + abs($amount));
}
public function getId(): ?int
{
return $this->id;
}
public function getEncounter(): ?Encounter
{
return $this->encounter;
}
public function setEncounter(?Encounter $encounter): static
{
$this->encounter = $encounter;
return $this;
}
public function getEnemy(): ?Enemy
{
return $this->enemy;
}
public function setEnemy(?Enemy $enemy): static
{
$this->enemy = $enemy;
return $this;
}
public function getLabel(): string
{
return $this->label;
}
public function setLabel(string $label): static
{
$this->label = $label;
return $this;
}
public function getArmorClass(): int
{
return $this->armorClass;
}
public function setArmorClass(int $armorClass): static
{
$this->armorClass = $armorClass;
return $this;
}
public function getMaxHp(): int
{
return $this->maxHp;
}
public function setMaxHp(int $maxHp): static
{
$this->maxHp = $maxHp;
return $this;
}
public function getCurrentHp(): int
{
return $this->currentHp;
}
public function setCurrentHp(int $currentHp): static
{
$this->currentHp = $currentHp;
return $this;
}
public function getPosition(): int
{
return $this->position;
}
public function setPosition(int $position): static
{
$this->position = $position;
return $this;
}
public function getAddedAt(): \DateTimeImmutable
{
return $this->addedAt;
}
}