epic(items): Mobile-owned container inventory (ModernUO) → stack merge/split capstone #209
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#209
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Epic. Rework the inventory into a Mobile-owned container graph (ModernUO model), then land player-facing stack merge/split via drag&drop as the capstone. Reviewed via
critical-design-review, grounded in the local ModernUO checkout (~/Development/ModernUO).Today everything lives in a flat
ItemRegistrykeyed byOwnerwith anItemPlaceenum, andCarriedWeight/Equipmentare caches pushed onto the player by the facade. The correct model (ModernUO): theMobileowns its inventory — aBackpackthat is aContainer : Item;Item.Parentis a Mobile (equipped), a Container (inside a bag, nestable), or the world (ground); weight is a cached total bubbled up the parent chain on change (Item.UpdateTotal(delta),Item.cs:3131), recomputed bottom-up on restore (Container.UpdateTotals,Container.cs:345). Containers cap onMaxItems(125) andMaxWeight; a stack counts as one item.Agreed decisions
Mobile.Backpack(aContainer),Item.Parentgraph;ItemPlaceenum → parent-derived placement. Applies to every Mobile (players + creatures).Mobile.Backpack.TotalWeight + equipped, cached + delta-bubbled by the container/mobile (not pushed by the facade).CarriedWeightstops being a facade-synced field.MaxItems(125) andMaxWeight; a stack (pile) is one item towardMaxItems. Over-cap drop →SystemMessageId.ContainerFull.world.savv3 → upgrade at load (read the old flat owned items, parent them into the new backpack). No wipe.ItemRegistrystays as the root/world-item store (ground/placed) + an id→Item index for O(1) lookup from wire intents; ownership/structure live on the graph, not in the registry.Gumpwindow (reuse the framework);InventoryPanelbecomes the backpack-container gump (converted in Stage 2). Nested bags open their own window.maxStackis the sole stackable flag (1 = non-stackable); loot auto-stack = first-fit; split UX = shift+drag → quantity prompt as aSplitAmountGump : Gump; partial ground drop in scope; no rate-limit for now; amount-based art hardened into aClient.Coreselector with per-kindstackArtcontent; stackable display name carries the quantity ("arrow"/"10 arrows"/"60000 coins", noun from content, format from the catalog); backpack art resized down (~UO-modest).Stages
Container : Item(child items);Item.Parent(Mobile | Container | world); placement derived from parent;Mobileowns aBackpack; weight cached + delta-bubbled, full recompute on restore;MaxItems+MaxWeightcapacity; cycle guard;ItemRegistry→ root store + id index; recursive per-player serialization (Item/Container v4) + a v3→v4 load upgrade. Existing pickup/give/equip/weight tests stay green (the safety net). No visible surface → no screenshots.Gump;InventoryPanelconverted. BumpProtocolVersion. Screenshots: backpack open, a nested bag open.SplitAmountGump) / partial ground drop, on the graph, viaItem.Fill/MergeInto/SplitOff+Containerstacking-on-drop; amount-art selector + per-kindstackArt; quantity display names; resized bag. BumpProtocolVersion.Invariants Check
ProtocolVersionin the same change (Stage 2/3).ContainerFulland any cue areSystemMessageId; item names are content noun + catalog format.InvokeAsync; weight bubble is synchronous in-thread; no lock; no sub-component lock.Worlddelegates; weight bubbling lives onContainer/Mobile(removes the recompute from the facade).Gumpcomponents; the screen coordinates.Client.Core; render/gesture is glue.Gameplay/, transport inNetworking/, meet inGameSessionHandler.Mobile.Backpack,Container.TotalWeightbubbling); the registry is only existence-store + id index, no state shadowing.Container : Itemis extend-by-type; capacity/stack are fields/def, no kind-switch.Containerbumps its own version and serializes children recursively; per-player self-contained sub-graph, no cross-player refs, no global linker; v3→v4 load upgrade; no tick-loop IO. The recursive-serialization migration is the one delicate spot.MaxItems/MaxWeightareGameOptionsfields, default in one place.GroundItem.Amountexists); container contents are private, sent on open to the requester.container_backpack/container_baggumps exist); missing → not drawn, never a placeholder as content.Item.Parent/RootParent,UpdateTotaldelta-bubble,Container.UpdateTotalson restore; diverge on no global serial graph (per-player sub-graph, no linker).docs/inventory.md; updatearchitecture.md; correct the staleCLAUDE.md"no inventory yet" bullets; DoD-delta below.No
✗. The World.cs and act-on-instance HARD GATES are improved.Server-side validation (trust boundary)
Container; withinMaxItems+MaxWeight; not a cycle (target is not the item nor a descendant). Server re-parents and re-bubbles totals.Verification plan
ContainerFull; restore rebuilds totals (v4 round-trip); v3→v4 upgrade parents old flat items into the backpack./giveviatype+Enter(notsay).Definition of Done
Mobileowns its inventory via aBackpackContainer;Item.Parent(Mobile/Container/world) replaces theItemPlaceenum; creatures get a backpack lazily.CarriedWeightis derived from the container graph (bubbled by the container/mobile), not pushed by theWorldfacade; the item wrappers are pure delegation.MaxItems(125)/over-MaxWeightdrops are rejected withContainerFull(a stack = one item).Containerself-serializes children with its own version); a v3 blob upgrades into the new backpack at load with no data loss.Gumpwindow; nested bags open their own;InventoryPanelis the backpack gump.SplitAmountGump, server-validated amount), and partial ground drop + first-fit re-pickup all work.Client.Coreselector over per-kindstackArtcontent; a stackable item's name reads"arrow"/"10 arrows"/"60000 coins"(noun from content, format from catalog).ProtocolVersionbumped with each wire change;docs/inventory.mdadded,architecture.md+ the staleCLAUDE.md"no inventory yet" bullets updated.dotnet testgreen, whole-solution build (client + tools), zero warnings; multi-platform preserved.feat(items): stack management — merge/split drag&drop, partial ground drop, amount art + quantity namesto epic(items): Mobile-owned container inventory (ModernUO) → stack merge/split capstone