fix(ui): restore modern spell formula presentation
Resolve spell-examination component cells through their DAT icon DIDs, project scarab and prismatic-taper formulas when ACE disables component enforcement, and version authored window geometry so stale examination sizes reset once without losing user layout behavior. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
3e31b0ac70
commit
d4ce64f56b
22 changed files with 307 additions and 41 deletions
|
|
@ -166,6 +166,13 @@ component formula. It sends no Appraise request and takes no busy reference,
|
|||
so the status-bar magnifier remains object-only. Switching the shared floaty
|
||||
from a pending object appraisal to a spell releases exactly that appraisal
|
||||
transaction before showing the SpellPanel.
|
||||
The formula-media correction now passes the component's DAT icon DID to
|
||||
`GetSpellComponentIcon` instead of its WCID, so the authored cells render
|
||||
their actual scarab/taper images. ACE characters with component enforcement
|
||||
disabled show the modern scarab/prismatic formula rather than an inactive
|
||||
legacy recipe (IA-21). A per-window authored-geometry revision resets the
|
||||
obsolete saved 545-pixel examination height once to LayoutDesc's 310 x 400
|
||||
extent, preserving its position and all future retail-style user resizing.
|
||||
|
||||
**Files:** `src/AcDream.Core.Net/Messages/AppraiseInfoParser.cs`;
|
||||
`src/AcDream.App/UI/RetailUiRuntime.cs`;
|
||||
|
|
@ -196,6 +203,10 @@ dragging preserves that selection without activating the item.
|
|||
Left-pressing a favorite spell likewise changes its selection immediately.
|
||||
Right-clicking it opens the spell examination subview; selecting it and using
|
||||
the status-bar magnifier does not examine the spell.
|
||||
The formula contains only its power scarab(s) and prismatic taper(s) in the
|
||||
component-disabled ACE test profile, every component cell shows its DAT icon,
|
||||
and the first corrected open is 310 x 400 even when the old profile saved a
|
||||
545-pixel height.
|
||||
The Black Phyntos Hive specifically reads `Value: ???`, `Burden: Unknown`,
|
||||
then its description after one retail paragraph break, without the bogus
|
||||
255-item/255-container line.
|
||||
|
|
|
|||
|
|
@ -53,11 +53,12 @@ accepted-divergence entries (#96, #49, #50).
|
|||
| IA-12 | UI toolkit mirrors retail behavior from research docs, not a byte-port — keystone.dll is outside decomp coverage; observed constants embedded (drag 3 px, tooltip 1000 ms). Synthetic wrapper borders/whole-window drag regions use the exact DAT Type-2/Type-9 control cursors. `gmPanelUI` children are independently imported retained frames rather than one physical parent, but `RetailPanelUiController` owns their one canonical geometry and exclusive child lifecycle | `src/AcDream.App/UI/README.md:3`; `src/AcDream.App/UI/CursorFeedbackController.cs`; `src/AcDream.App/UI/Layout/RetailPanelUiController.cs` | keystone.dll has no PDB/decomp; semantics are reconstructed from retail UI deep-dives, named client methods, and production LayoutDesc media. Separate child wrappers preserve each LayoutDesc's content tree while typed move/resize synchronization gives all registered toolbar/detail children the same persistent parent rectangle | Edge-case low-level input semantics can differ silently even though outer geometry, visibility, and restore-previous ownership match | `UIElementManager::CheckCursor` 0x0045ABF0; `UIElement_Resizebar::StartMouseResizing` 0x0046B7E0; `UIElement_Dragbar::StartMouseMoving` 0x0046C760; `gmPanelUI::SetupChildren` 0x004BC9E0; docs/research/retail-ui/04-input-events.md |
|
||||
| IA-13 | GameEventType registry deliberately omits event types retail ignores; unknown events fall through unhandled | `src/AcDream.Core.Net/Messages/GameEventType.cs:11` | Retail also ignores them — dropping matches retail by construction | If the "retail ignores X" judgment is wrong for any opcode (or a server mod uses one), the event is silently dropped with no diagnostic pointing at the omission | retail GameEvent dispatch (ignored-event set) |
|
||||
| IA-14 | Rendering + dat-handling base is WorldBuilder's tested port, not a fresh retail-decomp port (Phase N.4/O design stance) | `docs/architecture/worldbuilder-inventory.md` (code at `src/AcDream.{Core,App}/Rendering/Wb/`) | WB visually verified on the AC world, MIT, same stack; known WB↔retail deltas resolved case-by-case — terrain split kept retail `FSplitNESW` (**#51**, pinned by `SplitFormulaDivergenceTest`), scenery drift accepted (AP-31) | A WB-upstream divergence not yet caught ships silently as "our" behavior; guard = the inventory doc's 🟢/🔴 split + per-formula divergence tests | retail decomp per algorithm; `tests/.../SplitFormulaDivergenceTest.cs` |
|
||||
| IA-15 | D.2b gameplay UI is our own `UiHost`/`UiRoot` retained tree, not a byte-port of Keystone. `RetailUiRuntime` owns the production import/mount graph; `RetailWindowManager`/typed handles centralize registry, raise, focus/capture cleanup, lifecycle events, reverse-order grouped controller teardown, removable Silk input subscriptions, schema-v2 per-character/per-resolution automatic layouts, and portable named `saveui/loadui` profiles; `RetailWindowFrame` is the single production/Studio mount contract for imported-chrome and shared-wrapper windows. Production LayoutDesc imports include vitals `0x2100006C`, chat `0x21000006`, toolbar `0x21000016`, character `0x2100002E`, inventory `0x21000023` plus mounted `0x21000024/22/21`, dialog catalog `0x2100003C`, radar `0x21000074`, and external container `0x21000008` (shared bevel plus a user-directed compact 700-pixel initial content width instead of its authored 800-pixel root). The dialog context/queue/callback lifecycle is now a named-client port; only its retained rendering remains under this Keystone adaptation. | `src/AcDream.App/UI/RetailUiRuntime.cs`; `src/AcDream.App/UI/RetailWindowManager.cs`; `src/AcDream.App/UI/RetainedPanelControllerGroup.cs`; `src/AcDream.App/UI/UiHost.cs`; `src/AcDream.App/UI/RetailWindowLayoutPersistence.cs`; `src/AcDream.App/UI/Layout/RetailWindowFrame.cs`; `src/AcDream.App/UI/Layout/RetailDialogFactory.cs`; `src/AcDream.App/UI/Layout/RetailConfirmationDialogView.cs`; `src/AcDream.App/UI/Layout/ExternalContainerController.cs`; `src/AcDream.App/UI/Layout/LayoutImporter.cs`; binding supply in `GameWindow.cs` | Keystone has no matching PDB/decomp, so we preserve its observable ElementDesc/state/input behavior from DAT, named client call sites, and live evidence while using modern retained ownership. Real RenderSurfaces and imported element geometry remain the visual oracle; the external strip's initial width follows the connected visual direction and remains horizontally resizable to its authored extent or the viewport edge. | Persistence and low-level widget rendering are behaviorally reconstructed from retail semantics rather than a Keystone byte-port; lifecycle edge cases remain constrained by conformance tests. The external strip opens 100 pixels narrower than the raw LayoutDesc before user/persistence resizing. | Production LayoutDesc objects; `DialogFactory @ 0x004773C0..0x00478470`; `gmExternalContainerUI @ 0x004CBAD0..0x004CBFE0`; `docs/research/2026-07-13-retail-dialog-factory-pseudocode.md`; Keystone behavior notes in `docs/research/retail-ui/` |
|
||||
| IA-15 | D.2b gameplay UI is our own `UiHost`/`UiRoot` retained tree, not a byte-port of Keystone. `RetailUiRuntime` owns the production import/mount graph; `RetailWindowManager`/typed handles centralize registry, raise, focus/capture cleanup, lifecycle events, reverse-order grouped controller teardown, removable Silk input subscriptions, schema-v2 per-character/per-resolution automatic layouts with per-window authored-geometry revisions, and portable named `saveui/loadui` profiles; `RetailWindowFrame` is the single production/Studio mount contract for imported-chrome and shared-wrapper windows. Production LayoutDesc imports include vitals `0x2100006C`, chat `0x21000006`, toolbar `0x21000016`, character `0x2100002E`, inventory `0x21000023` plus mounted `0x21000024/22/21`, dialog catalog `0x2100003C`, radar `0x21000074`, and external container `0x21000008` (shared bevel plus a user-directed compact 700-pixel initial content width instead of its authored 800-pixel root). The dialog context/queue/callback lifecycle is now a named-client port; only its retained rendering remains under this Keystone adaptation. | `src/AcDream.App/UI/RetailUiRuntime.cs`; `src/AcDream.App/UI/RetailWindowManager.cs`; `src/AcDream.App/UI/RetainedPanelControllerGroup.cs`; `src/AcDream.App/UI/UiHost.cs`; `src/AcDream.App/UI/RetailWindowLayoutPersistence.cs`; `src/AcDream.App/UI/Layout/RetailWindowFrame.cs`; `src/AcDream.App/UI/Layout/RetailDialogFactory.cs`; `src/AcDream.App/UI/Layout/RetailConfirmationDialogView.cs`; `src/AcDream.App/UI/Layout/ExternalContainerController.cs`; `src/AcDream.App/UI/Layout/LayoutImporter.cs`; binding supply in `GameWindow.cs` | Keystone has no matching PDB/decomp, so we preserve its observable ElementDesc/state/input behavior from DAT, named client call sites, and live evidence while using modern retained ownership. Real RenderSurfaces and imported element geometry remain the visual oracle; the external strip's initial width follows the connected visual direction and remains horizontally resizable to its authored extent or the viewport edge. | Persistence and low-level widget rendering are behaviorally reconstructed from retail semantics rather than a Keystone byte-port; lifecycle edge cases remain constrained by conformance tests. The external strip opens 100 pixels narrower than the raw LayoutDesc before user/persistence resizing. | Production LayoutDesc objects; `DialogFactory @ 0x004773C0..0x00478470`; `gmExternalContainerUI @ 0x004CBAD0..0x004CBFE0`; `docs/research/2026-07-13-retail-dialog-factory-pseudocode.md`; Keystone behavior notes in `docs/research/retail-ui/` |
|
||||
| IA-17 | Toolbar chrome is toolkit-supplied through the central `RetailWindowFrame` mount (`UiCollapsibleFrame` 8-piece bevel) because LayoutDesc `0x21000016` carries no baked frame. It also supports a toolkit-defined collapse-to-one-row (bottom-edge resize snapping between a row-1-only and a two-row height, row-2 visibility tied to the stop) — retail's real collapse is keystone.dll (no decomp) and the DAT stacks both rows always. | `src/AcDream.App/UI/Layout/RetailWindowFrame.cs`; `src/AcDream.App/UI/UiCollapsibleFrame.cs`; toolbar policy in `GameWindow.cs`; spec: `docs/superpowers/specs/2026-06-20-d2b-toolbar-collapse-design.md` | The central mount now owns wrapper geometry/registration uniformly; border-over-content prevents the row-2 right cap from poking through | The collapse stops remain a toolkit reconstruction rather than a byte-port of Keystone behavior | gmToolbarUI WM chrome (keystone.dll, no PDB); no bevel ids in LayoutDesc 0x21000016 (toolbar dump) |
|
||||
| IA-18 | Effect overlay tile (enum 0x10000005) is a `ReplaceColor` SURFACE SOURCE — pure-white pixels in the composited drag icon are replaced PER-PIXEL with the same (x,y) pixel of the effect tile (the SURFACE overload `SurfaceWindow::ReplaceColor` 0x004415b0), preserving the tile's texture/gradient; the tile itself is NOT blitted as an additional layer. This IS faithful retail behavior. **Anti-regression: do NOT re-implement this as a blit layer NOR as a flat-color replace (it is a per-pixel surface copy).** | `src/AcDream.App/UI/IconComposer.cs` (`ReplaceWhiteFromSurface`) | Faithful port of `IconData::RenderIcons` @407614 → the SURFACE overload `ReplaceColor` 0x004415b0 (`dst[x,y]=src[x,y]` where `dst==white`); confirmed via clean Ghidra decompile + named decomp + visual (the Energy Crystal's blue is a gradient, 2026-06-17). | A blit-layer or flat-color re-implementation would show the wrong effect look (no gradient) — the visual-verification regression that retired the mean-color approximation | `IconData::RenderIcons` acclient_2013_pseudo_c.txt:407524; `ReplaceColor` SURFACE overload 0x004415b0:71656; `docs/research/2026-06-17-stateful-icon-RESOLVED.md` |
|
||||
| IA-19 | Automatic combat acquisition is narrowed to attackable non-player monsters. Retail `AutoTarget` falls back to `SelectNext(SELECTION_TYPE_COMPASS_ITEM)`, whose combat filter can also admit attackable enemy players in compatible PK states. | `src/AcDream.Core/Combat/CombatTargetPolicy.cs`; consumers `src/AcDream.App/Interaction/WorldSelectionQuery.cs` (`IsHostileMonster`/`FindClosestHostileMonster`) and `SelectionInteractionController.cs` (`SelectClosestCombatTarget`) | Explicit product direction: Auto Target must never select NPCs, players, pets, or other objects; manual player-selection commands remain available | In PK play, Auto Target will not acquire an otherwise valid hostile player as retail would; the player must be selected manually | `ClientCombatSystem::AutoTarget @ 0x0056BC80`; `CPlayerSystem::SelectNext @ 0x0055F9A0`; `ClientCombatSystem::ObjectIsAttackable @ 0x0056A600` |
|
||||
| IA-20 | The basic combat bar keeps dark-red media `0x0600715E` visible as the centered middle baseline. Retail skill-gates field `0x100005EF` to trained Recklessness; the separate bright child remains faithful live `SetPowerbarLevel` feedback from the absolute left edge. | `src/AcDream.App/UI/UiScrollbar.cs`; child-policy extraction in `src/AcDream.App/UI/Layout/DatWidgetFactory.cs` | Explicit connected visual direction: the dark middle track remains present behind live attack charge; the exact skill-gated treatment remains tracked by AP-112 | Untrained characters retain the dark-red baseline where retail may leave only the gray track; trained/untrained Recklessness presentation is not distinguishable | `gmCombatUI::RecvNotice_SetPowerbarLevel @ 0x004CC0E0`; `gmCombatUI::ListenToElementMessage @ 0x004CC430`; LayoutDesc `0x21000073` |
|
||||
| IA-21 | When ACE sends player BoolProperty `68` (`SpellComponentsRequired`) false, acdream presents the retail scarab/prismatic-taper formula even without a directly carried school focus. With component enforcement enabled, retail's exact focus/infusion versus account-customized selection remains intact. | `src/AcDream.App/Spells/SpellComponentRequirementService.cs` | A component-disabled server has no actionable legacy recipe; explicit product direction is that this client/server mode uses the modern scarab/taper component presentation | A custom server could expect retail's legacy recipe to remain visible even though casting consumes no components | `ClientMagicSystem::AreSpellComponentsRequired @ 0x00567B90`; `ClientMagicSystem::GetAppropriateSpellFormula @ 0x00567D50`; `CSpellBase::InqScarabOnlyFormula @ 0x00597050` |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -44,8 +44,12 @@ selects without waiting for release; right-click opens the examination
|
|||
floaty's authored SpellPanel locally; and spell IDs never enter global object
|
||||
selection or the status-bar magnifier path. The view projects retail's
|
||||
name/school/mana/duration/range/description and authored appropriate-formula
|
||||
component cells, including live missing-component state. Its connected visual
|
||||
gate is pending. Slice 3's first connected gate exposed
|
||||
component cells, including live missing-component state. The follow-up resolves
|
||||
those cells through the component icon DID, shows the modern
|
||||
scarab/prismatic formula for ACE characters whose component enforcement is
|
||||
disabled (IA-21), and one-time migrates stale saved examination dimensions to
|
||||
the authored 310 x 400 extent while preserving retail-style later resizing.
|
||||
Its connected visual gate is pending. Slice 3's first connected gate exposed
|
||||
three deeper defects, now corrected: the response enum mislabeled ACE's
|
||||
creature bit `0x0100` and dropped monster packets while retaining the busy
|
||||
cursor; the imported 310 x 400 examination layout was incorrectly hosted as
|
||||
|
|
|
|||
|
|
@ -112,8 +112,12 @@ Favorite spells now use the parallel retail catalog contract: MouseDown
|
|||
selects immediately, right-click opens the examination floaty's authored
|
||||
SpellPanel locally, and no spell ID enters global object selection or the
|
||||
status-bar magnifier command. Exact spell fields and the current appropriate
|
||||
formula's authored component cells are automated; the connected visual gate
|
||||
is pending.
|
||||
formula's authored component cells are automated. The correction resolves
|
||||
component icons by DAT DID, projects scarabs/prismatic tapers when ACE
|
||||
disables component enforcement (IA-21), and resets obsolete saved dimensions
|
||||
once to the authored 310 x 400 extent. The Release build with #228's 17
|
||||
tracked test warnings, 3,555 App tests / 3 skips, and 7,986 complete-solution
|
||||
tests / 5 skips pass; the connected visual gate is pending.
|
||||
Slice 3's first connected gate found and corrected the real remaining defects:
|
||||
ACE creature flag `0x0100` was mislabeled as a weapon profile and left the
|
||||
appraisal busy reference held, retail's examination UI had been mounted as a
|
||||
|
|
|
|||
|
|
@ -9,7 +9,11 @@ corrected and live-confirmed. The creature-page follow-up now has retail's
|
|||
ordered stat rows and animated private preview. The follow-up item report and
|
||||
authored 310 x 400 layout correction are implemented; their connected visual
|
||||
gate is pending. Favorite-spell press-time selection and right-click local
|
||||
SpellPanel examination are implemented and await their connected visual gate.
|
||||
SpellPanel examination are implemented. The follow-up now maps
|
||||
component-disabled ACE characters to the modern scarab/prismatic formula,
|
||||
resolves formula icons by their DAT icon DIDs, and migrates stale examination
|
||||
dimensions once to the authored 310 x 400 extent. Their connected visual gate
|
||||
is pending.
|
||||
**Milestone:** M4 prerequisite/preamble.
|
||||
**Architecture:** retained gameplay UI over shared selection, object, and
|
||||
interaction state. `GameWindow` remains a composition/callback shell.
|
||||
|
|
@ -257,9 +261,15 @@ on left press, while the spell branch of
|
|||
SpellPanel locally on right-click. Spell IDs never enter `SelectionState` or
|
||||
the status-bar magnifier path, and no Appraise/busy transaction is invented.
|
||||
The view projects exact spell fields and the current appropriate formula
|
||||
through authored component template `0x1000032E`; its connected visual gate is
|
||||
pending. The 61-test focused App gate, Release solution build, 3,553 App tests
|
||||
/ 3 skips, and 7,984 complete-solution tests / 5 skips pass.
|
||||
through authored component template `0x1000032E`. Its corrective pass uses
|
||||
each component descriptor's icon DID rather than its inventory WCID, applies
|
||||
the modern scarab/prismatic formula when ACE disables component enforcement
|
||||
(IA-21), and introduces per-window authored-geometry revisions so an obsolete
|
||||
saved examination height resets once without discarding position or future
|
||||
user resizing. The connected visual gate is pending. The 61-test focused App
|
||||
gate passed before this correction. The corrected focused tests, Release build
|
||||
with #228's 17 tracked test warnings, 3,555 App tests / 3 skips, and 7,986
|
||||
complete-solution tests / 5 skips pass.
|
||||
|
||||
## Slice 1 — spell-bar overflow arrows
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,37 @@ favorite-component events use WCIDs, resolved through DualEnumIDMap
|
|||
`0x27000002`. The magic-school focus map is retail enum category `0x28`, key
|
||||
`0x10000001`.
|
||||
|
||||
ACE integration policy (IA-21): when player BoolProperty `68`
|
||||
(`SpellComponentsRequired`) is false, casting already bypasses component
|
||||
preflight. In that server mode acdream also projects
|
||||
`InqScarabOnlyFormula` in Spell examination, so an inactive account-randomized
|
||||
legacy recipe is not exposed. With enforcement enabled, the focus/infusion
|
||||
branch above remains byte-for-byte retail.
|
||||
|
||||
### Spell-examination formula media and extent
|
||||
|
||||
Retail: `SpellExamineUI::ExamineSpell` `0x004B6210`,
|
||||
`gmFloatyExaminationUI::UpdateFromPlayerModule` `0x004D4250`, and LayoutDesc
|
||||
`0x2100006B`.
|
||||
|
||||
```text
|
||||
for each SCID in GetAppropriateSpellFormula(spell):
|
||||
component = SpellComponentTable[SCID]
|
||||
texture = GetSpellComponentIcon(component.iconDID)
|
||||
cell = AddItemFromTemplateList(0x1000032E)
|
||||
cell.icon = texture
|
||||
cell.missingOverlay = !ComponentTracker.ComponentIsOwned(component.WCID)
|
||||
|
||||
initial floaty extent = LayoutDesc root extent = 310 x 400 pixels
|
||||
restore user position/size from player UI state
|
||||
```
|
||||
|
||||
The component icon lookup consumes the component's icon DID, not its WCID.
|
||||
acdream's retained-layout persistence carries an authored-geometry revision:
|
||||
when a shipped LayoutDesc extent correction invalidates an older saved size,
|
||||
only width/height reset once; position, visibility, and later user resizing
|
||||
remain persistent like retail.
|
||||
|
||||
## Authoritative enchantment events
|
||||
|
||||
Retail dispatchers:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue