Run Re-Do
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
|
||||
namespace ProjectM.Simulation
|
||||
{
|
||||
/// <summary>
|
||||
/// One authored PERMANENT meta upgrade: tiered (buy tier owned+1 up to <see cref="MaxTier"/>), priced in Aether
|
||||
/// with a linear ramp (<c>cost(owned) = BaseCost + owned*CostGrowth</c>), class-gated by <see cref="ClassMask"/>
|
||||
/// (bit0 = Warrior, bit1 = Ranger — resolve via <see cref="BoonMath.MaskFor"/>, NEVER a raw <c>1<<ClassId</c>:
|
||||
/// the stored ClassId is the normalized CharacterId 2/3). <see cref="Id"/> is the stable APPEND-ONLY key
|
||||
/// persisted in SaveData v6 and keyed into the live StatModifier as <c>Tuning.MetaSourceIdBase + Id</c>.
|
||||
/// DISTINCT from <see cref="BoonDefBlob"/> — boons are run-scoped single-shots; overloading one catalog would
|
||||
/// blur the two channels (DR-037). <see cref="PrereqId"/> = 0xFF means no prerequisite (v1 ships a FLAT catalog
|
||||
/// — the operator default; trees are an authoring change, not a code change).
|
||||
/// </summary>
|
||||
public struct MetaUpgradeDefBlob
|
||||
{
|
||||
public byte Id;
|
||||
public byte ClassMask;
|
||||
public byte Target; // StatTarget as byte
|
||||
public byte Op; // ModOp as byte
|
||||
public byte MaxTier;
|
||||
public float ValuePerTier;
|
||||
public int BaseCost;
|
||||
public int CostGrowth;
|
||||
public byte PrereqId; // 0xFF = none
|
||||
public byte PrereqTier;
|
||||
public FixedString64Bytes Name;
|
||||
public FixedString128Bytes Desc;
|
||||
}
|
||||
|
||||
/// <summary>The baked permanent-upgrade pool (config blob, both worlds, NOT replicated).</summary>
|
||||
public struct MetaUpgradeCatalogBlob
|
||||
{
|
||||
public BlobArray<MetaUpgradeDefBlob> Defs;
|
||||
}
|
||||
|
||||
/// <summary>Singleton carrying the baked meta catalog (ONE MetaCatalogAuthoring in the gameplay subscene).</summary>
|
||||
public struct MetaUpgradeCatalog : IComponentData
|
||||
{
|
||||
public BlobAssetReference<MetaUpgradeCatalogBlob> Value;
|
||||
}
|
||||
|
||||
/// <summary>Pure helpers over the meta catalog + the director's <see cref="MetaTierState"/> record.</summary>
|
||||
public static class MetaMath
|
||||
{
|
||||
/// <summary>Find a def index by its stable id (-1 when absent — callers preserve-and-skip unknown ids).</summary>
|
||||
public static int FindDef(ref MetaUpgradeCatalogBlob pool, byte id)
|
||||
{
|
||||
for (int i = 0; i < pool.Defs.Length; i++)
|
||||
if (pool.Defs[i].Id == id) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>The owned tier of (class, upgrade) in the record buffer (absent row = 0).</summary>
|
||||
public static byte TierOf(DynamicBuffer<MetaTierState> record, byte classId, byte upgradeId)
|
||||
{
|
||||
for (int i = 0; i < record.Length; i++)
|
||||
if (record[i].ClassId == classId && record[i].UpgradeId == upgradeId) return record[i].Tier;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Aether cost of buying tier owned+1 (linear ramp; compute from the CLAMPED owned tier).</summary>
|
||||
public static int CostForTier(in MetaUpgradeDefBlob def, byte ownedClamped)
|
||||
=> def.BaseCost + ownedClamped * def.CostGrowth;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The DEFAULT v1 meta table + the blob builder the baker AND EditMode tests share (the BoonCatalogData
|
||||
/// pattern; an empty designer-row list on the authoring bakes this verbatim). FLAT catalog — every
|
||||
/// PrereqId = 0xFF (operator default: validate the economy before prereq trees). Ids append-only.
|
||||
/// Priced for the 15%-Aether node economy (~2-5 Aether per lucky room).
|
||||
/// </summary>
|
||||
public static class MetaCatalogData
|
||||
{
|
||||
public static BlobAssetReference<MetaUpgradeCatalogBlob> BuildDefault(Allocator allocator = Allocator.Persistent)
|
||||
{
|
||||
var builder = new BlobBuilder(Allocator.Temp);
|
||||
ref var root = ref builder.ConstructRoot<MetaUpgradeCatalogBlob>();
|
||||
var defs = builder.Allocate(ref root.Defs, 8);
|
||||
int i = 0;
|
||||
// id, mask(1=Warrior,2=Ranger,3=both), target, op, maxTier, valuePerTier, baseCost, growth, name, desc
|
||||
defs[i++] = Make(1, 3, StatTarget.MaxHealth, ModOp.Flat, 5, 15f, 10, 5, "Reinforced Frame", "+15 max health per tier");
|
||||
defs[i++] = Make(2, 3, StatTarget.Damage, ModOp.PercentAdd, 5, 0.08f, 12, 6, "Sharpened Arsenal", "+8% ability damage per tier");
|
||||
defs[i++] = Make(3, 3, StatTarget.CooldownTicks, ModOp.PercentMult, 3, -0.06f, 15, 10, "Swift Recovery", "-6% ability cooldown per tier");
|
||||
defs[i++] = Make(4, 3, StatTarget.MoveSpeed, ModOp.PercentAdd, 3, 0.05f, 10, 8, "Fleet Stride", "+5% move speed per tier");
|
||||
defs[i++] = Make(5, 1, StatTarget.MeleeDamage, ModOp.PercentAdd, 4, 0.10f, 12, 6, "Warrior's Might", "+10% melee damage per tier");
|
||||
defs[i++] = Make(6, 1, StatTarget.MeleeRange, ModOp.PercentAdd, 3, 0.08f, 10, 6, "Warrior's Reach", "+8% melee reach per tier");
|
||||
defs[i++] = Make(7, 2, StatTarget.Range, ModOp.PercentAdd, 4, 0.10f, 12, 6, "Ranger's Longshot", "+10% projectile range per tier");
|
||||
defs[i++] = Make(8, 2, StatTarget.ProjectileSpeed, ModOp.PercentAdd, 3, 0.10f, 10, 6, "Ranger's Velocity", "+10% projectile speed per tier");
|
||||
var blob = builder.CreateBlobAssetReference<MetaUpgradeCatalogBlob>(allocator);
|
||||
builder.Dispose();
|
||||
return blob;
|
||||
}
|
||||
|
||||
static MetaUpgradeDefBlob Make(byte id, byte mask, StatTarget target, ModOp op, byte maxTier,
|
||||
float valuePerTier, int baseCost, int growth, string name, string desc)
|
||||
{
|
||||
return new MetaUpgradeDefBlob
|
||||
{
|
||||
Id = id,
|
||||
ClassMask = mask,
|
||||
Target = (byte)target,
|
||||
Op = (byte)op,
|
||||
MaxTier = maxTier,
|
||||
ValuePerTier = valuePerTier,
|
||||
BaseCost = baseCost,
|
||||
CostGrowth = growth,
|
||||
PrereqId = 0xFF,
|
||||
PrereqTier = 0,
|
||||
Name = new FixedString64Bytes(name),
|
||||
Desc = new FixedString128Bytes(desc),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee5e173c41773dc4189279241bf322b7
|
||||
@@ -0,0 +1,63 @@
|
||||
using Unity.Entities;
|
||||
using Unity.NetCode;
|
||||
|
||||
namespace ProjectM.Simulation
|
||||
{
|
||||
/// <summary>
|
||||
/// One owned permanent meta-upgrade tier, keyed by (class, upgrade). The CycleDirector's DynamicBuffer of these is
|
||||
/// the authoritative per-class meta-progression record. A GLOBAL <c>[GhostField]</c> buffer on the ownerless
|
||||
/// interpolated director ghost (no OwnerSendType — the party invests together and every client reads it for the
|
||||
/// shop), mirroring <see cref="StorageEntry"/>. Persisted to disk (SaveData v6) and re-applied born-correct at
|
||||
/// player spawn as meta-band <see cref="StatModifier"/>s. Bytes only (Burst/serialization safe). Sparse — an
|
||||
/// absent (class, upgrade) row means tier 0.
|
||||
/// </summary>
|
||||
[InternalBufferCapacity(24)]
|
||||
public struct MetaTierState : IBufferElementData
|
||||
{
|
||||
/// <summary>Owning class id (Warrior/Ranger — the CharacterId anchor).</summary>
|
||||
[GhostField] public byte ClassId;
|
||||
/// <summary>Upgrade id (append-only key into the meta catalog).</summary>
|
||||
[GhostField] public byte UpgradeId;
|
||||
/// <summary>Owned tier (>=1; an absent row = 0).</summary>
|
||||
[GhostField] public byte Tier;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server-only singleton on the CycleDirector: the FIRST-COMMIT latch for the co-op route choice. Written IN-PLACE
|
||||
/// (immediate SystemAPI.SetComponent, not a deferred ECB) inside <c>RouteSelectSystem</c>'s drain loop so two
|
||||
/// same-tick picks cannot both observe <see cref="HasPick"/>==0 (the DR-014 atomicity idiom). NOT replicated.
|
||||
/// </summary>
|
||||
public struct RouteCommand : IComponentData
|
||||
{
|
||||
/// <summary>1 once a route has been committed for the current (RunEpoch, layer).</summary>
|
||||
public byte HasPick;
|
||||
/// <summary>The committed option index (into the replicated RouteOpt* set).</summary>
|
||||
public byte OptionIndex;
|
||||
/// <summary>Run epoch the pick is for (stale-reject guard).</summary>
|
||||
public int ForRunEpoch;
|
||||
/// <summary>Layer the pick is for (stale-reject guard).</summary>
|
||||
public int ForLayer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server-only persisted meta counters on the CycleDirector (mirrored to the replicated <see cref="RunInfo"/> for
|
||||
/// the HUD). Added UNCONDITIONALLY at director spawn (like CycleRuntime/ThreatState/RunPhase) so a New-Game boot
|
||||
/// has the component the bank block reads; restored values are SetComponent'd only inside the save-present block.
|
||||
/// NOT replicated.
|
||||
/// </summary>
|
||||
public struct MetaCounters : IComponentData
|
||||
{
|
||||
public int RunsCompleted;
|
||||
public int MaxDepthReached;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server-only tag of a player's class id, added at spawn by <c>GoInGameServerSystem</c>. Lets the meta systems
|
||||
/// resolve which per-class tier record to seed / spend against (class was previously only an <c>AbilityRef</c> /
|
||||
/// wire concern). NOT replicated.
|
||||
/// </summary>
|
||||
public struct PlayerClass : IComponentData
|
||||
{
|
||||
public byte ClassId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 307374d6819017f4da4bbfe64f11e7c5
|
||||
@@ -0,0 +1,18 @@
|
||||
using Unity.NetCode;
|
||||
|
||||
namespace ProjectM.Simulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Client → server permanent meta-upgrade purchase: <see cref="UpgradeId"/> keys the baked meta catalog. The
|
||||
/// TIER is SERVER-COMPUTED (a purchase always buys owned+1) — putting a tier on the wire would invite
|
||||
/// desync/cheat. Server-validated (<c>RunInfo.Lifecycle==Staging</c> phase gate, class mask, prereq, MaxTier,
|
||||
/// Aether affordability) with DR-014 in-loop ledger atomicity so two same-tick purchases on barely-enough Aether
|
||||
/// cannot both pass. UNCONDITIONAL wire type. Declared at Step 3 (wire front-load); consumed by
|
||||
/// <c>MetaSpendSystem</c> from Step 13.
|
||||
/// </summary>
|
||||
public struct MetaSpendRequest : IRpcCommand
|
||||
{
|
||||
/// <summary>Meta-catalog upgrade id (append-only key).</summary>
|
||||
public byte UpgradeId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71ddd683487e8704197b8eeddcb2c339
|
||||
Reference in New Issue
Block a user