feat(items): Mobile-owned container inventory + stack merge/split (#209) #211
No reviewers
Labels
No labels
alpha:wave-0
alpha:wave-1
alpha:wave-2
alpha:wave-3
area:assets
area:combat
area:ecology
area:infra
area:render
area:scripting
area:ui
area:world
enhancement
epic
migration
post-alpha
roadmap
tech-debt
type:bug
type:chore
type:design
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
marco/IsoMmo!211
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/item-stacking"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Reworks the inventory into a Mobile-owned container graph (ModernUO model) and lands player-facing stack merge/split via drag&drop on top — the epic in #209. Before this, every item lived in a flat
ItemRegistrykeyed byOwnerwith anItemPlaceenum, andCarriedWeightwas a cache theWorldfacade pushed onto the player. Now theMobileowns its inventory and the graph is the ownership model.Delivered in four stages (kept as separate commits; squash on merge):
Item.Parent(Mobile | Container | world),Container : ItemwithTotalWeight/TotalItemscached and bubbled up the parent chain on change (ModernUO'sUpdateTotal), a lazyMobile.Backpack, unlimited nesting with a cycle guard,MaxItems(125)+weight capacity.CarriedWeightis now derived on the entity, not pushed by the facade. Recursive per-player serialization (v5) with a v3→v5 load upgrade. No behaviour change.bag(container = true) becomes aContainer; recursiveOwnedItem.Contentson the wire; a server-validatedMoveItemintent re-parents an owned item into one of the player's own containers (capacity + cycle checked, rolled back on reject). Client:InventoryPanel→ContainerGump+ContainerWindows(state offGameScreenper the Screen HARD GATE), and the backpack window resized to a UO-modest size.Item.Fill/SplitOff/MergeInto; server-authoritativeStackMerge(drag a stack onto a same-kind stack, overflow stays on source) andStackSplit(shift+drag →SplitAmountGump,1≤n<Amount); partial ground drop; amount→art tiers extracted to a unit-testedClient.CoreStackArtSelector."arrow"/"10 arrows"/"60,000 coins", nouns held client-side (cliloc-styleItemNameCatalog, nothing on the wire), shown as a hover tooltip in the container window.Server-authoritative throughout (every move/merge/split/drop is an intent the server validates against ownership, capacity, cycle, range, and stack bounds); single-threaded sim (one
InvokeAsyncper op);Worldstays a thin facade; content authored in Lua.ProtocolVersion14 → 17.Screenshots / recording
Captured by piloting the client via the debug harness from a fresh DB.
Resized backpack (now UO-modest) rendered as a container gump, with a bag + items:
A coin stack with its real art and amount label (
500):Honest note on what the harness can't drive: the blind debug harness can't inject a double-click (open a nested bag window), a shift+drag (the split modal), a two-same-kind-stack setup for a drag-merge (
/giveauto-stacks and caps at 500), or hover (the quantity-name tooltip). Those interactions are covered by unit tests and code review (see below) but should get a manual playtest before release — flagging rather than faking a green box.How it was tested
just lint(CSharpier + analyzers) — 0 warnings;just test— all green (GameServer 271, Client.Core 147, Shared 107, Scripting 25 incl. theapi.d.luadrift-check, Auth 19, +others; 0 failures);just check-docsclean; whole solution builds (client + tools).InventoryContainerTests(weight bubbling 1/2-level, cycle, capacity, v5 round-trip, v3→v5 upgrade, MoveItem re-parent/full/cycle),StackingTests(merge combine/overflow/full/different-kind/non-stackable; split valid/out-of-range-rejected-server-side/units-invariant; partial drop + re-pickup),StackArtSelectorTests,StackNameTests,ContainerWindowLayoutTests,InventoryGraphTests, and protocol round-trips for the new messages.Checklist
just lintpasses (CSharpier + analyzers, zero warnings)just testis greenstackArtthresholds use global defaults (theClient.Coreselector has the override hook; content-side delivery deferred).Closes #209
Follow-up fixes (review)
stoneitem — it was rendering as the generic PlaceholderArt diamond (a §Assets HARD GATE violation); now packs the real UO rock static (item_stone0x1363). Backpack screenshot above updated to the real art. (Thebagicon stays a placeholder deliberately — it's slated for custom art via the Asset Editor.)