Run Re-Do

This commit is contained in:
2026-07-02 20:41:43 -07:00
parent 86575dd5bc
commit 16e396841e
188 changed files with 8291 additions and 2429 deletions
@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using ProjectM.Simulation;
using Unity.Collections;
using Unity.Entities;
using UnityEngine;
namespace ProjectM.Authoring
{
/// <summary>
/// Authoring for the boon-catalog config singleton (place ONE in the gameplay subscene). Designers can author
/// rows in the inspector; an EMPTY list bakes the code-default v1 table (<see cref="BoonCatalogData.BuildDefault"/>)
/// verbatim — so the subscene object needs zero property assignment through the tooling (the
/// component_properties enum/array-drop hazard). Ids are APPEND-ONLY (they ride the replicated BoonOffer bytes
/// and, later, per-run analytics).
/// </summary>
public class BoonCatalogAuthoring : MonoBehaviour
{
[Serializable]
public struct BoonRow
{
public byte Id;
public StatTarget Target;
public ModOp Op;
public float Value;
[Tooltip("Rarity draw weight: common 100 / rare 30 / epic 10. 0 removes the boon from the pool.")]
public byte Weight;
[Tooltip("bit0 = Warrior, bit1 = Ranger, 3 = both.")]
public byte ClassMask;
public string Name;
public string Desc;
}
[Tooltip("Leave EMPTY to bake the code-default v1 table; fill to fully replace it.")]
public List<BoonRow> Rows = new List<BoonRow>();
private class BoonCatalogBaker : Baker<BoonCatalogAuthoring>
{
public override void Bake(BoonCatalogAuthoring authoring)
{
var entity = GetEntity(authoring, TransformUsageFlags.None);
BlobAssetReference<BoonCatalogBlob> blob;
if (authoring.Rows == null || authoring.Rows.Count == 0)
{
blob = BoonCatalogData.BuildDefault();
}
else
{
var builder = new BlobBuilder(Allocator.Temp);
ref var root = ref builder.ConstructRoot<BoonCatalogBlob>();
var defs = builder.Allocate(ref root.Defs, authoring.Rows.Count);
for (int i = 0; i < authoring.Rows.Count; i++)
{
var r = authoring.Rows[i];
defs[i] = new BoonDefBlob
{
Id = r.Id,
Target = (byte)r.Target,
Op = (byte)r.Op,
Value = r.Value,
Weight = r.Weight,
ClassMask = r.ClassMask,
Name = new FixedString64Bytes(r.Name ?? string.Empty),
Desc = new FixedString128Bytes(r.Desc ?? string.Empty),
};
}
blob = builder.CreateBlobAssetReference<BoonCatalogBlob>(Allocator.Persistent);
builder.Dispose();
}
AddBlobAsset(ref blob, out _);
AddComponent(entity, new BoonCatalog { Value = blob });
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 075a570afb8ef9541b6307280b53f4e7
@@ -1,51 +0,0 @@
using ProjectM.Simulation;
using Unity.Entities;
using UnityEngine;
namespace ProjectM.Authoring
{
/// <summary>
/// Authoring for the home-base mining field (<see cref="BaseFieldSpawner"/>). Place ONE in the gameplay
/// subscene. <see cref="NodePrefab"/> = the SAME ResourceNode ghost prefab the expedition uses; the server
/// system overrides each instance to RegionTag{Base} + ResourceId.Ore and scatters them in the
/// [<see cref="InnerRadius"/>, <see cref="OuterRadius"/>] annulus around the base plot center. Defaults are
/// sized to the baked 32x32 plot (square corner reach ~22.6) inside the ~28.7 boundary ring, so nodes form a
/// reachable perimeter ring that never sits on a build cell.
/// </summary>
public class BaseFieldSpawnerAuthoring : MonoBehaviour
{
[Tooltip("Resource-node ghost prefab (ResourceNodeAuthoring + GhostAuthoring). Reuse the expedition node prefab.")]
public GameObject NodePrefab;
[Tooltip("Live base-node target; the field refills toward this each respawn pass.")]
[Min(1)] public int TargetCount = 10;
[Tooltip("Inner scatter radius — clears the build plot corner reach (~22.6) + spawn ring.")]
[Min(0f)] public float InnerRadius = 23.5f;
[Tooltip("Outer scatter radius — stays inside the walkable boundary ring (~28.7).")]
[Min(0f)] public float OuterRadius = 27f;
[Tooltip("Server ticks (@60) between top-up passes.")]
[Min(1)] public int RespawnIntervalTicks = 600;
private class BaseFieldSpawnerBaker : Baker<BaseFieldSpawnerAuthoring>
{
public override void Bake(BaseFieldSpawnerAuthoring authoring)
{
var entity = GetEntity(authoring, TransformUsageFlags.None);
AddComponent(entity, new BaseFieldSpawner
{
Prefab = authoring.NodePrefab != null
? GetEntity(authoring.NodePrefab, TransformUsageFlags.Dynamic)
: Entity.Null,
TargetCount = authoring.TargetCount,
InnerRadius = authoring.InnerRadius,
OuterRadius = authoring.OuterRadius,
RespawnIntervalTicks = authoring.RespawnIntervalTicks,
});
AddComponent(entity, new BaseFieldRuntime { Epoch = 0, NextSpawnTick = 0u });
}
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: c4055a6a779d06949ae23b16334b810e
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d0503ff85f3b39f49af750a451d44e00
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using ProjectM.Simulation;
using Unity.Collections;
using Unity.Entities;
using UnityEngine;
namespace ProjectM.Authoring
{
/// <summary>
/// Authoring for the PERMANENT meta-upgrade catalog singleton (place ONE in the gameplay subscene — the
/// BoonCatalogAuthoring pattern). An EMPTY row list bakes the code-default v1 table
/// (<see cref="MetaCatalogData.BuildDefault"/>) verbatim, so the subscene object needs zero property assignment
/// through the tooling. Ids are APPEND-ONLY (persisted in SaveData v6; a removed id's saved rows are preserved
/// and skipped, never crashed on).
/// </summary>
public class MetaCatalogAuthoring : MonoBehaviour
{
[Serializable]
public struct MetaRow
{
public byte Id;
[Tooltip("bit0 = Warrior, bit1 = Ranger, 3 = both.")]
public byte ClassMask;
public StatTarget Target;
public ModOp Op;
public byte MaxTier;
public float ValuePerTier;
public int BaseCost;
public int CostGrowth;
[Tooltip("0xFF (255) = no prerequisite.")]
public byte PrereqId;
public byte PrereqTier;
public string Name;
public string Desc;
}
[Tooltip("Leave EMPTY to bake the code-default v1 table; fill to fully replace it.")]
public List<MetaRow> Rows = new List<MetaRow>();
private class MetaCatalogBaker : Baker<MetaCatalogAuthoring>
{
public override void Bake(MetaCatalogAuthoring authoring)
{
var entity = GetEntity(authoring, TransformUsageFlags.None);
BlobAssetReference<MetaUpgradeCatalogBlob> blob;
if (authoring.Rows == null || authoring.Rows.Count == 0)
{
blob = MetaCatalogData.BuildDefault();
}
else
{
var builder = new BlobBuilder(Allocator.Temp);
ref var root = ref builder.ConstructRoot<MetaUpgradeCatalogBlob>();
var defs = builder.Allocate(ref root.Defs, authoring.Rows.Count);
for (int i = 0; i < authoring.Rows.Count; i++)
{
var r = authoring.Rows[i];
defs[i] = new MetaUpgradeDefBlob
{
Id = r.Id,
ClassMask = r.ClassMask,
Target = (byte)r.Target,
Op = (byte)r.Op,
MaxTier = r.MaxTier,
ValuePerTier = r.ValuePerTier,
BaseCost = r.BaseCost,
CostGrowth = r.CostGrowth,
PrereqId = r.PrereqId,
PrereqTier = r.PrereqTier,
Name = new FixedString64Bytes(r.Name ?? string.Empty),
Desc = new FixedString128Bytes(r.Desc ?? string.Empty),
};
}
blob = builder.CreateBlobAssetReference<MetaUpgradeCatalogBlob>(Allocator.Persistent);
builder.Dispose();
}
AddBlobAsset(ref blob, out _);
AddComponent(entity, new MetaUpgradeCatalog { Value = blob });
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 4f007f7870c0afd4e93ea5b86fd21c8b
@@ -100,6 +100,11 @@ namespace ProjectM.Authoring
AddComponent(entity, new RespawnState { RespawnTick = 0, DelayTicks = authoring.RespawnDelayTicks, InvulnTicks = authoring.RespawnInvulnTicks });
AddComponent(entity, new RespawnInvuln { UntilTick = 0 });
// Expedition redesign (the ONE player-ghost re-bake, front-loaded): the send-to-all ready-check
// flag + the owner-only choice-of-3 boon offer (inert until Step 9's BoonOfferSystem lights it up).
AddComponent<PlayerReady>(entity);
AddComponent<BoonOffer>(entity);
}
}
}
@@ -78,6 +78,13 @@ namespace ProjectM.Authoring
// runtime-spawned director ghost (server + client bake the same prefab -> hash matches), like CoreIntegrity.
AddComponent(entity, new ExpeditionObjective { State = ExpeditionObjectiveState.Idle, Remaining = 0 });
// Expedition redesign: the replicated run-lifecycle FSM (RunInfo, 17 [GhostField]s) + the per-class
// permanent-meta tier buffer (MetaTierState) BOTH land in this ONE coordinated re-bake (front-loaded
// ghost layout — the writer systems arrive across Steps 213 while the state sits inert/default).
// Born Staging/empty; server RunDirectorSystem / MetaSpendSystem are the sole writers.
AddComponent(entity, new RunInfo { Lifecycle = RunLifecycle.Staging });
AddBuffer<MetaTierState>(entity);
AddComponent(entity, new ThreatConfig
{
@@ -1,45 +0,0 @@
using ProjectM.Simulation;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
namespace ProjectM.Authoring
{
/// <summary>
/// Authoring for a walk-in <see cref="ExpeditionGate"/>. Place on a visible gate object in the gameplay
/// subscene; baked into both worlds at the gate's position (the server reads its LocalTransform for the
/// overlap test, the client renders the mesh). Set From/To regions + the arrival point in the destination
/// region (offset from that region's gate so the player doesn't immediately re-trigger).
/// </summary>
public class ExpeditionGateAuthoring : MonoBehaviour
{
public enum Region : byte { Base = 0, Expedition = 1 }
[Tooltip("Region a player must be in for this gate to act on them.")]
public Region From = Region.Base;
[Tooltip("Region the player is transited to.")]
public Region To = Region.Expedition;
[Min(0.5f)] public float Radius = 2.5f;
[Tooltip("Where the player arrives in the destination region (offset from that region's gate).")]
public Vector3 ArrivalPos = new Vector3(1000f, 1f, 0f);
private class ExpeditionGateBaker : Baker<ExpeditionGateAuthoring>
{
public override void Bake(ExpeditionGateAuthoring authoring)
{
// Dynamic so the baked entity carries a LocalTransform the server can read for the overlap test.
var entity = GetEntity(authoring, TransformUsageFlags.Dynamic);
AddComponent(entity, new ExpeditionGate
{
FromRegion = (byte)authoring.From,
ToRegion = (byte)authoring.To,
Radius = authoring.Radius,
ArrivalPos = new float3(authoring.ArrivalPos.x, authoring.ArrivalPos.y, authoring.ArrivalPos.z),
});
}
}
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 22f744b59ad23834abe28fc09b661005