Run Re-Do
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Burst;
|
||||
using Unity.Entities;
|
||||
using Unity.NetCode;
|
||||
|
||||
namespace ProjectM.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// Server-only choice-of-3 boon dealer: once per <see cref="RunRuntime.RoomEpoch"/> (int-equality latch on
|
||||
/// <see cref="BoonOfferState"/>, attached beside the catalog singleton), when the run FSM enters RoomReward it
|
||||
/// draws each EXPEDITION player's 3 distinct, rarity-weighted, class-filtered options via
|
||||
/// <see cref="BoonMath.PickBoons"/> — deterministically seeded from Hash(RunSeed, room, NetworkId) — and writes
|
||||
/// the player's owner-only replicated <see cref="BoonOffer"/> (Pending=1). A base-region player (dead-respawned,
|
||||
/// late joiner) gets NO offer and never holds the gate (RunDirector counts only Pending!=0). BoonApplySystem
|
||||
/// (Step 10) consumes picks; the Returning-edge strip zeroes stragglers.
|
||||
///
|
||||
/// Ordering: <c>[UpdateAfter(RunDirectorSystem)]</c> — on the RoomReward ENTRY tick this runs after the
|
||||
/// transition, so offers exist BEFORE RunDirector's exit gate first evaluates (next tick). No CyclePhase edge.
|
||||
/// </summary>
|
||||
[BurstCompile]
|
||||
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
|
||||
[UpdateInGroup(typeof(SimulationSystemGroup))]
|
||||
[UpdateAfter(typeof(RunDirectorSystem))]
|
||||
public partial struct BoonOfferSystem : ISystem
|
||||
{
|
||||
[BurstCompile]
|
||||
public void OnCreate(ref SystemState state)
|
||||
{
|
||||
state.RequireForUpdate<BoonCatalog>();
|
||||
state.RequireForUpdate<RunInfo>();
|
||||
state.RequireForUpdate<RunRuntime>();
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState state)
|
||||
{
|
||||
var catalogEntity = SystemAPI.GetSingletonEntity<BoonCatalog>();
|
||||
|
||||
// One-shot: attach this system's latch beside the catalog singleton (the RoomFieldState idiom).
|
||||
if (!SystemAPI.HasComponent<BoonOfferState>(catalogEntity))
|
||||
{
|
||||
state.EntityManager.AddComponentData(catalogEntity, new BoonOfferState());
|
||||
return; // structural change — clean re-read next tick
|
||||
}
|
||||
|
||||
var dirEntity = SystemAPI.GetSingletonEntity<RunInfo>();
|
||||
var info = SystemAPI.GetComponent<RunInfo>(dirEntity);
|
||||
if (info.Lifecycle != RunLifecycle.RoomReward)
|
||||
return;
|
||||
|
||||
var run = SystemAPI.GetComponent<RunRuntime>(dirEntity);
|
||||
var offered = SystemAPI.GetComponent<BoonOfferState>(catalogEntity);
|
||||
if (offered.OfferedRoomEpoch == run.RoomEpoch)
|
||||
return; // this room's offers are already dealt
|
||||
|
||||
var catalog = SystemAPI.GetComponent<BoonCatalog>(catalogEntity);
|
||||
if (!catalog.Value.IsCreated)
|
||||
return;
|
||||
ref var pool = ref catalog.Value.Value;
|
||||
|
||||
foreach (var (offer, owner, region, cls) in
|
||||
SystemAPI.Query<RefRW<BoonOffer>, RefRO<GhostOwner>, RefRO<RegionTag>, RefRO<PlayerClass>>()
|
||||
.WithAll<PlayerTag>())
|
||||
{
|
||||
if (region.ValueRO.Region != RegionId.Expedition)
|
||||
continue; // home-bound players (dead-respawned, joiners) are dealt nothing
|
||||
|
||||
// Deterministic per-player draw: reconnect-stable per session, replay-reproducible per (seed, room).
|
||||
uint offerSeed = RunMapMath.Hash(run.RunSeed, (uint)info.CurrentRoom, (uint)owner.ValueRO.NetworkId) | 1u;
|
||||
BoonMath.PickBoons(offerSeed, cls.ValueRO.ClassId, ref pool, out byte o0, out byte o1, out byte o2);
|
||||
offer.ValueRW = new BoonOffer { Pending = 1, Option0 = o0, Option1 = o1, Option2 = o2 };
|
||||
}
|
||||
|
||||
offered.OfferedRoomEpoch = run.RoomEpoch;
|
||||
SystemAPI.SetComponent(catalogEntity, offered);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user