fix(combat): restore retail combat bar controls
Resolve authored StringInfo labels from local.dat, port gmCombatUI's runtime option captions and checkbox widgets, and bind horizontal scrollbar media by retail structural roles so the green power jewel is a thumb instead of a tiled track. Persist the three combat options and make Auto Target govern target acquisition. Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
parent
2215c76c7e
commit
927fa7881a
19 changed files with 24591 additions and 48 deletions
|
|
@ -497,7 +497,7 @@ behavior. Estimated 17–26 days focused work, 3–5 weeks calendar.
|
|||
- **Wave 4.4e implemented — exact missile ammo number (live gate pending).** Pure Core reproduces retail's thrown-weapon-vs-separate-ammo resolution over the ordered player equipment list and its zero-to-one count normalization. Relevant equipment/stack events update authored missile indicator `0x10000194` with the DAT font. Warning-free Release build and 4,754-pass / 5-skip suite are green; AP-101 is retired.
|
||||
- **M2 held-object parenting shipped and live-gated 2026-07-11.** The combat toggle now ports `GetDefaultCombatMode` over ordered equipped contents, so a bow requests Missile instead of the old hardcoded Melee. CreateObject preserves parent/placement/timestamp fields, `0xF749` ParentEvent is handled, and `EquippedChildRenderController` renders the weapon as a separate child composed from the animated hand part + holding frame + child placement frame. Live gate passed: bow selected missile stance, rendered in-hand, followed animation, unequipped cleanly, and melee remained correct. App Release builds with zero warnings; the full 4,765-pass / 5-skip suite is green. AP-111 is retired; research: `docs/research/2026-07-11-combat-default-and-parent-event-pseudocode.md`.
|
||||
- **M2 local attack receive funnel implemented 2026-07-11; live gate pending.** Retail `ExecuteAttack` only sends the request; ACE chooses the concrete melee/missile action and returns it in a non-autonomous mt-0 `UpdateMotion`. The local branch now runs that state through the same constructor-defaulted `MoveToInterpretedState` funnel and 15-bit action-stamp gate as remotes, then applies sticky/long-jump tails. The old local-only direct `Commands[]` replay is deleted. Shared conversion lives in `InboundInterpretedMotionFactory`; research: `docs/research/2026-07-11-local-combat-motion-pseudocode.md`.
|
||||
- **M2 basic retail combat bar implemented 2026-07-11; live visual gate pending.** Production now mounts authored `gmCombatUI` LayoutDesc `0x21000073`, shows it only for Melee/Missile, binds authored high/medium/low buttons and desired-power slider, and routes mouse plus keyboard through one `CombatAttackController`. Matching retail x86 recovered exact 1.0-second normal and 0.8-second dual-wield power-up times; the same constants retire the guessed jump charge. Keybinding schema v3 migrates attack actions from one-shot Press to transition-emitting Hold. Research: `docs/research/2026-07-11-retail-combat-bar-pseudocode.md`; AP-24/AP-95 retired, AP-110 narrowed, AP-112 records the remaining advanced/Recklessness and command-interpreter seams.
|
||||
- **M2 basic retail combat bar implemented 2026-07-11; corrective live visual gate pending.** Production now mounts authored `gmCombatUI` LayoutDesc `0x21000073`, shows it only for Melee/Missile, binds authored high/medium/low buttons and desired-power slider, and routes mouse plus keyboard through one `CombatAttackController`. The corrective pass ports local.dat StringInfo labels, `gmCombatUI::PostInit` runtime option captions, interactive `UIOption_Checkbox` controls, and structural horizontal-scrollbar track/thumb roles. Matching retail x86 recovered exact 1.0-second normal and 0.8-second dual-wield power-up times; the same constants retire the guessed jump charge. Keybinding schema v3 migrates attack actions from one-shot Press to transition-emitting Hold. Research: `docs/research/2026-07-11-retail-combat-bar-pseudocode.md`; AP-24/AP-95 retired, AP-110 narrowed, AP-112 records the remaining advanced/Recklessness and command-interpreter seams.
|
||||
- **✓ 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.
|
||||
- **✓ SHIPPED — D.5.4 — Client object/item data model (foundation).** Shipped 2026-06-18 (`b506f53`..`a33e897`, 11 commits). Renamed `ItemRepository`→`ClientObjectTable` / `ItemInstance`→`ClientObject`; broadened the table to hold EVERY server object (retail `weenie_object_table` shape). `CreateObject` is now the canonical merge-upsert (`ClientObjectTable.Ingest`, retail `SetWeenieDesc` semantics) via a new Core.Net `ObjectTableWiring` (off GameWindow); `DeleteObject` evicts; `PlayerDescription` is a membership manifest (`RecordMembership`); live container-membership index (`GetContents`, retail `object_inventory_table`). `_liveEntityInfoByGuid` retired (selection/describe resolve from the one table). Root fix: the old enrich-existing-only `EnrichItem` dropped `CreateObject`s for items with no `PlayerDescription` stub — live-Coldeve 4/6 hotbar slots blank; items are now created, not dropped. **Crux resolved:** retail is TWO tables (`object_table` + `weenie_object_table`), NOT one — acdream's `WorldEntity` (3D system) + `ClientObjectTable` (data/UI) split was already architecturally faithful; the fix was the ingestion path, not a table unification. 2671 tests green.
|
||||
- **Roadmap correction (2026-07-10):** the completion order is now the architecture-first campaign in `docs/superpowers/plans/2026-07-10-retail-ui-fidelity-completion.md`. Retail `gmToolbarUI` is object-only: preserve `ShortCutData.index_`, `objectID_`, and `spellID_`, but do not invent spell glyphs on this bar. `PlayerModule::favorite_spells_[8]` feeds separate spell bars.
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
- `gmCombatUI::RecvNotice_AttackHeightChanged` `0x004CC080`
|
||||
- `gmCombatUI::RecvNotice_SetPowerbarLevel` `0x004CC0E0`
|
||||
- `gmCombatUI::RecvNotice_DesiredAttackPowerChanged` `0x004CC110`
|
||||
- `gmCombatUI::PostInit` `0x004CC1A0`
|
||||
- `gmCombatUI::ListenToElementMessage` `0x004CC430`
|
||||
- `gmCombatUI::RecvNotice_SetCombatMode` `0x004CC620`
|
||||
- `ClientCombatSystem::Begin` `0x0056A460`
|
||||
|
|
@ -16,6 +17,9 @@
|
|||
- `ClientCombatSystem::UseTime` `0x0056C1F0`
|
||||
- `ClientCombatSystem::HandleAttackDoneEvent` `0x0056C500`
|
||||
- `ClientCombatSystem::SetRequestedAttackHeight` `0x0056C8F0`
|
||||
- `UIOption_Checkbox::UIOption_Checkbox` `0x00486A50`
|
||||
- `UIOption_Checkbox::ListenToElementMessage` `0x004869A0`
|
||||
- `compute_str_hash` `0x00413110`
|
||||
- Matching retail executable disassembly (`acclient.exe` v11.4186 + PDB):
|
||||
`ATTACK_POWERUP_TIME = 1.0` seconds and
|
||||
`DUAL_WIELD_POWERUP_TIME = 0.8` seconds. The named pseudo-C's x87
|
||||
|
|
@ -133,3 +137,44 @@ attack power increase/decrease action:
|
|||
Both keyboard and retained buttons therefore use one transition-based attack
|
||||
controller. A one-shot click/keypress is not sufficient: down begins charging,
|
||||
up ends the request.
|
||||
|
||||
## Authored labels and option checkboxes
|
||||
|
||||
```text
|
||||
PostInit():
|
||||
repeat = child 0x10000053 as UIOption_Checkbox
|
||||
repeat.playerOption = AutoRepeatAttack
|
||||
repeat.label = StringInfo(hash("ID_CombatPanelOption_AutoRepeatAttack"))
|
||||
|
||||
autoTarget = child 0x10000054 as UIOption_Checkbox
|
||||
autoTarget.playerOption = AutoTarget
|
||||
autoTarget.label = StringInfo(hash("ID_CombatPanelOption_AutoTarget"))
|
||||
|
||||
keepInView = child 0x10000055 as UIOption_Checkbox
|
||||
keepInView.playerOption = ViewCombatTarget
|
||||
keepInView.label = StringInfo(hash("ID_CombatPanelOption_ViewCombatTarget"))
|
||||
```
|
||||
|
||||
The first two labels are deliberately not authored in LayoutDesc. They are
|
||||
assigned by `gmCombatUI::PostInit`, so a layout-only renderer must perform that
|
||||
controller binding. Ordinary Type-12 captions and High/Medium/Low use StringInfo
|
||||
property `0x17` and resolve through local.dat table `0x23000001`.
|
||||
|
||||
`UIOption_Checkbox` is a toggle `UIElement_Button`. Its visible 13x13 face is
|
||||
the authored child `0x10000328`; the label is embedded in the option object and
|
||||
starts after that face. Mouse release toggles attribute `0x0E` and immediately
|
||||
applies the associated player option.
|
||||
|
||||
## Horizontal scrollbar media roles
|
||||
|
||||
```text
|
||||
track = scrollbar DirectState image
|
||||
thumb = structural child element 1
|
||||
live power fill = nested meter child 0x10000050 / image child 2
|
||||
```
|
||||
|
||||
These roles are identified by authored element ids, never by resolved width.
|
||||
The combat thumb is a 12x14 green jewel; inheritance reflow can make its
|
||||
effective child geometry wide enough that a width heuristic incorrectly tiles
|
||||
the jewel as the background. The base track is `0x060074CA`, thumb
|
||||
`0x06001923`, and live red fill `0x06001200`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue