Characters: seed the current party sheets, add skill expertise
The party sheets in assets/characters/ changed: Darrah_Oakwanderer.pdf is gone, replaced by Darragh Fermwyr (same halfling ranger, renamed and updated), Owayn Fermwyr (stout halfling fighter, BJ) and Dunnchadh (ghostwise halfling druid, Circle of the Blighted, Rick). - app:character:seed now carries those three sheets, read from the PDFs' form fields. Darragh's portrait is renamed to match his new name. - New skill expertise field (double proficiency bonus), since Darragh's Deft Explorer gives him +7 Stealth and the sheet had nowhere to show it. Expertise implies proficiency; the sheet marks it with two dots, the edit form gets a checkbox list next to skill proficiencies. - Migration adds skill_expertise, backfilling existing rows with [] so the NOT NULL JSON column works on tables that already have characters. Tests: expertise math on the entity, and a catalog test that every seed sheet hydrates into a Character that passes validation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Character;
|
||||
|
||||
use App\Command\SeedCharactersCommand;
|
||||
use App\Entity\Character;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
/**
|
||||
* Every ready-made sheet in app:character:seed must hydrate into a Character
|
||||
* that passes validation - a typo'd field name or an over-long value would
|
||||
* otherwise only surface when someone runs the command on testing/live.
|
||||
*/
|
||||
class SeedCharactersCatalogTest extends KernelTestCase
|
||||
{
|
||||
public function testEveryCatalogSheetHydratesIntoAValidCharacter(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
$command = self::getContainer()->get(SeedCharactersCommand::class);
|
||||
$validator = self::getContainer()->get(ValidatorInterface::class);
|
||||
|
||||
$sheets = (new \ReflectionMethod($command, 'sheets'))->invoke($command);
|
||||
$hydrate = new \ReflectionMethod($command, 'hydrate');
|
||||
|
||||
$names = array_column($sheets, 'name');
|
||||
self::assertSame(['Darragh Fermwyr', 'Owayn Fermwyr', 'Dunnchadh'], $names);
|
||||
|
||||
foreach ($sheets as $row) {
|
||||
/** @var Character $c */
|
||||
$c = $hydrate->invoke($command, $row);
|
||||
$violations = $validator->validate($c);
|
||||
self::assertCount(0, $violations, $row['name'].': '.$violations);
|
||||
|
||||
foreach ($c->getSkillProficiencies() as $skill) {
|
||||
self::assertArrayHasKey($skill, Character::SKILLS, $row['name']);
|
||||
}
|
||||
foreach ($c->getSavingThrowProficiencies() as $ability) {
|
||||
self::assertContains($ability, Character::ABILITIES, $row['name']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,20 @@ class CharacterTest extends TestCase
|
||||
self::assertSame(1, $c->skillModifier('Perception'));
|
||||
}
|
||||
|
||||
public function testExpertiseDoublesProficiencyBonus(): void
|
||||
{
|
||||
$c = $this->darragh()->setSkillExpertise(['Stealth']);
|
||||
|
||||
// Dex 17 -> +3, expertise (2 x +2 prof) -> +7
|
||||
self::assertSame(7, $c->skillModifier('Stealth'));
|
||||
// Sleight of Hand stays plain proficient -> +5
|
||||
self::assertSame(5, $c->skillModifier('Sleight of Hand'));
|
||||
|
||||
// Expertise alone implies proficiency
|
||||
$c->setSkillProficiencies([])->setSkillExpertise(['Perception']);
|
||||
self::assertSame(5, $c->skillModifier('Perception')); // Wis +1, 2 x +2
|
||||
}
|
||||
|
||||
public function testSaveModifierAddsProficiencyOnlyWhenProficient(): void
|
||||
{
|
||||
$c = $this->darragh();
|
||||
|
||||
Reference in New Issue
Block a user