using Unity.Collections;
using Unity.Entities;
namespace ProjectM.Simulation
{
///
/// One authored PERMANENT meta upgrade: tiered (buy tier owned+1 up to ), priced in Aether
/// with a linear ramp (cost(owned) = BaseCost + owned*CostGrowth), class-gated by
/// (bit0 = Warrior, bit1 = Ranger — resolve via , NEVER a raw 1<<ClassId:
/// the stored ClassId is the normalized FrameKind 2/3). is the stable APPEND-ONLY key
/// persisted in SaveData v6 and keyed into the live StatModifier as Tuning.MetaSourceIdBase + Id.
/// DISTINCT from — boons are run-scoped single-shots; overloading one catalog would
/// blur the two channels (DR-037). = 0xFF means no prerequisite (v1 ships a FLAT catalog
/// — the operator default; trees are an authoring change, not a code change).
///
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;
}
/// The baked permanent-upgrade pool (config blob, both worlds, NOT replicated).
public struct MetaUpgradeCatalogBlob
{
public BlobArray Defs;
}
/// Singleton carrying the baked meta catalog (ONE MetaCatalogAuthoring in the gameplay subscene).
public struct MetaUpgradeCatalog : IComponentData
{
public BlobAssetReference Value;
}
/// Pure helpers over the meta catalog + the director's record.
public static class MetaMath
{
/// Find a def index by its stable id (-1 when absent — callers preserve-and-skip unknown ids).
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;
}
/// The owned tier of (class, upgrade) in the record buffer (absent row = 0).
public static byte TierOf(DynamicBuffer 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;
}
/// Aether cost of buying tier owned+1 (linear ramp; compute from the CLAMPED owned tier).
public static int CostForTier(in MetaUpgradeDefBlob def, byte ownedClamped)
=> def.BaseCost + ownedClamped * def.CostGrowth;
}
///
/// 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).
///
public static class MetaCatalogData
{
public static BlobAssetReference BuildDefault(Allocator allocator = Allocator.Persistent)
{
var builder = new BlobBuilder(Allocator.Temp);
ref var root = ref builder.ConstructRoot();
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(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),
};
}
}
}