6.5 KiB
tags, status, updated, permalink
| tags | status | updated | permalink | ||||
|---|---|---|---|---|---|---|---|
|
built | 2026-05-31 | gamevault/02-game-design/data-driven-abilities |
M3 — Data-Driven Abilities & Modifiers (design)
Status: ✅ BUILT 2026-05-31 — runtime-validated on 6.4.7. Final architecture + build deviations in DR-004_M3_DataDriven_Abilities_Modifiers; session 2026-05-31_M3_Data_Driven_Abilities. Build-time refinements vs this design: prefab refs went to a companion
AbilityPrefabElementbuffer (not the blob — blobs don't remap entity refs); recompute runs every predicted tick (not the dirty-flag option — rollback-correctness);StatModifier.Target/Opreplicate as rawbyte;MaxHealthsingle-sourced from the character SO;PlayerAimSystemleft as-is. The design narrative below is preserved as the original intent.
Goal: make the combat/ability system scalable by moving ability and character stats out of hard-baked components into authored definitions, with runtime modifiers (upgrades/buffs) mutating effective values.
Why
M2 hard-codes combat values in baked components (AbilityStats, Projectile Speed/Damage/Range, PlayerMoveStats, Health.Max). Adding abilities or tuning balance means editing code/prefabs. For an ARPG with many abilities + an upgrade/modifier meta-game, definitions must be data a designer edits, and values must be mutable at runtime by upgrades/buffs. Locked by Pillars: server-authoritative + deterministic, so modifiers must be prediction-correct.
Decisions (operator-chosen 2026-05-31)
- Scope = pattern slice. Establish the definition + modifier pattern and prove it by refactoring the current projectile ability + 1–2 sample abilities onto it. Not building a full multi-archetype/loadout system yet (that comes later, on this foundation).
- Authoring = ScriptableObjects → baked to blob assets. Designers edit SO assets in the inspector; a
Bakerconverts them into DOTS-native immutableBlobAssetReferenceruntime data (Burst-fast, shared, zero per-instance cost). - Modifiers = flat + percent stacks. ARPG-standard additive (+X) and percentage (+X%) modifier stacks compute effective stats from the base definition; upgrades add/remove modifiers. Server-authoritative + prediction-correct.
- Stat scope = abilities + character stats. One framework covers ability stats (damage/cooldown/range/projectile speed/auto-target) and character stats (max health, move speed, turn rate), so an upgrade can buff either.
Architecture (proposed — DOTS-idiomatic)
Authoring → runtime pipeline
AbilityDefinition/CharacterStatsDefinitionScriptableObjects (designer-facing; live underAssets/_Project/Abilities/).- A baker bakes the SO set into a singleton "definition database": a
BlobAssetReference<AbilityDatabaseBlob>holding an array of ability/stat definitions indexed by a stableAbilityId(andCharacterId). Entity-prefab references (e.g. the projectile ghost) are resolved at bake into the blob; managed/UI assets (icon, description, VFX/SFX prefabs) stay off the blob (blobs are unmanaged) — looked up separately by id for presentation. - Gameplay entities carry a light
AbilityRef { AbilityId }/CharacterStatsRef { CharacterId }instead of inlined values; systems read the base values from the blob.
Base → modifiers → effective stats
StatModifier(buffer element):{ StatTarget (enum: Damage, CooldownTicks, Range, ProjectileSpeed, MoveSpeed, MaxHealth, …), Op (Flat | PercentAdd | PercentMult), float Value, ModifierSource }.- Per-entity
DynamicBuffer<StatModifier>holds active modifiers (from upgrades, gear, buffs). - A deterministic
StatRecomputeSystemcomputes effective-stat components (e.g.EffectiveAbilityStats,EffectiveMoveStats) frombase (blob) + modifiersusing the standard order:effective = (base + Σ flat) × (1 + Σ percentAdd) × Π (1 + percentMult). Recompute on modifier-set change (dirty) rather than every tick. - Gameplay systems read the effective components:
AbilityFireSystemuses effective damage/cooldown/range;PlayerMoveSystemuses effective move speed; health uses effective max.
Netcode determinism (the important constraint)
- Definitions are static config — baked identically into both worlds' blobs; not replicated.
- Modifiers affect server-authoritative damage, so the predicted client must compute the same effective stats: the active-modifier state is replicated (a ghost buffer of
StatModifier, or derived deterministically from a replicated upgrade-level/loadout component).StatRecomputeSystemis a pure function → predicted + server results match. No wall-clock; timed buffs (a later extension) expire onNetworkTick.
Proposed definition fields (refine at build time)
AbilityDefinition: AbilityId; DisplayName (FixedString); [authoring/UI only] Description, Icon; Archetype (enum — Projectile now; Hitscan/MeleeCone/AoE/Buff later); Damage; DamageType/Element (enum, for resistances later); CooldownTicks; Range; ProjectileSpeed; AutoTargetRange, AutoTargetConeDegrees; ProjectilePrefab (entity-prefab ref, baked); [UI] MuzzleVfx/HitVfx/Sfx; Tags (FixedList — for modifier targeting, e.g. "all Fire abilities +10%"). Future: ResourceCost, CastTime, Charges.
CharacterStatsDefinition: CharacterId; MaxHealth; MoveSpeed; TurnRate; (extensible).
Refactor target (the pattern slice)
Move M2's hard-baked values onto the data model: AbilityStats + Projectile(Damage/Speed/Range) ← ability definition by id; PlayerMoveStats + Health.Max ← character-stats definition; gameplay reads route through the effective-stat components. Add 1–2 sample abilities (e.g. a faster low-damage shot + a slow heavy shot) purely as data to prove no code changes are needed per ability.
Open questions (defer to build)
- Modifier replication: ghost buffer of active modifiers vs. replicate a compact upgrade/loadout state and re-derive modifiers locally (less bandwidth).
- Recompute trigger: dirty-on-change vs every-tick (perf vs simplicity).
- How many/which sample abilities; whether to include a basic upgrade source (pickup/level) to exercise modifiers, or stub modifiers via the debug hook.
- UI/icon/description pipeline (managed lookup keyed by id).
- Tag/element taxonomy (kept minimal until needed).
Related
DR-003_M2_Combat_Netcode_Architecture (the stats this refactors) · Systems_Index · Pillars (server-authoritative + deterministic).