feat(spells): #11 SpellTable - hydrate metadata from spells.csv at startup
New SpellMetadata + SpellTable. Loads docs/research/data/spells.csv at GameWindow construction (3,956 spells x 11 useful fields including Family for buff stacking which issue #6 needs). The CSV is copied to bin/<config>/net10.0/data/spells.csv via the csproj <None Include> entry; SpellTable.LoadFromCsv resolves relative to AppContext.BaseDirectory. Hand-rolled CSV parser handles RFC 4180 quoted fields with embedded commas (the Description column) + escaped double-quotes ("" -> "). No external CsvHelper dep. Falls back to SpellTable.Empty + console warning if the file is missing (tooling contexts). Spellbook now accepts an optional SpellTable in its constructor + exposes TryGetMetadata(spellId, out SpellMetadata). When the table is absent (legacy `new Spellbook()` calls), TryGetMetadata returns false gracefully so existing tests keep passing. GameWindow: - SpellTable field initialized via LoadSpellTable() helper that handles the missing-file case + emits the spells: loaded N entries log line. - SpellBook field constructor-initialized with the loaded SpellTable so TryGetMetadata works for the live session. 10 new tests (SpellTableTests): - Empty table behavior - Header-only loads to empty - Single row populates all metadata - Quoted Description with embedded commas - Blank lines skipped - Bad-spell-id rows silently skipped (third-party data is messy) - Unknown spell-id lookup returns false - ParseRow primitive: simple comma split, quoted-field with comma, escaped double-quote. Total tests: 818 -> 828. Closes #11. Phase G (issue #6 — fold enchantment buffs into vital max via EnchantmentMath using SpellTable.Family for stacking) unblocked. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
83b020499b
commit
4ceac5cb40
7 changed files with 463 additions and 23 deletions
44
src/AcDream.Core/Spells/SpellMetadata.cs
Normal file
44
src/AcDream.Core/Spells/SpellMetadata.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
namespace AcDream.Core.Spells;
|
||||
|
||||
/// <summary>
|
||||
/// Per-spell static metadata loaded once at startup from
|
||||
/// <c>docs/research/data/spells.csv</c> via <see cref="SpellTable"/>.
|
||||
/// One record per known spell id (3,956 entries in the retail dump).
|
||||
///
|
||||
/// <para>
|
||||
/// Used for:
|
||||
/// </para>
|
||||
/// <list type="bullet">
|
||||
/// <item>Buff / debuff bar labels (<see cref="Name"/>,
|
||||
/// <see cref="School"/>, <see cref="IconId"/>).</item>
|
||||
/// <item>Stacking aggregation (<see cref="Family"/> — only one
|
||||
/// enchantment per family-bucket is active; this is what
|
||||
/// <c>EnchantmentMath</c> uses to filter out superseded
|
||||
/// buffs in <c>LocalPlayerState.GetMaxApprox</c>).</item>
|
||||
/// <item>Spell tooltips (<see cref="Description"/>,
|
||||
/// <see cref="ManaCost"/>, <see cref="Duration"/>,
|
||||
/// <see cref="SpellWords"/>).</item>
|
||||
/// <item>Cast-bar audio + animation cues
|
||||
/// (<see cref="SpellWords"/> drives the chant).</item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para>
|
||||
/// Fields not exposed (yet) from the 35-column source CSV: SortKey,
|
||||
/// Difficulty, Flags, Generation, IsFastWindup, IsIrresistible,
|
||||
/// IsOffensive, IsUntargetted, Speed, CasterEffect, TargetEffect,
|
||||
/// TargetMask, Type, plus 10 anonymous Unknown1..10. Add them on
|
||||
/// demand as panels grow.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public sealed record SpellMetadata(
|
||||
uint SpellId,
|
||||
string Name,
|
||||
string School,
|
||||
uint Family,
|
||||
uint IconId,
|
||||
string SpellWords,
|
||||
float Duration,
|
||||
int ManaCost,
|
||||
bool IsDebuff,
|
||||
bool IsFellowship,
|
||||
string Description);
|
||||
Loading…
Add table
Add a link
Reference in a new issue