feat: catalog curated abilities and definitions
This commit is contained in:
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace BattleForge\Domain;
|
||||||
|
|
||||||
|
final class AbilityCatalog
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return array<string, AbilityDefinition>
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace BattleForge\Domain;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
|
final readonly class AbilityDefinition
|
||||||
|
{
|
||||||
|
public const TARGET_SELF = 'self';
|
||||||
|
public const TARGET_ALLY = 'ally';
|
||||||
|
public const TARGET_ENEMY = 'enemy';
|
||||||
|
public const TARGET_TILE = 'tile';
|
||||||
|
|
||||||
|
public const EFFECT_RESTORE_HEALTH = 'restore_health';
|
||||||
|
public const EFFECT_DEAL_DAMAGE = 'deal_damage';
|
||||||
|
public const EFFECT_APPLY_BUFF = 'apply_buff';
|
||||||
|
|
||||||
|
/** @var list<string> */
|
||||||
|
private const TARGETS = [self::TARGET_SELF, self::TARGET_ALLY, self::TARGET_ENEMY, self::TARGET_TILE];
|
||||||
|
|
||||||
|
/** @var list<string> */
|
||||||
|
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}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace BattleForge\Domain;
|
||||||
|
|
||||||
|
enum AbilityId: string
|
||||||
|
{
|
||||||
|
case Heal = 'heal';
|
||||||
|
case AreaDamage = 'area_damage';
|
||||||
|
case Buff = 'buff';
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace BattleForge\Tests\Unit\Domain;
|
||||||
|
|
||||||
|
use BattleForge\Domain\AbilityCatalog;
|
||||||
|
use BattleForge\Domain\AbilityDefinition;
|
||||||
|
use BattleForge\Domain\AbilityId;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
final class AbilityCatalogTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testItExposesExactlyTheThreePlannedAbilities(): void
|
||||||
|
{
|
||||||
|
$keys = array_keys(AbilityCatalog::definitions());
|
||||||
|
|
||||||
|
sort($keys);
|
||||||
|
|
||||||
|
self::assertSame(['area_damage', 'buff', 'heal'], $keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHealTargetsAnAllyWithinRangeAndRestoresHealth(): void
|
||||||
|
{
|
||||||
|
$definition = AbilityCatalog::definitions()[AbilityId::Heal->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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user