This commit is contained in:
2026-07-04 16:57:40 -07:00
parent 16e396841e
commit c6b7890212
47 changed files with 1713 additions and 209 deletions
Binary file not shown.
@@ -63,7 +63,7 @@ namespace ProjectM.Simulation
[Serializable]
public class SaveData
{
public const int CurrentVersion = 6; // v6: permanent META (per-class upgrade tiers + run counters); v5 added RunOutcomeoreCurrent
public const int CurrentVersion = 6; // v6: permanent META (per-class upgrade tiers + run counters); v5 added RunOutcome; v4 added CoreCurrent
/// <summary>Oldest save schema the loader accepts (additive); a v2 save loads with structures at full HP.</summary>
public const int MinLoadableVersion = 2;
@@ -27,7 +27,7 @@ namespace ProjectM.Simulation
// field 0-defaults and the restore guard maps 0 -> baked Max); v0/v1 garbage is still rejected.
if (data == null || data.Version < SaveData.MinLoadableVersion || data.Version > SaveData.CurrentVersion) return null;
data.Ledger ??= Array.Empty<LedgerRow>();
data.MetaUpgrades ??= Array.Empty<MetaUpgradeSave>(); // v6: null on any v<=5 file();
data.MetaUpgrades ??= Array.Empty<MetaUpgradeSave>(); // v6: null on any v<=5 file
return data;
}
catch (Exception e)
@@ -37,6 +37,22 @@ namespace ProjectM.Simulation
}
}
/// <summary>
/// A TERMINAL save (Victory/Loss latched) Continues as a FRESH CAMPAIGN that keeps the base + permanent
/// meta: the outcome latch, goal meter, and core integrity reset to 0 (the spawn restore guards re-map
/// 0 -> InProgress / empty meter / baked-full Core), while meta tiers, run counters, the ledger, and
/// placed structures survive untouched. Without this, Continue/PLAY AGAIN after a win re-latches the dead
/// outcome banner on the first replicated snapshot. Pure + idempotent; no-op for in-progress saves.
/// </summary>
public static void RollTerminalCampaignForward(SaveData data)
{
if (data == null || data.RunOutcome == 0) return;
data.RunOutcome = 0;
data.GoalCharge = 0;
data.CoreCurrent = 0;
}
public static void Save(SaveData data)
{
if (data == null) return;
@@ -90,6 +90,11 @@ namespace ProjectM.Simulation
/// at spawn on the baked value, legible at a glance).</summary>
public const float BossScaleMultiplier = 1.6f;
/// <summary>Room-landing grace before the FIRST enemy slot spawns (~1.7 s @ 60): the party just teleported
/// in and needs a beat to orient — without it the wave engages while the camera is still settling (demo
/// polish; an idle player died in ~2 s of landing). Subsequent slots keep the normal drip cadence.</summary>
public const uint RoomEntryGraceTicks = 100;
// ---- Inventory (per-player bag; InventoryMath / ResourceHarvestSystem / InventoryDepositSystem) ----
/// <summary>Max stacks a player can carry; InventoryMath rejects deposits past this and the harvest remainder spills to the global ledger.</summary>
@@ -0,0 +1,16 @@
using Unity.Entities;
namespace ProjectM.Simulation
{
/// <summary>
/// SERVER-ONLY roster tag for the players conscripted into the CURRENT run — stamped on every connected
/// player at the launch edge (Launching → room 0), removed on the Returning edge. Room advances teleport
/// ONLY participants: a dead-respawned member (back at base) is re-conscripted on the next advance (the
/// operator-locked default), while a mid-run late JOINER — who never readied — stays safely at base until
/// the next Staging (spec §2.2 closed-party rule; post-impl review, confirmed medium). NOT a [GhostField];
/// disconnect cleanup is free (the tag dies with the player ghost's LinkedEntityGroup despawn).
/// </summary>
public struct RunParticipant : IComponentData
{
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 92795e88279d04e42af70f8ec1032cc7