From 88149ab1cf6348a32f64e213ba0ce9bb760c095d Mon Sep 17 00:00:00 2001 From: Keith Solomon Date: Sun, 5 Jul 2026 18:47:20 -0500 Subject: [PATCH] feat: catalog curated abilities and definitions --- src/Domain/AbilityCatalog.php | 52 +++++++++++++++++++++ src/Domain/AbilityDefinition.php | 51 ++++++++++++++++++++ src/Domain/AbilityId.php | 12 +++++ tests/Unit/Domain/AbilityCatalogTest.php | 59 ++++++++++++++++++++++++ 4 files changed, 174 insertions(+) create mode 100644 src/Domain/AbilityCatalog.php create mode 100644 src/Domain/AbilityDefinition.php create mode 100644 src/Domain/AbilityId.php create mode 100644 tests/Unit/Domain/AbilityCatalogTest.php diff --git a/src/Domain/AbilityCatalog.php b/src/Domain/AbilityCatalog.php new file mode 100644 index 0000000..6114d62 --- /dev/null +++ b/src/Domain/AbilityCatalog.php @@ -0,0 +1,52 @@ + + */ + public static function definitions(): array + { + static $cache = null; + + if ($cache !== null) { + return $cache; + } + + $cache = [ + AbilityId::Heal->value => new AbilityDefinition( + id: AbilityId::Heal, + label: 'Heal', + range: 2, + areaRadius: 0, + target: AbilityDefinition::TARGET_ALLY, + effect: AbilityDefinition::EFFECT_RESTORE_HEALTH, + effectValue: 5, + ), + AbilityId::AreaDamage->value => new AbilityDefinition( + id: AbilityId::AreaDamage, + label: 'Area Damage', + range: 3, + areaRadius: 1, + target: AbilityDefinition::TARGET_TILE, + effect: AbilityDefinition::EFFECT_DEAL_DAMAGE, + effectValue: 3, + ), + AbilityId::Buff->value => new AbilityDefinition( + id: AbilityId::Buff, + label: 'Rally', + range: 1, + areaRadius: 0, + target: AbilityDefinition::TARGET_ALLY, + effect: AbilityDefinition::EFFECT_APPLY_BUFF, + effectValue: 2, + ), + ]; + + return $cache; + } +} diff --git a/src/Domain/AbilityDefinition.php b/src/Domain/AbilityDefinition.php new file mode 100644 index 0000000..506d0a2 --- /dev/null +++ b/src/Domain/AbilityDefinition.php @@ -0,0 +1,51 @@ + */ + private const TARGETS = [self::TARGET_SELF, self::TARGET_ALLY, self::TARGET_ENEMY, self::TARGET_TILE]; + + /** @var list */ + private const EFFECTS = [self::EFFECT_RESTORE_HEALTH, self::EFFECT_DEAL_DAMAGE, self::EFFECT_APPLY_BUFF]; + + public function __construct( + public AbilityId $id, + public string $label, + public int $range, + public int $areaRadius, + public string $target, + public string $effect, + public int $effectValue, + ) { + if ($this->range < 0) { + throw new InvalidArgumentException('Ability range cannot be negative.'); + } + + if ($this->areaRadius < 0) { + throw new InvalidArgumentException('Ability area radius cannot be negative.'); + } + + if (!in_array($this->target, self::TARGETS, true)) { + throw new InvalidArgumentException("Unknown ability target: {$this->target}."); + } + + if (!in_array($this->effect, self::EFFECTS, true)) { + throw new InvalidArgumentException("Unknown ability effect: {$this->effect}."); + } + } +} diff --git a/src/Domain/AbilityId.php b/src/Domain/AbilityId.php new file mode 100644 index 0000000..b4f5d0f --- /dev/null +++ b/src/Domain/AbilityId.php @@ -0,0 +1,12 @@ +value]; + + self::assertInstanceOf(AbilityDefinition::class, $definition); + self::assertSame('Heal', $definition->label); + self::assertSame(2, $definition->range); + self::assertSame(0, $definition->areaRadius); + self::assertSame('ally', $definition->target); + self::assertSame('restore_health', $definition->effect); + self::assertSame(5, $definition->effectValue); + } + + public function testAreaDamageTargetsAnEnemyTileAndAffectsANeighborhood(): void + { + $definition = AbilityCatalog::definitions()[AbilityId::AreaDamage->value]; + + self::assertSame('Area Damage', $definition->label); + self::assertSame(3, $definition->range); + self::assertSame(1, $definition->areaRadius); + self::assertSame('tile', $definition->target); + self::assertSame('deal_damage', $definition->effect); + self::assertSame(3, $definition->effectValue); + } + + public function testBuffTargetsAnAllyAndAppliesAnAttackBonus(): void + { + $definition = AbilityCatalog::definitions()[AbilityId::Buff->value]; + + self::assertSame('Rally', $definition->label); + self::assertSame(1, $definition->range); + self::assertSame(0, $definition->areaRadius); + self::assertSame('ally', $definition->target); + self::assertSame('apply_buff', $definition->effect); + self::assertSame(2, $definition->effectValue); + } +}