using Unity.Entities;using Unity.Mathematics; namespace ProjectM.Simulation { /// /// Pure, deterministic merge logic for a shared storage container's buffer. /// No RNG / wall-clock, so server and (future) prediction agree. Deposit merges into an existing row /// for the same item or appends a new row; Withdraw decrements and drops a row that hits zero, clamping /// to available. DynamicBuffer is a handle, so mutations apply to the underlying entity buffer. /// Unit-tested in EditMode via a plain Entities world. /// public static class StorageMath { /// Add of , merging into an existing row if present. No-op for count <= 0 or itemId 0. public static void Deposit(DynamicBuffer buffer, ushort itemId, int count) { if (count <= 0 || itemId == 0) return; for (int i = 0; i < buffer.Length; i++) { if (buffer[i].ItemId == itemId) { var entry = buffer[i]; entry.Count += count; buffer[i] = entry; return; } } buffer.Add(new StorageEntry { ItemId = itemId, Count = count }); } /// /// Remove up to of , clamped to what is available. /// Drops the row when it reaches zero. Returns the amount actually withdrawn (0 if none). No-op for /// count <= 0 or itemId 0. /// public static int Withdraw(DynamicBuffer buffer, ushort itemId, int count) { if (count <= 0 || itemId == 0) return 0; for (int i = 0; i < buffer.Length; i++) { if (buffer[i].ItemId == itemId) { var entry = buffer[i]; int taken = entry.Count < count ? entry.Count : count; entry.Count -= taken; if (entry.Count <= 0) buffer.RemoveAt(i); else buffer[i] = entry; return taken; } } return 0; } /// Total count of across the buffer (0 if absent / itemId 0). EB-2 ledger /// affordability read; mirrors MachineSlotMath.TotalOf. Pure, non-generic, Burst-safe. public static int TotalOf(DynamicBuffer buffer, ushort itemId) { if (itemId == 0) return 0; int total = 0; for (int i = 0; i < buffer.Length; i++) if (buffer[i].ItemId == itemId) total += buffer[i].Count; return total; } /// END-1 soft-loss penalty: remove a FRACTION (0..1) of EVERY row, floored per row, dropping any /// row that hits zero. Pure/deterministic (no RNG, no wall-clock), Burst-safe; iterates back-to-front so a /// dropped row never skips its successor. No-op for fraction <= 0. public static void DrainFraction(DynamicBuffer buffer, float fraction) { fraction = math.clamp(fraction, 0f, 1f); if (fraction <= 0f) return; for (int i = buffer.Length - 1; i >= 0; i--) { var entry = buffer[i]; int drop = (int)math.floor(entry.Count * fraction); if (drop <= 0) continue; entry.Count -= drop; if (entry.Count <= 0) buffer.RemoveAt(i); else buffer[i] = entry; } } } }