Player motor, vitals and stats contract¶
Status: This is a dated behavior baseline with later batch notes, not a current wiring map. Descriptions of AbilityHolder and legacy fallback paths may no longer apply. Use the roadmap and current source for implementation status.
R0 design deliverable, 2026-10-26, batch 28. Follow the rebuild plan, input/assignment contract and run/bootstrap lifecycle. This defines responsibility and behavior constraints for extraction; it does not implement the target components, optimize the game or prove runtime equivalence.
Preserve current one/two-player movement feel, authored values, arithmetic order, combat/ability triggers, damage/status rules and progression. Owner-approved extensions remain P1 keyboard-or-pad/P2–P4 pad-only, explicit assignment, four-player capacity and persistent button remapping. They do not authorize new balance formulas or changed ability effects. Tutorial and GAME-01 work stay parked. No runtime/scene/package/save changes or Unity run belong to this design batch.
Evidence and reference limits¶
The reference is committed HEAD 5347e460 plus existing uncommitted work, not a certified Steam source. Existing source pins and batch evidence remain intact. This contract adds:
- Authored player baseline: all seven characters in Bootloader's catalog, their IDs, vitals/movement/dash/stat defaults, referenced weapon component values and ability paths. These are raw authored values, not guaranteed initial live stats after shop/automatic abilities.
- Capability reference map: 88 category rows across 49 scripts, with line references and examples for motion/dash/targeting, vitals/damage/regen, status, stat mutation/reads and death/revive registration. This lexical scan includes enemy, Tutorial, view and test uses; names alone do not resolve receiver types or inherited calls.
- Serialized binding map: 46 Player/PlayerModifier/AbilityHolder/PlayerCollectItem/StatusPanel/AbilityPanel records (22 direct components and 24 prefab-inherited stripped references) in the player-prefab tree and scene files, stable file IDs and selected fields. Prefab inheritance/overrides still require Unity-import verification; an empty direct Rigidbody column is not proof that a scene player has no Rigidbody. A separate persistent-event name scan across 1,256 serialized scene/prefab/asset files found no calls named for the 24 reviewed capability entry points; this does not exclude other method names, inherited callbacks or runtime listeners.
- Equivalence cases: 28 source-derived numerical/ordering examples and required future observations. Arithmetic spot checks are document checks, not execution of production methods or player tests.
Focused review covers Player and PlayerModifier in full, Entity status/damage/lifecycle, dash and relevant AbilityHolder health/state hooks, AbilityFunction admission, the revive/PSkin upgrade branch, pickup/level-buff/lifesteal/weapon health writers, EnemyFunc attack/status routing and presentation/targeting consumers. The maps are an audit aid, not a complete compiled call graph or evidence every asset is released/reachable.
Existing initialization and update order¶
- GameInitialization activates pre-wired scene players, calls Player.ApplyCharacter and registers camera/HUD. ApplyCharacter reads GameManager.Character[playerNumber], instantiates a weapon under child 0, assigns feedback/animator and starts PlayerModifier.SetupPlayer. Starting that coroutine is not full readiness.
- Entity.Awake caches Rigidbody/Animator/renderer/collider, Camera.main and UIResourceProvider material. Entity.OnEnable recreates poison/burn dictionaries, resets status multipliers/timers and selected flags, enables the collider and restores material. Player.OnEnable then finds enemies and subscribes target events; OnDisable removes those subscriptions.
- PlayerModifier.SetupPlayer loads character/weapon bases, shop modifiers and selected transient defaults, starts with potion stock from session data and sets isModifierReadyToCalculate. It yields, calls Calculate, yields again, then fills HP/stamina/ammo, sets ReloadTimer and binds ammo UI. Its Update also calls Calculate once ready. Preserve/characterize first-frame ordering; do not assume characterInit means all of these operations finished.
- AbilityHolder.Start initializes effect/counter state and starts PlayerSettingsAbilityFirstTime, which waits for characterInit, adds the character ability, resets shared cooldown fields, configures special startup effects and loads dashSpeed. That Start path is not the same as OnEnable during revive.
- Within Player.Update the sequence is Movement → stamina regeneration → health regeneration → player effects → invulnerability timer → crowd-control duration → target-list update/sort. PlayerModifier and AbilityHolder have separate Updates. Relative ordering between separate components has not been established by this source audit.
- Movement writes Rigidbody2D.linearVelocity from Update, not FixedUpdate. The base Player prefab has mass 1, zero linear damping/gravity, angular damping 0.05, interpolation 0, collision mode 0 and constraints 4. Project fixed timestep is 0.02 seconds. Retain source settings/scene overrides as evidence; changing update cadence, damping or interpolation is not an automatic cleanup step.
Target readiness must explicitly await required player stats, weapon, ability state and registrations, as defined by the run lifecycle. Awake may cache local components; injected owner/content/runtime context must be valid before gameplay activation. First extraction must preserve observable scheduling, then any timing change requires its own equivalence evidence.
Owner safeguard, 2026-10-27: shared character cleanup must preserve each character's actual attack/stat consumers. The combat preservation cases document Wu Ran's swords, Tiamra's orbs and Zenova's movement-sensitive charge/missile paths. Wu Ran/Tiamra held weapons still supply stats and hooks while companion entities execute attacks. Apply this constraint to stat projection, readiness, movement observations and death/revive/reset before changing those shared capabilities; do not assume every character fires its held weapon directly.
Target authoritative owners¶
These are boundaries, not a demand for one MonoBehaviour per row. Combine tightly related state when useful, but keep one authority and explicit commands.
| Owner | State / responsibility | Inputs and outputs; excluded responsibilities |
|---|---|---|
| Player identity/assembly | Stable roster ID, character definition and injected input/capability references; prepare/register actor. | Receives validated request. Does not derive identity from array position or own HP/cooldowns. |
| Motor/dash execution | Current motion intent, body motion, facing/motion observation and dash execution phase/direction. | Consumes owned movement and authorized dash request with stats. Reports execution completion/observations. No package input polling, stamina mutation, ability selection or scene lookup. |
| Vitals | Current HP/stamina/potion stock, regen scheduling, resource spend/heal/set operations and cap observation flags. | Reads stat snapshot and explicit modifier inputs. Reports resource changes/death candidate. Distinguish damage, healing, ability costs and initialization; do not route every direct HP write through TakeDamage. |
| Damage resolution | Authoritative invulnerability timer/gate and ordered shield/mitigation/reaction/lethal processing for one attributed hit. | Reads defense/ability state through defined hooks; requests vitals changes and reactions in the existing order. No UI material, pool spawn or global stat-field writes as hidden responsibilities. |
| Status effects | Active poison/burn sources and duration/power, freeze contributions, stun remaining time and timing gates. | Accepts attributed status commands; schedules damage attempts through damage resolution and exposes modifiers. No duplicated player HP or new immunity policy. |
| Stat model/projection | Character/weapon bases, shop/run contributions, upgrade mutations, temporary/per-state inputs and derived outputs. | One authoritative calculation with documented units/order. Views/abilities query snapshots and issue typed mutations instead of arbitrary writes. Preserve time-varying health/stamina/status dependencies. |
| Ability/weapon execution | Admission, cooldowns, character/perk state, ability-specific shield counters/thresholds, dash hooks and weapon/attack behavior. | Authorizes resource use and motor action through capabilities; consumes results/events. The complete effect/attack design follows in the combat contract. |
| Actor life/run registry | Alive/dead/reviving state, one death/activation transition, registrations and run reset/disposal. | Vitals proposes lethal resolution; life policy applies existing Last Stand/revive order. Death/revive does not change roster/device identity or reset acquired upgrades. |
| Target selection | Registered enemy hit points, effective distances, priority and fallback queries. | Supplies existing target/facing results. Motor does not sort enemies or decide weapon target priority. Full targeting/attack migration belongs to R4. |
| Presentation/feedback | Animator/flip/materials, particles/trails, health/stamina/stat/ammo views and sounds. | Consumes committed state/ordered feedback requests. Must not own immunity, HP, stat refresh or active-player registration. Preserve visible/audio timing through integration checks. |
Keep Player as a temporary actor facade while callers migrate, with each remaining delegated member and its removal condition recorded. This is not permission to preserve duplicate authoritative fields behind new wrappers. Shared Entity functionality should be extracted/adapted with Enemy callers, not changed globally while checking only Player. Retain the shape of IDamageable/ICrowdControl until the combat/status contract accounts for all implementers and callers.
Motor and dash semantics¶
Movement first returns with zero velocity when stunned. Otherwise it samples input, updates LastHorizontalInput only for nonzero horizontal input, and, when not dashing, normalizes direction before multiplying by finalMovementSpeed. Speed remains the raw input-vector magnitude, animator IsMoving uses Speed > 0.1, and dashForce uses normalized direction times dashSpeed. Facing uses the selected weapon target's X position, not movement direction. A final Speed == 0 check zeros velocity even outside that non-dashing block. The NoPassing overlap callback pushes transform position by direction × 0.1 per callback; do not quietly multiply it by deltaTime or replace its collision rule.
A nonzero partial post-filter vector can therefore produce full movement speed. Preserve normalization separately from input sensitivity/dead zones, raw Speed and animation thresholds. Do not add proportional walking, manual aim, acceleration, new diagonal bonuses, rotation behavior or a different target fallback. Preserve held-movement resume semantics from the input contract; clock/input gating must not leave stale dash edges or require a new movement gesture after pause.
Batch 51 adds original actual-axis/motor observations for the next extraction. Its keyboard filter replays all 2,967 retained state boundaries exactly, including scaled pause, opposed horizontal holds and reversal. The reference also retains production velocity, raw observed Speed, last horizontal input, dash intent and explicit stun/dash state. These include sub-.1 full-speed movement, stunned stale Speed and a dashing actor retaining externally supplied velocity after input release. Player itself is still unchanged; the fixture's supplied dash state/velocity is not executed dash-admission, collision, animation or full motor acceptance. Use these observations to compare the shared motor before activation. Legacy axes continue updating during stun even though Movement returns before observing them; retain that distinction. Input timeline continuity across absent actors/first activation remains a separate gate, so actor retry must not silently reset a session-owned sampling history. Keep the remaining capability gates open.
Batch 52 now implements the motion calculation in PlayerMotor. Player's public Speed/LastHorizontalInput forward to its one owned motor, and both Movement (legacy input) and ApplyMovementInput (assigned session) call that calculation. Input polling, facing/target selection and Unity presentation remain facade/driver adapters. Stun avoids sampling/observing motor input while the session's clocked keyboard history still advances; walking and dashing preserve the original velocity-write decisions. The pure motor and actual facade compare exactly against every original stored state boundary. Existing Update order and the cursor/target/damage/resource/status suffix remain byte-preserved; physics settings and cadence are unchanged.
As of batch 52, an explicitly enabled PlayerMovementDriver runs on the prepared production prefab; other legacy behaviours remain quarantined, with root physics enabled. The character definition supplies the old dash-speed primitive for dash intent without executing dash ability logic. Real scene checks cover body motion, animation/empty-target facing, context/device loss, pre-activation input, pause/retry/reload and two independent keyboard/pad actors. Native production Soldier/Knight also move after a normal reward pause/resume. Disabled legacy MonoBehaviours may still receive Unity physics callbacks; collision/collectible ownership is not certified by the quarantine. The later batch 53 below addresses NoPassing and ScreenBounds/camera ownership with focused checks; full targeting, vitals/damage/status/death/revive, actual dash admission/resources/hooks and complete F2 remain open. They must consume these owners rather than add a second motor or enable the old manager chain.
Batch 53 extracts unchanged viewport clamping and NoPassing separation into MotionConstraints. ScreenBounds retains its script identity, Update timing and Start-cached renderer extents. The prepared actor binds the run's camera before activation; unbound production/drone/Tutorial consumers retain Camera.main lookup. Unity sends trigger stays even to disabled Player behaviours: the movement driver claims callback ownership while inactive, and the legacy callback stays silent after that claim, including driver disable/destruction. The driver forwards only while active and not cancelled. Unclaimed production actors retain their legacy path. Ten boundary and ten motor groups pass headlessly against original captures; native/full collision integration remains pending. No authored prefab, scene, physics setting or movement rule changed.
Dash input is admitted only when not already dashing/combined-paused and dashForce is nonzero, through AbilityFunction.UseAbility. AbilityFunction starts cooldown handling and the requested coroutine. Dash then writes the selected character's stamina cost onto its Ability, checks pause, subtracts 5 for SurvivalFitness, and compares current stamina to that cost. On failure it clears ability.isOnCooldown. On success it executes the existing ordered dash perks (Aid Roll unless SuddenDeath, Malice health/stat effects, TacticalFire/RunandGun/FocusDodge/BombRoll/Vacuum/Airstrike/TidalVeil hooks and the Soldier reload contribution), spends stamina, requests an impulse, marks isDashing, triggers animation/audio/facing, requests 0.65-second invulnerability, records DodgeRoll, hides weapons and waits the authored scaled dash duration before clearing the phase.
Do not move dash into the motor by discarding those admission/hook/cost semantics. Separate authorization/resource transaction from body execution, retaining the order and failure/cancellation outcomes. Dash speed is an impulse magnitude after prepared direction, not proof of constant travel speed or distance; existing velocity, physics cadence and interruptions need real measurements. Death/reset must cancel owned work without a late completion mutating a revived/retried actor.
Vitals and ordered damage resolution¶
Hit processing order¶
| Phase | Current source behavior to characterize/preserve |
|---|---|
| Incoming wrapper | EnemyFunc.DoDamageToPlayer returns for null/dead/dashing player, otherwise calls TakeDamage and then applies configured crowd control. A shield/invulnerability early return inside TakeDamage does not automatically suppress that wrapper's status application. |
| Pre-gate reflection | ProtoBarrier ability reflection uses raw hit × missingHPmultiplier × (2 + 0.04 × run level), outside the IsInvulnerable check. It currently passes the source as reflected source; do not correct attribution incidentally. |
| Immunity/shields | Inside !IsInvulnerable, early-return order is active ProtoBarrier threshold, emergency shield blocks, Stellar shield blocks, TidalVeil count/deactivation and reload shield absorption. Preserve break thresholds, counter resets, explosion/invulnerability requests and return points. Shield visuals currently act as state; target execution needs authoritative defense state with the same outcomes. |
| Mitigation/resource conversion | Floor finalDamageTakenModif to 20, then dmg = raw × modifier / 100. UniqueCons requires stamina > dmg × 2, spends that amount, then subtracts raw damage / 2 from dmg. EmergencyShield can replace a lethal dmg with currentHP − maxHP/5. Negative/zero values and strict inequalities are source facts requiring disposition, not a silently clamped new formula. |
| HP application | Subtract adjusted dmg unless isLastStand. HP changes from a hit are distinct from direct costs/heals/upgrade changes elsewhere. Existing feedback starts at its current point; do not reorder random/hook execution by introducing async presentation. |
| Reactions | Current order is ThornArmor/ETArmor reflection → SurvivalFitness stamina → AdrenalineRush → Rebuke → BloodThirst → FinalForm → FirstAid → FleeBoots random roll → PanicFrenzy → ETArmor effect/Judgement random roll → JetPropulsion reset/activation → Enrage counter → low-health EmergencyShield. Preserve thresholds, random-call order and which effects can run after a nominally prevented HP loss. |
| Lethal handling | If HP <= 0 and LastStand is present/unexpired, mark expired, restore maxHP, invoke LastStand and return before the normal invulnerability tail. Otherwise set HP0 and Death. LastStand's own coroutine drains 3% maxHP per scaled second and finishes via TakeDamage. It is not an ordinary revive. |
| Immunity tail | Raw incoming hit >= 35% maxHP requests 1 second; otherwise 0.1 seconds. Invulnerable(time) only increases the remaining timer. IsInvulnerable updates in Player.Update, so requesting a duration is not an immediate gate change. Compare multiple callbacks within the same frame. |
Defense-state details stay owned by the corresponding ability instance and are consumed through ordered damage hooks. ProtoBarrier absorbs while its accumulated threshold is <= capacity, adds the raw hit, and on reaching capacity resets accumulation, deactivates and requests explosion damage max(10, temporaryAmp × currentHP / 100) plus one-second immunity. Emergency shield decrements blocks, breaks with finalDamage × 2, restores its block counter by adding5 and requests1.5 seconds. Stellar shield breaks at <=0 with 50 × (1 + temporaryAmp/100) and1.5 seconds. TidalVeil decrements its count and deactivates only when count <=0 and ready, but returns from damage handling regardless. Reload shield subtracts the raw hit, clamps its remaining capacity to0 on break and requests1.5 seconds. These early-return shields do not spill over excess hit damage to HP in the current code. A reusable shield abstraction must preserve those policies rather than supplying a new default overflow rule.
Health/stamina mutation routes include PlayerModifier setup/AddHP/AddSTM/ModifHP/ModifSTM; PlayerCollectItem potion/stamina/random pickup effects; PlayerManager level-up buffs; PlayerAttack lifesteal; PlayerWeapon SoulWager health changes; AbilityHolder dash costs, LightBlessing/AidRoll/LastStand and other health effects; and UpgradeFunction PSkin/Revive. The new boundary must retain each reason/source and current timing. A generic setter that treats all decreases as damage or immediately clamps every heal would change reactions, resource costs and next-update caps.
Regeneration and cap behavior¶
UsingStamina subtracts the amount and sets regenTimer=2. RegenerateStamina decrements a positive timer; only a subsequent invocation with nonpositive timer grants finalRegenStaminaAmount if below cap, then sets timer=0.2. It does not integrate a continuous per-second rate or catch up all missed ticks. Its cap flag uses strict currentStamina > finalMaxStamina: clamp/true on that invocation, otherwise false even at exact equality. Preserve/characterize that flag before changing it to >=, since item consumers use it.
RegenerateHealth first caps potion stock and sets PotionIsAtMax using >=. It clamps HP at/above maximum and returns. Otherwise it rejects an active regen, SuddenDeath or LastStand. It consumes one potion before the wait and adds 10 to regenPower, then adds FailedVitamin2, TrueVitamin5, PSkin/DePetrify2, SurvivalInstinct.surviveRegen and Meditate value × maxHP / 100 when present. HealthRegen sets the lock, waits 0.1 scaled seconds, adds regenPower/10 and releases the lock. No-potion/perk cases can still start a zero-power cycle. A cancelled coroutine may not reach its release; reset/revive must own that state explicitly without changing an in-flight heal's normal timing.
Invulnerability countdown and status ticks use scaled deltaTime; selection/menu presentation uses independent time as already recorded. No new healing, stamina or invulnerability tick rate is approved by this cleanup.
Stats: units, formulas and invalidation¶
Owner's two-part stat design · 2026-10-28¶
The owner wants two clearly inspectable responsibilities. Player stats hold the original/base inputs and published final values for each player: for example base damage and final damage, together with the current character/weapon/shop/upgrade inputs needed to calculate them. The central modifier calculation explicitly lists and calculates what affects each stat, including named abilities, items, status effects and other sources. It must be easy to open that calculation and see all known factors affecting critical chance, attack speed or another value. These are proposed responsibilities, not a claim that the current code is finished or a mandate for exactly two physical files.
Ability modules own their activation rules and private state; the central stat calculation reads the relevant owned state and applies the explicitly named formula. For example, a CCombo module owns whether CCombo is active. The stat calculation visibly states how active CCombo affects attack and reload speed. It does not duplicate CCombo's active flag as an independent authority or require the reader to search every ability script to discover stat contributions. Other ability effects, such as spawning or damage interception, still belong to their appropriate modules/capabilities. Item and permanent progression changes need explicit inputs and reasoned mutations; do not lose their source or apply them twice.
In the current PlayerStatProjection, active CCombo supplies a factor of 3 to attack speed and 4 to reload speed through inverse interval formulas. FocusDodge currently supplies +35 critical chance, +200 critical damage and a 2× projectile-speed factor. The owner's CCombo/critical example expresses the need for visibility; this source check does not establish a CCombo critical-stat bonus. finalAttackSpeed and finalReloadSpeed are intervals, so a faster attack can mean a smaller numeric interval. The owner mentioned elemental points as an illustration; it is not a game feature or implementation request.
PlayerModifier already captures base/shop/bonus values, live health/stamina/potions and weapon values, then assembles ability flags and publishes PlayerStatProjection results into compatibility fields. PlayerStatSnapshot contains input/effect/result structs. This is a partial extraction: callers still mutate fields and central rules still depend on AbilityHolder flags. It is not the completed two-part design.
The target calculation must keep all sources visible by stat, preserve existing arithmetic/order, units, integer casts, clamps, previous-maximum reads and live-versus-captured values. The player/ability/weapon owns its actual mutable state; the calculation reads a coherent context for that player. Preserve special cases where upgrades alter current HP/stamina or barrier state as well as maxima. Recalculation frequency remains compatible until all invalidation sources are traced; do not claim event-driven caching or performance improvement without evidence. Inspect Wu Ran/Tiamra weapon-data consumers and Zenova's movement/charge before changing stat access.
Plan named source rules in a central, human-readable stat calculation rather than distributing arithmetic across ability scripts. If that calculation later becomes too large, group it by stat while retaining a single navigable source-of-effects index. Decide concrete class/file names during the architecture plan. Adding a new stat means adding its base/final representation and its sources explicitly; no example stat is introduced now. The migration must preserve old public/serialized field and upgrade/evolution callers through temporary bridges until every consumer is migrated.
Source authority is PlayerModifier.SetupPlayer/Calculate and its mutation methods. Character fields, default weapon data and GameSession.modifiers remain the authored/run inputs. The baseline CSV records all seven catalog characters; for example neutral Soldier data has HP100, stamina100, movement2.8, dash cost20/speed5/duration0.5, weapon damage10, attack rate1.5, reload rate0.4 and ammo10. A neutral synthetic calculation yields attack interval 1/1.5 and reload interval 1/0.4; live automatic Frenzy/shop state can differ.
Do not standardize units from names alone: finalAttackSpeed and finalReloadSpeed are intervals; base/bonus rates and several percent-style inputs are different units. Preserve float operation order/casts until comparison evidence supports a change. In the table, booleans select the stated active value or neutral 1/0. References to prior derived values are intentionally noted.
| Output | Current calculation |
|---|---|
| missingHPmultiplier | 1 + (1 − currentHP / previous finalMaxHealth), computed before the new maximum; startup zero/previous-frame dependencies require characterization. |
| finalMaxHealth / finalMaxStamina | (base + shop + bonus) / 2 when DemonPact / ADPact is present respectively; otherwise divisor1. |
| finalRegenStaminaAmount | base increment + bonus increment, applied per regen invocation rather than per second. |
| finalMovementSpeed | (movementSpeed + shopMovementSpeed + (bonusMovementSpeed + fleeBootsBonus)/100) × runandGunMS × Sniper0.9 × SwiftPot1.25 × SurvivalInstinct.surviveMS × FinalForm1.25 × (1+jetPropulsionBonus) × Enrage1.5 × AdrenalineRush1.2 × StaminaBoost × bloodThirstMS, divided by FreezeSlowModifier. StaminaBoost = 1 + currentStamina / base maxStamina / 2. |
| finalWeaponAmmo | int(baseAmmo + shopWeaponAmmo + bonusWeaponAmmo). AddAmmo itself truncates its argument before accumulating the integer bonus. |
| finalReloadSpeed | 1/(baseReloadSpeed + shopReloadSpeed + bonusReloadSpeed) × QuickHand0.75 / Frenzy4 / CCombo4. A neutral state uses active divisors1. |
| finalDamage | (baseDamage + shopDamage + bonusDamage + PManiac.potionAmount + PotOverlord.previous finalMaxPotion) × tacticalFireMultiplier × bBloodMultiplier × Assassination15 × Sniper2.5 × RagePot1.25 × BerserkPot1.5 × FinalForm1.25 × Enrage2 × (1+SWCount×5/100) × LongSlash2 × DemonPact2 × WarCry1.2. Inactive additive perk terms are0. |
| finalCritDamage | criticalDamage + shopCritDamage + bonusCritDamage + PotOverlord term + LateBloomer(sureFireCCHCount×10) + FocusDodge200 + Rebuke200. |
| totalBaseCriticalChance / finalCritChance | base+shop+bonus; final adds FocusDodge35 + sureFireCCHCount + Enrage25. |
| attackSpeedNonBuff | 1 / ((first runtime weapon.attackSpeed + shopAttackSpeed + bonusAttackSpeed + Strafe(finalMovementSpeed/2)) × Knight0.5 × Miner0.5 × ADPact2 × Sniper0.75 × QuickHand1.25). Knight/Miner factors depend on ProtoBarrier/GroundBreaker ability presence. |
| finalAttackSpeed | attackSpeedNonBuff / (HastePot1.25 × BerserkPot1.5 × runandGunMultiplier × Frenzy(2+SoldierFrenzyASBonus) × bBloodMultiplier × PanicFrenzy2 × CCombo3 × FinalForm1.25 × AdrenalineRush1.2). |
| Skill amplification | totalBonusSkillAmp = bonusSkillAmp + shopSkillAmp; temporaryAmp adds MasteryPot50 + tacticalFireSA + stellarShieldBlock×20; finalBonusSkillAmp = finalDamage × temporaryAmp / 100. |
| finalMaxPotion / finalWeaponLevel | bonusMaxPotion + int(shopMaxPotion), calculated after damage/crit; weaponLevel + shopWeaponLevel. |
| finalProjectileSpeed / finalAttackRange | (first runtime weapon.projectileSpeed + bonusProjectileSpeed) × Sharpshooter2 × FocusDodge2; attackRange + bonusFindRange. |
| finalVampiricPower / finalVampiricChance | respective base + bonus + BloodThirst contribution. |
| finalDamageTakenModif | damageTaken − SWCount×5 − GuardPot20 − Enrage50 − Meditate(value×5) − WarCry20 − poisonedTerm; poisonedTerm is −10, so poison increases the result by10. Player.TakeDamage can later floor/mutate this derived field. |
Initialize neutral multipliers explicitly; do not depend on serialized zero values becoming valid by chance. Snapshot inputs must include live HP/stamina, potion count/cap, status and temporary ability state, not only acquired upgrades. Calculate currently runs every Update once ready. Moving to event-driven refresh is a separate optimization requiring a complete invalidation source for all those writers, transition timing and first-frame behavior. No performance gain is claimed by this contract.
Owned TacticalFire and RunAndGun now expose active state through their runtime modules. PlayerStatProjection applies their named damage/skill-amplification and movement/attack-interval factors centrally; PlayerModifier selects module state where present and retains the old input fields as the production compatibility route. This source change is uncompiled and untested. The existing duration, cancellation/overlap, death/revive and two-player comparisons remain open; see the hook contract.
Owned Berserker Blood stores the last valid fire-time health percentage; the central projection derives its named damage and attack-interval factor from that sample. The legacy path and an owned module before its first valid sample retain bBloodMultiplier input. Exactly 20% and unordered comparisons retain the prior sample, matching the source branch behavior. This migration is uncompiled and untested; see the weapon-fire contract.
Owned Sure Fire now retains its fire/critical counter in its module. The compatibility property in PlayerModifier supplies the existing central critical chance and Late Bloomer formulas, and keeps the old serialized value via FormerlySerializedAs. The weapon's fire and per-critical ordering is documented in the ability contract. Serialization and gameplay remain unverified.
Owned Blood Thirst exposes its active state and last eligible fire sample to the central projection. Its vampiric power, chance and movement calculations remain named in PlayerStatProjection, with legacy inputs before a fire sample or without an owned module. The distinct hit, fire and per-attack stages are documented in the ability contract. This is uncompiled and untested.
Owned Adrenaline Rush uses a named PlayerStatProjection.ApplyAdrenalineRegen mutation for its per-work +4/−4 stamina-regeneration contribution, preserving the existing ordering against other direct bonus writers. Its active-state movement and attack factors stay in the central projection. Its damage reaction and cancellation policy are uncompiled and untested.
Owned Flee Boots samples projected movement on activation and exposes its stored integer bonus to the central movement calculation. The named threshold/conversion is in PlayerStatProjection.FleeBootsBonus; its reaction and cleanup policy is uncompiled and untested.
Owned Jet Propulsion stores its crash/acceleration bonus in its module, while the central movement projection retains the 1 + bonus factor. The damage reaction and suspension contract records its separate operations and remaining comparisons; it is uncompiled and untested.
Existing mutation families¶
| API family | Semantics / side effects to retain |
|---|---|
| AddDMG/ASP/CDMG/CCH/RS/MS/SAMP/BonusSTMRegen/ProjectileSpeed/FindRange/VampiricChance/Power/WeaponLevel | Add to their specific bonus/rate/percent/count field; they are not interchangeable stat units. |
| AddAmmo | Add int(argument) to bonusWeaponAmmo. |
| AddHP / AddSTM / AddDMGTaken | Add max-HP bonus and optionally current HP; add max-stamina bonus and current stamina; subtract the argument from base damageTaken respectively. |
| ModifDMG/CDMG/CCH | Replace the bonus with (current final × argument) − (base + shop). Do not also multiply the base or compound every source independently. |
| ModifASP | Add (baseAttackSpeed + shopAttackSpeed + bonusAttackSpeed) × argument to bonusAttackSpeed; Calculate reads the runtime weapon.attackSpeed as its rate base. |
| ModifRS | Derive a new interval from the first weapon.reloadTime + bonuses, reduce it by its fraction argument, then invert back to a bonus rate. Preserve zero/denominator edge cases for disposition. |
| ModifMS / ModifAMMO | bonusMovementSpeed += finalMovementSpeed × argument (later divided by100); ammo bonus = int(finalAmmo×argument) − (int(baseAmmo)+int(shopAmmo)). |
| ModifHP / ModifSTM | Replace max bonus from current final, active pact divisor and argument, then multiply current HP/stamina by the argument. HP also sets ProtoBarrier accumulated threshold to (10 + old finalMaxHealth × (20 + thresholdBonus)/100) × its existing accumulated/capacity ratio (or0 when accumulation is0); retain that evaluation order. This is not a generic fill-to-new-maximum operation. |
| ModifSAMP | Add (totalBonusSkillAmp × argument) − totalBonusSkillAmp to bonusSkillAmp. |
The new stat owner must expose reasoned mutations/capacity changes with these effects, while vitals owns current resources and defense execution owns shields. Use a coordinated transaction/hook where one upgrade changes several owners; do not leave free writes into a public stats bag. Exact command names belong to implementation design after the combat/reward contracts reconcile them.
Status, death, revive and reset¶
Entity owns common status behavior used by enemies as well as players. Poison/burn entries are keyed by source.ToString(); repeated same-key application replaces duration/power. Poison ticks each accumulated second and decrements/removes before its damage attempt; burn ticks each 0.5 seconds and checks removal before decrementing, while still attempting damage for that processed entry. Their loops use changing dictionary Count with a snapshot key list, so simultaneous expiration can affect which entries are processed. Preserve source evidence; do not silently replace duration/stacking/iteration with a nicer generic DOT model.
Freeze multiplies a slowdown factor for a scaled wait, divides it out afterward and clears the flag later when the multiplier equals1. Stun replaces remaining duration only with a longer request and clears on countdown expiry. PoisonSlowModifier becomes0.75 while poison entries exist; trace its actual consumers before promising every attack interval changes. Re-enable recreates status dictionaries and resets several flags/multipliers, but it is not a complete reset of every Player/AbilityHolder timer or coroutine lock.
Player.Death marks IsDeath, clears targets, zeros observed Speed, disables the actor and calls GameManager.CheckGame. The current end check explicitly inspects slots0/1. The target run registry must use joined participants and one owned death transition without inventing a new game-over rule; added-player decisions follow the roster contract. The base Entity death/pool behavior is different and must not accidentally replace Player death during extraction.
UpgradeFunction.REVIVE loops every inactive array slot, activates it, restores half maximum HP/stamina, re-registers camera, clears listed reload/regen/dash/cooldown/miner-CD flags and reconnects a spawned drone. It does not rerun full character setup or reset acquired upgrades. New-run initialize, death/revive and despawn/dispose are distinct operations. Do not make revive rebuild a new player or refill all resources. Unjoined inactive slots and residual invulnerability/regen/ability timers need explicit reachability/runtime checks before migration; activating an unused slot is not an accepted new ownership policy.
Reset/cleanup must account for target/collectible subscriptions, body velocity, live weapon/companion/effect objects, invulnerability/status/regen timers and locks, temporary ability contributions, registrations and source attribution. New run resets run-owned bonuses; revive retains acquired progression and applies its defined partial reset. Input sessions and saved remaps survive actor death/retry; normal currency/unlocks/config are never reset to make fixtures pass.
Targeting, presentation and serialized migration¶
Target registration currently finds tagged Enemy objects on enable and listens to GameEvents.AddTarget/RemoveTarget. Each enemy hit point contributes distance minus hitPointSize; every update recomputes effective distances, puts invulnerable enemies at1000 and sorts. Priority is clamped; if the chosen target is invulnerable, queries walk toward lower indices, then return a camera-side fallback or null. This is not equivalent to selecting any valid target anywhere in the list. Preserve target ownership/priority, fallback based on LastHorizontalInput and weapon range/facing while moving it out of the motor; profiling/caching policy belongs to later measured work.
PlayerEffect drives footsteps from raw Speed, stops audio under combined pause and creates movement trails above finalMovementSpeed5 with moveTrialDelay. Blink/material/particle and HUD tween timing are feedback responsibilities, not immunity/HP authorities. StatusPanel and GameEnd/leaderboard readers need unit-correct stat snapshots; do not rewrite combat math to match a differently formatted display. The documented Soldier Frenzy reload-display disagreement remains a separate check, not grounds to change the formula here.
The base Player prefab directly wires Player↔PlayerModifier↔AbilityHolder/collector, effects and materials, with default playerNumber1 and Range(0,1); scene players override identity and have separate HUD references. Original weapon parenting depended on child0 and an unused CenterPoint name lookup. Batch 49 explicitly binds that original weapon mount and removes the unused lookup/property; the camera center calculation is separate and unchanged. Required target assembly must expose its actual mount/body/capability references, inject roster identity and validate them before activation. Preserve prefab/meta identity and scene overrides where possible; imported types/references and all four arena scenes (including Experimental's release status) still need later validation. Do not treat zero runtime-populated weapon/UI fields or inherited Rigidbody records as automatically broken content.
| Source/caller area | Migration/disposition |
|---|---|
| Player/Entity | Extract motor, vitals, ordered damage/status and lifecycle seams; retain a tracked compatibility facade until callers migrate. Shared enemy base consumers require matching checks. |
| PlayerModifier | Retain formulas/units and reference cases; move authoritative base/shop/upgrade/temporary inputs and derived snapshots to one owner. Delay caching until invalidation is complete and measured. |
| AbilityHolder/AbilityFunction | Move dash body execution to motor and current resources to vitals while retaining perk/cooldown admission and ordered hooks. Per-player cooldown/state separation and complete ability execution belong to the next contract. |
| PlayerWeapon/PlayerAttack/carriers/companions | Query targeting/stats/capabilities; route health costs/heals/lifesteal explicitly. Preserve random/cast/attribution and effect ordering; full attacks/effect hooks remain R4. |
| UpgradeFunction/PlayerManager/PlayerCollectItem | Replace direct current-stat/HP/stamina/revive writes with coordinated owner operations; keep numerical/perk outcomes. Reward choice, inactive-slot eligibility and pickup arbitration remain in later contracts. |
| Enemy/EnemyFunc/attacks/states | Retain wrapper admission and CC-after-hit behavior; route into shared damage/status contracts. Derived attacks may inherit a mapped call and therefore need hierarchy review even without a lexical row. |
| StatusPanel/AbilityPanel/GameManager/GameEnd/stat exports | Consume snapshots/events and explicit registration. Retain scene/ID wiring and observed presentation; do not become another stats or alive-player authority. |
| Camera/GameEvents/collectibles | Register/unregister by owned actor lifetime exactly once; preserve revive/retry targets and ownership. No callbacks from a disposed run. |
| Tutorial/debug/tests | Keep parked Tutorial's separate actor code as a consumer to migrate later, not the target motor. Adapt meaningful fixtures and deliberate debug setters to explicit owners; no gameplay changes just to make a fixture green. |
Required equivalence evidence¶
The 28 CSV cases specify proposed future fixtures and numerical/ordering expectations. They are source-derived examples, not a passing test suite. Each implementation slice must exercise the real old/new production method paths under the same configured inputs; avoid writing a mirror implementation and calling it proof.
- Record finite settled initial stats for all seven characters under controlled shop/ability state, then test additive/multiplicative/fractional/truncating mutations and temporary combinations. Cover pact divisors, health/stamina-dependent movement, freeze and weapon intervals. Distinguish startup order-dependent values from settled snapshots.
- Sample keyboard/stick cardinal/diagonal/partial inputs, release/reversal, dash from idle/motion, stun during dash, boss overlap and pause/held movement at stated render/fixed cadences. Compare requested impulses plus actual position/velocity curves, direction/facing, animation/audio and admission timing; no invented universal tolerance or distance formula.
- Exercise each shield's early-return/break path, raw/mitigated damage, status application while blocked, direct HP costs/heals, same-frame repeated hits, low-health reactions, Last Stand and lethal/normal invulnerability. Preserve random state and hook/attribution order.
- Advance real regen/status/invulnerability coroutines across normal/paused time, disable/death/revive and interrupted reset. Verify potion/stamina consumption before/after waits, delayed caps, flags and status-source expiration with multiple sources.
- Retry/revive repeatedly with two players and the planned up-to-four-slot registry: retained input/remaps, no duplicate weapons/abilities/registrations, joined-player-only participation and unchanged one/two-player behavior. Do not invent three/four-player rewards/payouts.
- Validate imported prefab/scene components and IDs before real Linux IL2CPP player checks. Physical device/Windows checks remain required later. Source hashes, arithmetic examples and a compiling extraction are insufficient for movement-feel equivalence.
Open behavior dispositions and batch boundary¶
| ID | Unresolved fact / required follow-up |
|---|---|
| PC-01 | First/previous-frame finalMaxHealth/finalMaxPotion reads and separate component Update order can change projections. Characterize initialization and transitions before a pure-snapshot/event-driven rewrite. |
| PC-02 | UniqueCons can make adjusted damage negative at high reduction; immunity is timer-requested rather than immediately gated. Reproduce controlled cases and seek a deliberate behavior disposition before clamping/reordering. |
| PC-03 | Exact-max stamina flag, zero-power healing cycles, status-loop expiration and freeze restoration are source quirks/risks. Establish reachability and intended correction separately from extraction. |
| PC-04 | Revive traverses inactive slots, clears selected flags but not every timer, and re-enable differs from Start. Audit joined-slot eligibility, retained effects and repeated revive; do not assert runtime failure from static inspection alone. |
| PC-05 | Perk/shield HP/status/target ownership still crosses AbilityHolder/weapon/upgrade paths. Reconcile ordered effect hooks and per-player state against the combat contract before the R3/R4 implementation gate. |
| PC-06 | Three/four-player behavior needs the active roster and later reward/result/backend specifications. This contract preserves the one/two-player reference and specifies capability isolation, not new balance. |
These are local design follow-ups under ARCH-01 and existing GAME-08/09/10, CONTENT-05 and performance evidence, not duplicate confirmed game defects. The combat contract now records ordered damage/resource/stat hooks, followed by the stage/reward/persistence contracts. Batch 34 retains PC-01–PC-06 in the phase gate register and admits the isolated R1a lifetime slice next. Player capability characterization and migration remain required before R3/R4 ownership changes; no capability rebuild is implemented by these documents.
Assigned dash composition · batch 58¶
Batch 58, implementation 56f764c9, connects DashPressed from the movement driver's single command frame to PlayerDashController and the actor's ability owner. Movement applies first, then existing stamina/immunity clocks tick, then dash admission consumes that same frame. This explicitly orders the new driver; relative scheduling between the old Player/AbilityHolder Updates was never certified. Full movement-feel/input timing qualification remains open.
PlayerDashHooks contains the existing ordered rules with injected character, pause, effect dispatch, feedback and weapon-hide work. LegacyPlayerDashHooks supplies the old global adapters; the development actor supplies its selected Character and private ability scope. Zero direction, active dash, pause, cooldown and insufficient stamina retain their gates and costs. Character dash speed/duration, impulse, SurvivalFitness, ordered Malice changes and Soldier reload contribution are preserved. Concrete perk bodies still use their legacy dependencies and require their own migration; this does not qualify upgraded dash combinations in the shared scene.
The prepared actor selects scoped executor completion. Iterator cleanup releases only its own dash generation, so an older stopped dash cannot clear a later one. Owned weapon-hide cleanup restores the corresponding weapon. Actor disable retires owned work and clears dash/motion; revive policy retains its existing cooldown/flag reset authority. Device loss neutralizes future input and preserves the actor; it does not invent cancellation or pause of already admitted timed effects. The development launcher's audio and transient dash count are explicit preview feedback and do not publish progression data.
Fifteen focused groups pass headlessly, including actual assigned input for all seven characters, two-player keyboard/gamepad isolation, held-button suppression, costs/impulses, stamina recovery/immunity expiry, pause/context gates and cleanup. All 70 original dash and 14 shared status cases still match; the prior ability ownership/clock/scene/launcher checks pass. Full health recovery, status/damage services, stat mutation callers, joined death/revive/run registration, targeting and attacks remain to compose. Player/AbilityHolder legacy Update stays disabled in the shared scene. No native playthrough, physical controller or Windows acceptance is closed.
Ability ownership prerequisite · batch 57¶
Later ownership prerequisite: batch 57 gives opted-in shared-scene actors private ability state and owned coroutine work. See the combat ownership contract for authority, duplicate-acquisition compatibility and cancellation limits. Supply explicit character/pause/effect-service dependencies before connecting assigned dash commands or active simulation; default Player/AbilityHolder simulation remains disabled in that scene. This does not finish R3 or character combat.
Current R3 extraction · batch 54¶
Batch 54, implementation a86b4742, moves calculation into PlayerStatProjection, using primitive PlayerStatInputs/PlayerStatEffects and a returned PlayerStatSnapshot. PlayerModifier captures live character/weapon/shop/upgrade/resource/status/effect inputs and publishes the outputs back through its existing fields. Previous finalMaxHealth/finalMaxPotion are explicit inputs; operation order, units, truncation and first-frame behavior remain. Setup/upgrade mutations and Update scheduling are unchanged. No event-driven caching or measured performance claim. Existing direct input/output field writers are still migration work; this does not claim a complete stat-command owner.
PlayerResourceCycle owns stamina delay and immunity timer/gate, plus spending, cap and health-recovery transactions through IPlayerResourceState. Player implements that state interface over its existing HP/stamina/potion/regen/cap fields; there is no second copied resource balance or serialized field migration. Player retains perk eligibility/power, feedback and the existing scaled 0.1-second coroutine. Damage, status, direct ability/upgrade writes, death/revive cancellation and shared-scene activation remain to migrate. The shared actor still does not run active vitals/combat merely because these methods exist.
The pre-change PlayerCapabilityReference retains 217 cases (31 variants × seven characters), each with three projections and 24 outputs: base/individual ability flags, mixed temporary/stat inputs, live weapon-rate changes and previous-maximum cases. New outputs compare exactly as round-trip float strings; 14 earlier character setup cases retain their original 92-field/yield observations. Six focused resource groups cover delay boundaries/pause, strict stamina cap, fractional/zero-power recovery, deferred health caps, immunity request/expiry, player isolation and live stat projection. These are headless checks, not sword/orb/charge execution or end-to-end character qualification. Full lifecycle/combat/physical-device/Windows acceptance remains open under the grouped workflow. No original baseline is repinned.
Current R3 damage/life boundaries · batch 55¶
Batch 55, implementation 50a33027, routes Player.TakeDamage through PlayerDamageResolver: reflect before reading live immunity → ordered shield admission → HP/mitigation work → reactions → lethal/LastStand handling → immunity request. PlayerDamageHit retains original source/attack/type/knockback arguments; the legacy effect path still ignores the previously unused metadata. Nested reflection remains synchronous with independent hit values. Resolver disposition does not change EnemyFunc's crowd-control delivery after a blocked hit.
PlayerDamageHooks (formerly LegacyPlayerDamageHooks) retains the current character/perk implementations, field mutations, random calls and service accesses in named stages. Player owns its damage presentation entry. This adapter isolates the remaining migration work; it is not a new authoritative ability/shield/service implementation. Do not change reflection attribution, negative UniqueCons outcomes, shield overflow, LastStand or within-stage hook order incidentally. Actual method comparisons cover 28 original damage/status cases, including shield breaks, raw-hit immunity thresholds, conversion boundaries, reflection, same-frame repeated hits, FinalForm/LastStand/death and wrapper poison/stun cases. Eight resolver checks cover stage order, live gates, argument preservation, re-entry, exceptions and isolation.
PlayerLifeCycle sequences death state/targets/observed motion → deactivate → run callback. Player.Death supplies the existing GameManager.CheckGame adapter. TryRevive retains activeSelf eligibility, activation before half-resource restoration, camera registration before flag resets, reload/regen/immunity/dash/cooldown/miner resets and drone master restoration. UpgradeFunction's REVIVE delegates to this owner. HP/stamina/potions, acquired abilities, remaining timers/ammo and caller identity retain their prior rules; no entity-base or companion movement code is changed.
Four original upgrade/revive fixture cases compare inactive/active slot combinations and repeated calls; two direct owner checks cover callback order, retained state and drone restoration. These deliberately use an inactive parent to avoid unrelated bootstrap. They do not exercise live OnEnable/Start, real death/revive animation, coroutine cancellation, input/reconnect or a full run. PC-04 remains open: legacy eligibility can include unused inactive slots and uses its old partial reset. Joined-player/run registry and owned status/dash/ability work must replace those compatibility assumptions before full acceptance. No R3/F2, physical/Windows or new arena-combat completion claim.
Current dash/status ownership · batch 56¶
Batch 56, implementation 41f13af2, moves AbilityHolder.Dash execution into PlayerDashExecutor, cached per Player. The original public entry/delegate remains. Cost assignment before pause, SurvivalFitness subtraction, >= stamina admission, ordered perks, duration read, resource/impulse/presentation work, scaled wait and cooldown release on insufficient stamina remain. ScenePlayerDashHooks (formerly LegacyPlayerDashHooks) retains perk/service/character/pause reads and the original weapon-hide coroutine host. The old DashInput adapter remains; assigned-session dash edges are not connected in the shared actor yet. Shared Ability cooldown/runtime fields and global workers remain R4 migration work; execution isolation does not establish cooldown isolation.
An executor generation captures queued admission and retires completion after cancellation or another accepted dash. Player disable, death/revive and actor release invalidate its execution; a pre-death waiter cannot clear a revived actor's later dash. This does not undo perk work already committed, dispose the old global ability worker or replace all effect timers. Existing revive flag-reset order is retained. Seven-character comparisons cover successful/insufficient/paused admission, SurvivalFitness, ordered Malice thresholds, Soldier reload contribution, zero-force public execution and SuddenDeath eligibility, including requested physics impulse and authored wait values. They do not qualify real input, wall-clock duration, every perk combination or movement feel.
EntityStatusCycle now owns poison/burn application/removal/tick rules, stun and freeze completion through the existing Entity state. CCDuration supplies scaled delta explicitly, preserving virtual TakeDamage and Enemy's post-base poison-resistance override. Existing dictionary/source types, serialized fields, public/virtual APIs, source.ToString keys, replacement/iteration quirks, tick/reset order and float operations remain. Fourteen original cases compare actual methods, including one non-damaging Enemy override case; complete enemy DoT combat still requires R5 integration.
ResetActivationState invalidates an older freeze completion before restoring its existing state. A controlled original iterator trace yielded multiplier 0.5 after reset; the new guard retains 1. Normal overlapping freeze products/restoration still match. This is a scoped PC-03 lifecycle correction, not a resolution of other status quirks or the deferred main-menu freeze. Ten new execution/reset groups and the batch-55 damage/life regression pass headlessly. Full capability wiring, active arena combat, live coroutine/pause/reconnect and Windows/physical-device qualification remain open. No source reference is repinned.
Character setup implementation · batch 49¶
Batch 49 changes the existing production setup boundary: GameInitialization supplies selected Character, shop values, ammo view and audio to Player.ApplyCharacter; its explicit overload returns the real setup coroutine. PlayerModifier copies those supplied inputs before its existing waits; its calculation methods are unchanged. The old parameterless adapters remain. The canonical prefab explicitly binds its original weapon mount, inherited by all eight actors in the four inspected arenas. A renamed/reordered mount works, and missing/foreign mounts fail before an extra spawn. The unused Player.CenterPoint property/lookup is removed; the camera's separate center calculation is unchanged.
The checked-in Editor fixture records original production setup output: 14 controlled cases, four observations each, 92 fields, preserving first-calculation NaN and exact float strings. Standalone RunBatch49 passes 22/22; Linux IL2CPP verifies Soldier alone and Soldier/Knight together, HUD/weapons, progressing combat/timer, return and quit. This does not close PC-01–PC-06, movement feel, complete lifecycle, death/revive, AbilityHolder or four-player acceptance. Actual run-owned actor creation and legacy-equivalent input/motor integration remain next; Gameplay.unity is still the no-actor host.
Actor preparation before capabilities · batch 50¶
Batch 50 exposes the existing Entity component cache/base-reset and Player character/stat iterator for inactive owned preparation. The normal Awake/OnEnable and typed audio-service adapter still use the same underlying operations. Player Update and later movement/resource/damage/status methods, Entity damage/status methods and PlayerModifier calculation code remain untouched. Prepared actors keep legacy MonoBehaviours and physics off, so no duplicated motor or ability implementation is introduced. The factory copies shop values, uses the existing input identity and owns actor/weapon material instances across cancel/retry/unload. A rendered preview is now available through the development tool; the on-disk shared host remains free of authored development content.
The next motor owner must consume a verified legacy-equivalent sampler and explicitly replace this isolation for its capability. Existing Player/Entity/AbilityHolder lifecycle and damage/death/revive contracts still govern migration; PC-01–PC-06 stay open. Four prepared data/identity instances were tested with synthetic devices, not four-player gameplay or a completed setup/HUD/reward layout.