Lot moves to ItemInstance at key 11; stacks merge, split, restore and root by lot

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Nibu 2026-11-13 03:00:44 +00:00
commit 00a036427d
5 changed files with 16 additions and 12 deletions

View file

@ -2465,7 +2465,7 @@ public class EquippedCargoBay : EquippedItem
// For simple commodities, search for existing item stacks to add to
foreach (var cargoItem in Cargo.Keys)
{
if (!item.Data.Key.Equals(cargoItem.Data.Key)) continue;
if (!item.Data.Key.Equals(cargoItem.Data.Key) || item.Lot != cargoItem.Lot) continue;
var cargoCommodity = (SimpleCommodity) cargoItem;
if (cargoCommodity.Quantity >= itemData.MaxStack) continue;
@ -2563,7 +2563,7 @@ public class EquippedCargoBay : EquippedItem
ItemsOfType[item.Data.Key] = new List<ItemInstance>();
ItemsOfType[item.Data.Key].Add(item);
}
else if (Occupancy[cargoCoord.x, cargoCoord.y] is SimpleCommodity cargoCommodity && cargoCommodity.Data.Key.Equals(item.Data.Key))
else if (Occupancy[cargoCoord.x, cargoCoord.y] is SimpleCommodity cargoCommodity && cargoCommodity.Data.Key.Equals(item.Data.Key) && cargoCommodity.Lot == item.Lot)
{
if (cargoCommodity.Quantity + item.Quantity <= itemData.MaxStack)
{
@ -2643,7 +2643,7 @@ public class EquippedCargoBay : EquippedItem
item.Quantity -= quantity;
Mass -= itemData.Mass * quantity;
ThermalMass -= itemData.Mass * itemData.SpecificHeat * quantity;
return new SimpleCommodity{Data = item.Data, Quantity = quantity, Rotation = item.Rotation};
return new SimpleCommodity{Data = item.Data, Lot = item.Lot, Quantity = quantity, Rotation = item.Rotation};
}
public void Remove(CraftedItemInstance item)

View file

@ -60,8 +60,8 @@ public static class EntitySerializer
&& itemManager.GetData(commodity).MaxStack >= 1)
{
var maxStack = itemManager.GetData(commodity).MaxStack;
var first = new SimpleCommodity { Data = commodity.Data, Rotation = commodity.Rotation, Quantity = maxStack };
var rest = new SimpleCommodity { Data = commodity.Data, Rotation = commodity.Rotation, Quantity = commodity.Quantity - maxStack };
var first = new SimpleCommodity { Data = commodity.Data, Lot = commodity.Lot, Rotation = commodity.Rotation, Quantity = maxStack };
var rest = new SimpleCommodity { Data = commodity.Data, Lot = commodity.Lot, Rotation = commodity.Rotation, Quantity = commodity.Quantity - maxStack };
if (!bay.TryStore(first, position)) rest.Quantity += first.Quantity;
spill.Add((bay, rest));
return;
@ -82,7 +82,7 @@ public static class EntitySerializer
var remaining = rest.Quantity;
while (remaining > 0)
{
var chunk = new SimpleCommodity { Data = rest.Data, Rotation = rest.Rotation, Quantity = Math.Min(remaining, maxStack) };
var chunk = new SimpleCommodity { Data = rest.Data, Lot = rest.Lot, Rotation = rest.Rotation, Quantity = Math.Min(remaining, maxStack) };
var placed = home.TryStore(chunk) || bays.Where(b => b != home).Any(b => b.TryStore(chunk));
// A refused store may still have topped up a stack; what is left in the chunk found no room.
remaining -= Math.Min(remaining, maxStack) - (placed ? 0 : chunk.Quantity);

View file

@ -24,6 +24,11 @@ public abstract class ItemInstance
{
[JsonProperty("data"), Key(0)] public CultRecordRef<ItemData> Data;
[JsonProperty("rotation"), Key(1)] public ItemRotation Rotation;
// The lot this unit was minted or mined from: crafted units' design, provenance, workmanship and role fills all
// live there; a mined SimpleCommodity names its Extracted lot (zone, belt, ore). Lot 0 is no lot: bought, staged
// and loadout commodities, and every save from before the field reached SimpleCommodity.
[JsonProperty("lot"), Key(11)] public int Lot;
}
[Union(0, typeof(CompoundCommodity)),
@ -33,9 +38,7 @@ public abstract class ItemInstance
public abstract class CraftedItemInstance : ItemInstance
{
// Keys 2 (Quality), 9 (Ingredients) and 10 (Product) belonged to fields moved to Lot; do not reuse them.
// The lot this unit was minted from: its design, provenance, workmanship and role fills all live there.
[JsonProperty("lot"), Key(11)] public int Lot;
// Key 11 (Lot) lives on ItemInstance.
}
[MessagePackObject, JsonObject(MemberSerialization.OptIn)]

View file

@ -72,8 +72,9 @@ public class ItemManager
};
}
// The one resolution path from a crafted instance to the lot it was minted from.
public Lot GetLot(CraftedItemInstance item) => Lots[item.Lot];
// The one resolution path from an instance to the lot it was minted or mined from. Lot 0 throws: a bought or
// staged commodity has no lot, and the caller asks Lot != 0 first.
public Lot GetLot(ItemInstance item) => Lots[item.Lot];
// Returns stat when not equipped: a null-entity context with no heat, no power and no modifiers. It says so
// in its own name (UnequippedStatContext) rather than being a third quiet dialect of the arithmetic.

View file

@ -225,7 +225,7 @@ public static class RunSave
var roots = zones
.SelectMany(zone => zone.Contents?.Entities ?? new List<EntityPack>())
.SelectMany(EntitySerializer.Items)
.OfType<CraftedItemInstance>()
.Where(item => item.Lot != 0)
.Select(item => item.Lot);
cache.Commit(batch =>
{