diff --git a/src/AcDream.Core/Items/ClientObjectTable.cs b/src/AcDream.Core/Items/ClientObjectTable.cs
index 5ed850a8..b789c11c 100644
--- a/src/AcDream.Core/Items/ClientObjectTable.cs
+++ b/src/AcDream.Core/Items/ClientObjectTable.cs
@@ -205,6 +205,20 @@ public sealed class ClientObjectTable
return true;
}
+ ///
+ /// Apply a SetStackSize (0x0197) update: set the object's StackSize + Value and
+ /// fire ObjectUpdated so bound widgets refresh the quantity overlay. False if the
+ /// object is unknown. Retail: ACCWeenieObject::ServerSaysSetStackSize (0x0058...).
+ ///
+ public bool UpdateStackSize(uint guid, int stackSize, int value)
+ {
+ if (!_objects.TryGetValue(guid, out var item)) return false;
+ item.StackSize = stackSize;
+ item.Value = value;
+ ObjectUpdated?.Invoke(item);
+ return true;
+ }
+
///
/// Canonical CreateObject ingestion: create-if-absent, else patch the
/// wire-carried fields in place (retail SetWeenieDesc). Preserves the
diff --git a/tests/AcDream.Core.Tests/Items/ClientObjectTableUpdateTests.cs b/tests/AcDream.Core.Tests/Items/ClientObjectTableUpdateTests.cs
index b5ec1c78..e2bab4ea 100644
--- a/tests/AcDream.Core.Tests/Items/ClientObjectTableUpdateTests.cs
+++ b/tests/AcDream.Core.Tests/Items/ClientObjectTableUpdateTests.cs
@@ -38,4 +38,24 @@ public sealed class ClientObjectTableUpdateTests
Assert.Equal(99, t.Get(0x50000001u)!.Properties.Ints[5]);
Assert.True(updated);
}
+
+ [Fact]
+ public void UpdateStackSize_knownObject_setsFieldsAndFiresUpdated()
+ {
+ var t = new ClientObjectTable();
+ t.AddOrUpdate(new ClientObject { ObjectId = 0x600u, StackSize = 1, Value = 5 });
+ bool updated = false;
+ t.ObjectUpdated += _ => updated = true;
+
+ bool ok = t.UpdateStackSize(0x600u, stackSize: 25, value: 125);
+
+ Assert.True(ok);
+ Assert.Equal(25, t.Get(0x600u)!.StackSize);
+ Assert.Equal(125, t.Get(0x600u)!.Value);
+ Assert.True(updated);
+ }
+
+ [Fact]
+ public void UpdateStackSize_unknownObject_returnsFalse()
+ => Assert.False(new ClientObjectTable().UpdateStackSize(0xDEADu, 1, 1));
}