304ee8c2a7
Portal-gated rooms: new RunLifecycle.RoomExplore loot window; room+nodes persist past the last kill; PortalInteractRequest -> server-only PortalCommand advances (client derives the portal pos). RunDirectorSystem moves teardown off the InRoom edge, relocates the boss-branch/route-gate into the RoomExplore exit, and advances an empty expedition immediately (incl. a boss clear). Class-at-base via a shared ClassSwapUtil (meta-band strip + per-class MetaTierState replay, so the base RPC and the dev SetClass can't drift); ClassSelectRequest Staging-gated. Per-run PREP buffs (PrepPurchaseRequest, PrepCatalog): once-per-run == the row's prep StatModifier band is present, TotalOf-before-Withdraw atomic, stripped on Returning beside the boon band. Health.Max promoted to [GhostField] (the one ghost-hash re-bake) so the boss + floating enemy HP bars read it directly. Feel: melee cone connect-thunk (C3), hit-stop throttle so a horde wipe can't stutter (C4), Spitter cornered-hold. 456/456 EditMode; two adversarial reviews (pre-code 13-confirmed folded; post-impl clean bar the RoomExplore boss-clear dead-time, fixed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
61 lines
3.8 KiB
C#
61 lines
3.8 KiB
C#
namespace ProjectM.Simulation
|
|
{
|
|
/// <summary>
|
|
/// Server-only working state for the run FSM — lives on the CycleDirector beside <see cref="RunInfo"/> but is
|
|
/// NOT replicated (adding fields here never re-bakes the ghost). Owned/written by <c>RunDirectorSystem</c>.
|
|
/// Determinism: <see cref="RunSeed"/> = max(1, Hash(<see cref="RunEpoch"/>, <see cref="HostSalt"/>)) — monotonic
|
|
/// int, never a tick, equality-compared. Tick sentinels (<see cref="RewardGraceTick"/>/<see cref="RouteGraceTick"/>)
|
|
/// route through <see cref="TickUtil.NonZero"/> and compare via NetworkTick.IsNewerThan (never raw uint).
|
|
/// </summary>
|
|
public struct RunRuntime : Unity.Entities.IComponentData
|
|
{
|
|
// ---- run identity / seed ----
|
|
/// <summary>Working copy of the run seed (mirrored to the replicated <see cref="RunInfo.RunSeed"/>).</summary>
|
|
public uint RunSeed;
|
|
/// <summary>Monotonic run counter; bumped on the Staging→Launching edge so each run reseeds. Equality-compared.</summary>
|
|
public int RunEpoch;
|
|
/// <summary>Per-playthrough salt folded into <see cref="RunSeed"/> for cross-session map variety (seeded at spawn, non-tick).</summary>
|
|
public uint HostSalt;
|
|
|
|
// ---- room traversal ----
|
|
/// <summary>Monotonic room-seed counter; bumped per room advance so the field/enemy directors reseed. Equality-compared.</summary>
|
|
public int RoomEpoch;
|
|
/// <summary>Which of the two ping-pong sub-arena slots the active room occupies (CurrentRoom & 1).</summary>
|
|
public byte ActiveSubSlot;
|
|
/// <summary>The active room's stable map node id (single plan authority — field/enemy directors read this, never re-derive).</summary>
|
|
public int CurrentNodeId;
|
|
/// <summary>The active room's column (mirrors <see cref="RunInfo.CurrentCol"/>).</summary>
|
|
public byte CurrentCol;
|
|
/// <summary>The active room's <see cref="RoomTypeId"/> (single plan authority).</summary>
|
|
public byte CurrentRoomType;
|
|
|
|
// ---- scarcity / banking latches ----
|
|
/// <summary>Run-wide remaining resource-node allotment (floors each room's scatter; decrements per node) → true scarcity.</summary>
|
|
public int NodeBudgetRemaining;
|
|
/// <summary>The <see cref="RunEpoch"/> the terminal bank last fired for — equality latch so a multi-tick Returning banks once.</summary>
|
|
public int LastBankedRunEpoch;
|
|
/// <summary>1 iff the run ended by a genuine BOSS clear (gates the win-meter/RunsCompleted credit; 0 on abort/wipe).</summary>
|
|
public byte LastTerminalCleared;
|
|
/// <summary>Rooms actually CLEARED this run (bumped on each InRoom→RoomReward edge; reset at launch) — the
|
|
/// honest depth the terminal bank records into MaxDepthReached (never the planned RoomCount — D-F3).</summary>
|
|
public int RoomsClearedThisRun;
|
|
|
|
// ---- ready / grace ----
|
|
/// <summary>Previous-tick all-ready state (rising-edge latch for the Staging→Launching launch).</summary>
|
|
public byte WasAllReady;
|
|
/// <summary>Server tick the RoomReward boon-pick grace elapses (NonZero; IsNewerThan-compared).</summary>
|
|
public uint RewardGraceTick;
|
|
/// <summary>Server tick the RouteSelect grace elapses → auto-pick lowest-index reachable (NonZero; IsNewerThan-compared).</summary>
|
|
public uint RouteGraceTick;
|
|
|
|
/// <summary>DR-046: RoomExplore soft-timeout (NonZero; auto-advance if nobody interacts the portal), so the
|
|
/// loot window can never softlock. Set on entering RoomExplore, compared via NetworkTick.IsNewerThan.</summary>
|
|
public uint ExploreGraceTick;
|
|
|
|
|
|
// ---- boons ----
|
|
/// <summary>Monotonic per-run boon-pick counter → distinct SourceIds in the run-scoped boon band; reset each run.</summary>
|
|
public uint BoonPickCounter;
|
|
}
|
|
}
|