Files
Project-M/Assets/_Project/Scripts/Simulation/World/RunRuntime.cs
T
kronic 304ee8c2a7 Base<->Expedition ties: portal rooms, class-at-base, prep spend + Health.Max, C3/C4/Spitter (DR-046)
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>
2026-07-06 10:37:24 -07:00

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 &amp; 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;
}
}