feat(ui): centralize retail selection state
This commit is contained in:
parent
c7607f019c
commit
7983309d23
30 changed files with 591 additions and 108 deletions
|
|
@ -102,7 +102,11 @@ retained gameplay tree, LayoutDesc importer, window runtime, and panel
|
|||
controllers. Neither presentation stack owns independent game-state truth.
|
||||
Plugins register retained gameplay markup through the BCL-only
|
||||
`AcDream.Plugin.Abstractions.IUiRegistry`; they do not import App or ImGui
|
||||
assemblies.
|
||||
assemblies. Core `SelectionState` is the sole selected-object owner for world,
|
||||
radar, inventory, paperdoll, toolbar, use/examine consumers, and plugins;
|
||||
`IPluginHost.Selection` exposes that same state and retail-style old/new callback.
|
||||
Temporary pointer modes are separate App orchestration in `InteractionState` and
|
||||
must never become a competing selection owner.
|
||||
|
||||
Every retained gameplay window has a typed `RetailWindowHandle` and at most one
|
||||
`IRetainedPanelController` lifecycle owner. Multi-controller windows use
|
||||
|
|
@ -164,6 +168,7 @@ src/
|
|||
IPluginHost.cs -> done
|
||||
IGameState.cs -> done
|
||||
IEvents.cs -> done
|
||||
ISelectionService.cs -> done
|
||||
|
||||
AcDream.App/ Layer 1 + Layer 4 wiring
|
||||
Rendering/
|
||||
|
|
|
|||
|
|
@ -214,8 +214,9 @@ What `GameWindow` loses:
|
|||
`LiveSessionController`.
|
||||
- `_entitiesByServerGuid` + per-entity dictionaries + ServerGuid↔Id
|
||||
translation → moved into `LiveEntityRuntime`.
|
||||
- `WorldPicker` + `_selectedGuid` + `SendUse` / `SendPickUp` → moved
|
||||
into `SelectionInteractionController`.
|
||||
- `WorldPicker` + selection-driven `SendUse` / `SendPickUp` orchestration → moved
|
||||
into `SelectionInteractionController`; Core `SelectionState` remains the
|
||||
already-shipped session owner and is injected into that controller.
|
||||
- Per-frame draw orchestration → moved into `RenderFrameOrchestrator`.
|
||||
|
||||
The eventual `GameEntity` aggregation (target state described in
|
||||
|
|
@ -289,7 +290,7 @@ All four M1 demo targets must still work.
|
|||
|
||||
### Step 4 — `SelectionInteractionController`
|
||||
|
||||
**Scope:** Extract `WorldPicker`, `_selectedGuid`, `SendUse`,
|
||||
**Scope:** Extract `WorldPicker`, the Core `SelectionState` consumers, `SendUse`,
|
||||
`SendPickUp`, and the `InputAction.Select*` / `UseSelected` /
|
||||
`SelectionPickUp` switch cases into one controller. Depends on Step 3
|
||||
(uses `LiveEntityRuntime`).
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ accepted-divergence entries (#96, #49, #50).
|
|||
|
||||
---
|
||||
|
||||
## 3. Documented approximation (AP) — 99 active rows
|
||||
## 3. Documented approximation (AP) — 98 active rows
|
||||
|
||||
Wave-0 UI ledger repair (2026-07-10) retired stale AP-38, resolved the AP-84
|
||||
collision, restored overwritten paperdoll rows as AP-92/AP-93, and registered
|
||||
|
|
@ -162,8 +162,7 @@ AP-94..AP-110 for the confirmed retail-UI completion gaps.
|
|||
| AP-53 | Contents grid slot count defaults to 102 (main pack) or 24 (side bag) when the open container's `ItemsCapacity` is absent/0. Container-switching is now wired (D.2b): clicking a side bag sends `Use 0x0036` and the grid repopulates from `ViewContents 0x0196`. This row covers only the capacity *default* when the server omits `ItemsCapacity`. | `src/AcDream.App/UI/Layout/InventoryController.cs` (`Populate`) | `ItemsCapacity` is not reliably on every ClientObject yet; 102/24 are the retail standard capacities. Retire when `ItemsCapacity` is confirmed delivered for all containers. | A non-102/24 pack shows the wrong empty-slot count — cosmetic only. | retail main-pack capacity 102; side-pack 24; ACE `ItemsCapacity`; `ViewContents 0x0196` |
|
||||
| AP-54 | Inventory window vertical resize is a toolkit approximation: bottom-edge drag, expand-only (Min = dat default 372 px, Max = 560 px constant), contents grid/sub-window/scrollbar/backdrop stretched via overridden `Left\|Top\|Bottom` anchors. | `src/AcDream.App/Rendering/GameWindow.cs` (inventory frame setup) | retail's gmInventoryUI resize lives in keystone.dll (no decomp); the anchor-stretch + 372..560 range matches the observed retail behavior for the dev loop. | The expand range / which elements reflow could differ from retail (e.g. retail may allow shrink-below-default); shrink is intentionally disabled for now. | retail gmInventoryUI resize (keystone.dll, no decomp); `UiNineSlicePanel` resize |
|
||||
| AP-55 | The toolbar's item slots keep the hardcoded `UiItemSlot.EmptySprite = 0x060074CF` rather than resolving their cell template via attribute `0x1000000e`. Correct in outcome (the toolbar's `0x1000000e` resolves to a generic prototype whose empty media is `0x060074CF`) but not dat-resolved. | `src/AcDream.App/UI/UiItemSlot.cs` (`EmptySprite` default); `src/AcDream.App/UI/Layout/ToolbarController.cs` | The inventory lists now port the retail resolver (`ItemListCellTemplate`); the toolbar was left on its hardcoded default to avoid regressing frozen, working art. Retire when the toolbar is routed through `ItemListCellTemplate`. | If a future toolbar layout's `0x1000000e` points at a non-generic prototype, the toolbar would show stale art. | `UIElement_ItemList::InternalCreateItem` 0x004e3570; catalog `0x21000037` |
|
||||
| AP-57 | The open-container triangle (`0x06005D9C`) + selected-item square (`0x06004D21`) are drawn as **procedural `UiItemSlot` overlays** keyed by controller state (`_openContainer`/`_selectedItem`), reproducing the retail keying (`item.itemID == openContainerId / selectedItemId` per `UpdateOpenContainerIndicator 0x004e3070` / `ItemList_SetSelectedItem 0x004e2fe0`) but not via the dat prototype's `m_elem_Icon_OpenContainer`/`m_elem_Icon_Selected` state elements + `SetOpenContainerState`/`SetSelectedState` calls. | `src/AcDream.App/UI/UiItemSlot.cs`; `src/AcDream.App/UI/Layout/InventoryController.cs` | The procedural overlay produces the correct visual result; the 36×36 container prototype (`0x1000033F`) lacks the square child, so the procedural overlay is the only way to show the square on a selected bag — the dat-state path retail uses would require the prototype to be richer than it is. | A cell that should show an indicator via a dat-state path retail uses but we don't would be missed; outcome currently matches retail for the open/selected indicator cases. | `UIElement_ItemList::UpdateOpenContainerIndicator` 0x004e3070; `ItemList_SetSelectedItem` 0x004e2fe0; `SetOpenContainerState` 0x004e1200; `SetSelectedState` 0x004e1240 |
|
||||
| AP-58 | Inventory item **selection is panel-local + visual-only** — clicking a grid item moves the green square but does not wire to the selected-object bar (`SelectedObjectController`) or to global 3D-world selection. | `src/AcDream.App/UI/Layout/InventoryController.cs` (`SelectItem`) | The selected-object bar + 3D-world selection wiring is deferred to the selection follow-up; the panel-local square is the correct first step. | Selecting an inventory item doesn't populate the selected-object strip as retail does — functional gap deferred. | `gmInventoryUI`/`gmBackpackUI` item-select → `ClientUISystem::SetSelectedObject`; `SelectedObjectController` |
|
||||
| AP-57 | The open-container triangle (`0x06005D9C`) + selected-item square (`0x06004D21`) are drawn as **procedural `UiItemSlot` overlays** keyed by `_openContainer` plus Core `SelectionState.SelectedObjectId`, reproducing the retail keying (`item.itemID == openContainerId / selectedID` per `UpdateOpenContainerIndicator 0x004e3070` / `SetSelectedObject 0x0058c2e0`) but not via the dat prototype's `m_elem_Icon_OpenContainer`/`m_elem_Icon_Selected` state elements + `SetOpenContainerState`/`SetSelectedState` calls. | `src/AcDream.App/UI/UiItemSlot.cs`; `src/AcDream.App/UI/Layout/InventoryController.cs` | The procedural overlay produces the correct visual result; the 36×36 container prototype (`0x1000033F`) lacks the square child, so the procedural overlay is the only way to show the square on a selected bag — the dat-state path retail uses would require the prototype to be richer than it is. | A cell that should show an indicator via a dat-state path retail uses but we don't would be missed; outcome currently matches retail for the open/selected indicator cases. | `UIElement_ItemList::UpdateOpenContainerIndicator` 0x004e3070; `ACCWeenieObject::SetSelectedObject` 0x0058c2e0; `SetOpenContainerState` 0x004e1200; `SetSelectedState` 0x004e1240 |
|
||||
| AP-59 | The per-cell container **capacity bar** (retail `UIElement_UIItem::UpdateCapacityDisplay 0x004e16e0`, element `0x10000347`) is drawn as a **procedural `UiItemSlot` overlay** (track `0x06004D22` full + fill `0x06004D23` clipped bottom-up to `GetContents(guid).Count / ItemsCapacity`), not via a real dat `UIElement_Meter` child. **Right-anchored flush** (the dat rect X=26 in a 36px cell sat ~5px off the edge — flush per the visual gate) and **bottom-up fill assumed** (the dat `m_eDirection` 0x6f isn't read, cf. AP-50). A CLOSED side bag reads empty until opened (its contents aren't indexed until `ViewContents`) — faithful to retail's known-children count, divergent if retail pre-loads. | `src/AcDream.App/UI/UiItemSlot.cs` (`CapacityFill` draw); `src/AcDream.App/UI/Layout/InventoryController.cs` (`SetCapacityBar`) | UiItemSlot is a behavioral leaf that paints overlays procedurally (cf. AP-57); the meter sprites + fill formula are the faithful port. Right-anchor + bottom-up were visual-gate calls. Further visual polish is deferred — ISSUES #146. | Fill direction / exact bar rect could differ from retail's `m_eDirection` + dat X; closed-bag bars read empty if retail pre-loads container counts. | `UIElement_UIItem::UpdateCapacityDisplay` 0x004e16e0; element `0x10000347` (back `0x06004D22` / front `0x06004D23`); `GetNumContainedItems` |
|
||||
| AP-60 | Inventory drag **`OnDragLift` is a no-op** + the source cell is **not dimmed**: retail dims the lifted item's source cell (`RecvNotice_ItemListBeginDrag`); acdream leaves the item in place during the drag + the floating ghost. | `src/AcDream.App/UI/Layout/InventoryController.cs` | Cosmetic only — the dragged item is still unambiguously identifiable; dimming the source requires tracking the source cell reference across drag events, deferred to a polish pass. | A drag in progress doesn't visually mark its origin — cosmetic only, no functional effect. | `RecvNotice_ItemListBeginDrag` acclient_2013_pseudo_c.txt |
|
||||
| AP-61 | Drop on a **CLOSED side bag is advisory-accept**: the client can't know a closed bag's item count (contents aren't indexed until opened), so `OnDragOver` shows green + relies on the server's `InventoryServerSaveFailed` reject + the rollback; retail knows the count when loaded. | `src/AcDream.App/UI/Layout/InventoryController.cs` (`IsContainerFull`) | A drop into a closed-but-full bag shows green then snaps back (a flicker) instead of pre-showing red. Faithful only when the bag has been opened (then `GetContents` is populated). The server's `0x00A0` + optimistic rollback is the authoritative safety net. | A drop into a closed-but-full bag shows green → brief flicker → snap-back instead of retail's pre-emptive red circle. | `UIElement_ItemList::InqDropIconInfo 0x004e26f0` |
|
||||
|
|
|
|||
|
|
@ -481,7 +481,7 @@ behavior. Estimated 17–26 days focused work, 3–5 weeks calendar.
|
|||
- **D.5 — Core panels.** Attributes (`chunk_00470000.c:FUN_0047ba70`), Skills (same), Paperdoll (`chunk_004A0000.c:FUN_004A5200`), Inventory, Spellbook (`chunk_004C0000.c`), Fellowship, Allegiance. Each uses the port sketches in slice 05. **(Targets `AcDream.UI.Abstractions` — ships with D.2a using ImGui-rendered widgets; reskinned by D.2b.)** The *chat* panel originally listed under D.5 shipped early in Phase I (I.4 input + I.7 combat translator superseded the chat-panel design here); this entry now covers Attributes / Skills / Paperdoll / Inventory / Spellbook / Fellowship / Allegiance only.
|
||||
- **✓ SHIPPED — D.5.1 — Toolbar (action bar).** Shipped 2026-06-16/17 (`30b28c2`→`0e7a083`, branch claude/hopeful-maxwell-214a12). First data-driven *game* panel: `gmToolbarUI` (`LayoutDesc 0x21000016`) — 18 shortcut slots from the persisted `PlayerDescription` SHORTCUT block, real **composited** item icons (opaque type-default underlay via the `EnumIDMap 0x10000004` resolve), **occupancy-gated slot numbers 1–9** (occupied = dark-box peace/war `0x10000042/43`; empty = background `0x1000005e` from cell composite `0x10000341`), **click-to-use** (`ItemHolder::UseObject` → `0x0036`), **peace/war stance** indicator live-wired to `CombatState`, **movable**, and a **chrome frame** (UiNineSlicePanel drawn over content via the new `UiElement.OnDrawAfterChildren` hook). New shared widgets `UiItemSlot` (`UIElement_UIItem` 0x10000032, procedural leaf) + `UiItemList` (`UIElement_ItemList` 0x10000031, factory branch) + `IconComposer` (CPU layered composite). `CreateObject.TryParse` extended to the full ACE-order weenie-header tail to capture `IconId`/`IconOverlay`/`IconUnderlay` → `ItemRepository.EnrichItem` → re-render. Spec/plan `docs/superpowers/{specs,plans}/2026-06-16-d2b-toolbar-phase1*.md`; research drop `docs/research/2026-06-16-*deep-dive.md` + synthesis. Divergence IA-16/IA-17 added. **User-confirmed** (numbers, icons, frame). Per-task spec+code-review throughout.
|
||||
- **✓ SHIPPED — D.5.2 — Stateful item-icon system.** Shipped 2026-06-17/18 (`419c3ac`..`fb288ad`, branch claude/hopeful-maxwell-214a12; **visually verified on a live Coldeve server**). Faithful retail icon composite (`IconData::RenderIcons` @0x0058d180): (1) `UiEffects` bitfield captured from the `CreateObject` weenie header (was discarded) → `ItemInstance.Effects`; (2) `IconComposer.GetIcon` rewritten as a 2-stage composite — Stage 1 = drag icon (base + custom overlay) + the effect treatment, Stage 2 = type-default underlay + custom underlay + drag. The effect treatment ports the **surface overload** of `SurfaceWindow::ReplaceColor` (`0x004415b0`): the textured effect tile (`EnumIDMap 0x10000005` by `LowestSetBit(effects)+1`, fallback `0x21` solid-black) is copied **per-pixel** into the icon's pure-white pixels — magical items take the tile's GRADIENT hue, mundane items go black; (3) `PublicUpdatePropertyInt (0x02CE)` parser + `WorldSession.ObjectIntPropertyUpdated` event + `GameWindow` subscription → `ItemRepository.UpdateIntProperty` → icon re-composites live. **Appraise (`0x00C9`) carries NO icon data** (ACE proof: `Icon`/`IconOverlay`/`IconUnderlay`/`UiEffects` all lack `[AssessmentProperty]`) — dropped as a no-op. **Two visual-verification fixes landed after the subagent build:** the `effects==0` recolor MUST run (mundane white edges → black, `40c97a5`) and the tint is a per-pixel GRADIENT not a flat color (the surface overload, `fb288ad`) — both confirmed via clean Ghidra + named decomp. Divergence: IA-16 retired; IA-18 (per-pixel surface-copy anti-regression) + AP-45 (0x02CE sequence) added; **AP-43/AP-44 retired by the visual fixes**. Spec/plan/research: `docs/superpowers/{specs,plans}/2026-06-17-d2b-stateful-icon*.md`, `docs/research/2026-06-17-stateful-icon-RESOLVED.md`.
|
||||
- **D.5 remaining — sub-phase ledger.** D.5.1 (toolbar + the `UiItemSlot`/`UiItemList`/`IconComposer` spine) ✅, D.5.2 (stateful icons) ✅, D.5.4 (client object/item data model) ✅, D.2b-B window manager (`abbd97b`) ✅, D.2b-B B-Grid (inventory sub-window mount + `UiItemList` grid) ✅, D.2b-B B-Controller (inventory population + burden meter + captions, `03fbf44`; **visually confirmed 2026-06-21** — two render bugs fixed at the gate `417b137`: backdrop wash-out [a #145 continuation — mounted sub-window slots must keep their own frame ZLevel, not inherit the base root's 1000] + captions [drive the host UiText directly]) ✅, D.2b-B B-Wire (`EncumbranceVal`/PropertyInt 5 wire delivery, AP-48/49 retired) ✅, inventory window finish Stage 1 (scroll/frame/vertical-resize/102-slot grid) ✅, empty-slot art ✅, container-switching + open/selected indicators + main-pack icon + per-bag capacity bar ✅, B-Drag (inventory drag-drop / item moving) ✅, **Sub-phase C (paperdoll): Slice 1 equip slots ✅ + Slice 2 3-D doll `UiViewport` (Type 0xD via the `IUiViewportRenderer` RTT seam) + Slots toggle ✅ (visually confirmed 2026-06-25, `8fa66c2` — pose/camera/heading all retail-verbatim)** ✅, **UI Studio** (`AcDream.App ui-studio`) ✅, **importer dat-fidelity (Fix A/B/C)** ✅, and **Character window** (`LayoutDesc 0x2100002E`, `CharacterStatController`) ✅ are shipped. Build order from here: **(d) finish the bar — D.5.3 remaining (selected-object meter/mana, stack entry/slider) + spell shortcuts + the selection→selected-object-bar wiring (AP-58); ISSUES #146/#147/#148.** Each ☐ below gets its own brainstorm → spec → plan.
|
||||
- **D.5 remaining — sub-phase ledger.** D.5.1 (toolbar + the `UiItemSlot`/`UiItemList`/`IconComposer` spine) ✅, D.5.2 (stateful icons) ✅, D.5.4 (client object/item data model) ✅, D.2b-B window manager (`abbd97b`) ✅, D.2b-B B-Grid (inventory sub-window mount + `UiItemList` grid) ✅, D.2b-B B-Controller (inventory population + burden meter + captions, `03fbf44`; **visually confirmed 2026-06-21** — two render bugs fixed at the gate `417b137`: backdrop wash-out [a #145 continuation — mounted sub-window slots must keep their own frame ZLevel, not inherit the base root's 1000] + captions [drive the host UiText directly]) ✅, D.2b-B B-Wire (`EncumbranceVal`/PropertyInt 5 wire delivery, AP-48/49 retired) ✅, inventory window finish Stage 1 (scroll/frame/vertical-resize/102-slot grid) ✅, empty-slot art ✅, container-switching + open/selected indicators + main-pack icon + per-bag capacity bar ✅, B-Drag (inventory drag-drop / item moving) ✅, **Sub-phase C (paperdoll): Slice 1 equip slots ✅ + Slice 2 3-D doll `UiViewport` (Type 0xD via the `IUiViewportRenderer` RTT seam) + Slots toggle ✅ (visually confirmed 2026-06-25, `8fa66c2` — pose/camera/heading all retail-verbatim)** ✅, **UI Studio** (`AcDream.App ui-studio`) ✅, **importer dat-fidelity (Fix A/B/C)** ✅, **Character window** (`LayoutDesc 0x2100002E`, `CharacterStatController`) ✅, and **Wave 3.1 unified selection** (Core `SelectionState`; AP-58 retired) ✅ are shipped. Build order from here: **finish Wave 3 item policy/router/cursors, then D.5.3 selected-object mana + stack entry/slider and the separate favorite-spell bars; ISSUES #146/#147/#197.** Each ☐ below gets its own focused oracle/spec/plan/gate.
|
||||
- **✓ SHIPPED — UI Studio** (2026-06-26, branch `claude/hopeful-maxwell-214a12`, ~33693c6→HEAD). Standalone `AcDream.App ui-studio <datdir> [--layout 0xNNNN | --dump <slug>] [--screenshot <png>]` Silk tool that previews any panel through the **production renderer** (`RenderBootstrap.cs` + GameWindow untouched). Sources: 26-window retail dump (`docs/research/2026-06-25-retail-ui-layout-dump.json`) via `--dump`, or live dat import + fixtures via `--layout`. Interactive canvas (click-routing to `UiHost`, Interact/Inspect ImGui toggle), headless `--screenshot`. Architecture: `src/AcDream.App/Studio/` (`StudioWindow`, `PanelFbo`, `FixtureProvider`, `LayoutSource`) + `src/AcDream.App/Rendering/RenderBootstrap.cs`. ISSUES #156 (inventory all-black in studio) and #157 (GameWindow font resolver) filed.
|
||||
- **✓ SHIPPED — Importer dat-fidelity** (Fix A/B/C, 2026-06-26, same branch). **Boundary established: look = importer (font/justification/color from dat); state/behavior = runtime controller logic.** Fix A: justification property `0x14`/`0x15` → `UiText` Centered/RightAligned/VerticalJustify. Fix B: FontColor property `0x1B` → `UiText.DefaultColor`; character colors proved RUNTIME (no importer fix). Fix C: FontDid → per-element dat font via a resolver in `DatWidgetFactory` — **STUDIO path only**; GameWindow passes `null` (Issue #157). Fix 4 (UIState-group activation): INVESTIGATED — no importer fix exists; the dat has no Visible encoding; runtime `gm*UI` is correct.
|
||||
- **✓ SHIPPED — Character window** (`LayoutDesc 0x2100002E`, `CharacterStatController`, 2026-06-26, same branch). **Visually user-confirmed 2026-06-26 — Attributes tab reads as retail.** Three tabs, header (name/heritage/PK), large-gold level number (dat font, `largeDatFont` 18px), "Total Experience (XP):" + "XP for next level:" captions, 9-row attribute list (icons + right-aligned values + Health/Stamina/Mana vitals), click-to-select (top/bottom selection bars + footer State-B "{Attr}: {value}" / "Experience To Raise: Infinity!" + affordability-gated raise triangles), centered footer. User noted "still needs some polish for later" — deferred to Issue #158.
|
||||
|
|
|
|||
|
|
@ -979,8 +979,9 @@ implementation patch.
|
|||
is not a faithful `UseObject` port: it omits ready/vendor/trade/confirmations,
|
||||
the complete `DetermineUseResult` classification, container post-actions,
|
||||
`AttemptPlaceIn3D`, and retail busy semantics.
|
||||
6. Selection remains owned by `GameWindow._selectedGuid`; the previously documented
|
||||
`Core.SelectionState` file is absent in this worktree.
|
||||
6. **Resolved 2026-07-11:** selection is owned by Core `SelectionState` and shared
|
||||
by world/radar/inventory/paperdoll/toolbar/plugins; the former
|
||||
`GameWindow._selectedGuid` and inventory-local selected item are gone.
|
||||
7. Current cursor catalog exposes only `0x27..0x29`. The reachable global catalog
|
||||
also includes `0x01..0x06` and `0x0A..0x0F`.
|
||||
8. Current cursor resolution uses a modern static priority chain. Retail instead
|
||||
|
|
|
|||
|
|
@ -316,15 +316,24 @@ chrome and content together; full suite and user visual gate.
|
|||
|
||||
### 3.1 Centralize selection
|
||||
|
||||
- [ ] Add Core `SelectionState` with selected guid, previous guid, reason/source,
|
||||
- [x] Add Core `SelectionState` with selected guid, previous guid, reason/source,
|
||||
and one change event.
|
||||
- [ ] Route world pick, radar click, inventory selection, paperdoll selection,
|
||||
- [x] Route world pick, radar click, inventory selection, paperdoll selection,
|
||||
toolbar selected strip, target indicator, examine/use, and plugins through it.
|
||||
- [ ] Delete panel-local competing selection state except transient hover/row focus.
|
||||
- [ ] Add one App interaction-state owner for `None`, `Use`, `Examine`, and
|
||||
- [x] Delete panel-local competing selection state except transient hover/row focus.
|
||||
- [x] Add one App interaction-state owner for `None`, `Use`, `Examine`, and
|
||||
`UseItemOnTarget(sourceGuid)`; Core selection remains session truth while target
|
||||
mode remains UI orchestration state.
|
||||
|
||||
**Implementation (2026-07-11):** Core `SelectionState` ports
|
||||
`ACCWeenieObject::SetSelectedObject @ 0x0058C2E0`, including deduplication,
|
||||
previous/previous-valid tracking, object-removal clear, and plugin callbacks.
|
||||
World pick, radar, combat targeting, inventory, paperdoll slots, the selected
|
||||
toolbar strip, target indicator, use/pickup, PreviousSelection, and the new
|
||||
`IPluginHost.Selection` contract share that owner. `InteractionState` separately
|
||||
owns `None`/`Use`/`Examine`/`UseItemOnTarget`; `ItemInteractionController` now
|
||||
projects its pending source from that state. AP-58 retired.
|
||||
|
||||
### 3.2 Port item-use/drop legality into Core
|
||||
|
||||
- [ ] Translate `ItemHolder::UseObject`, `DetermineUseResult`, and
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue