feat(ui): Phase D.2a — VitalsPanel wired into GameWindow + backend pivot
Closes Phase D.2a. Launch with ACDREAM_DEVTOOLS=1 now shows a live
ImGui "Vitals" window whose HP bar reads CombatState.GetHealthPercent
for the local player. Without the env var the branches are dead code,
no ImGui context is created, and behaviour is identical to before.
GameWindow hunks:
- fields: _imguiBootstrap / _panelHost / _vitalsVm + DevToolsEnabled
- init (OnLoad): construct bootstrap + host, register VitalsPanel
- GUID push: _vitalsVm?.SetLocalPlayerGuid(chosen.Id) at live-connect
- frame begin: _imguiBootstrap.BeginFrame(dt) after GL clear
- frame end: _panelHost.RenderAll(ctx) + _imguiBootstrap.Render() after debug overlay
- input gating: skip WASD when ImGui.GetIO().WantCaptureKeyboard
Backend pivot: Hexa.NET.ImGui → ImGui.NET + Silk.NET.OpenGL.Extensions.ImGui.
First-light integration with the Hexa backend crashed 0xC0000005 inside
Hexa.NET.ImGui.Backends.OpenGL3.ImGuiImplOpenGL3.InitNative. Root cause:
Hexa's native OpenGL3 backend resolves GL function pointers via GLFW or
SDL internally; with Silk.NET (which uses neither) the pointers are null
and the native code crashes on first use. The mitigation path was
already planned — the design doc's Risk section called a pivot to
ImGui.NET a "one-morning operation" — and that's exactly what happened.
- Packages: Hexa.NET.ImGui 2.2.9 + Hexa.NET.ImGui.Backends 1.0.18
→ ImGui.NET 1.91.6.1 + Silk.NET.OpenGL.Extensions.ImGui 2.23.0
- ImGuiBootstrapper: was static Initialize(gl)+Shutdown() wrapping
Hexa's OpenGL3 init; now an IDisposable wrapping Silk.NET's
ImGuiController instance which handles GL backend init + input
subscription in one go.
- SilkInputBridge.cs deleted (~190 LOC): ImGuiController subscribes
IKeyboard / IMouse events itself, we don't need a bespoke bridge.
- ImGuiPanelRenderer: ImGuiNET.ImGui.* calls instead of
Hexa.NET.ImGui.ImGui.*. Widget surface unchanged.
Boundary discipline is preserved — no panel imports ImGuiNET; only
ImGuiPanelRenderer does. The D.2b custom toolkit will implement the
same IPanelRenderer contract without touching panel code.
Out of scope (tracked for follow-up):
- Stam/Mana currently return float? null (VitalsVM). Absolute values
need LocalPlayerState + PlayerDescription (0x0013) parsing to be
stored rather than discarded — filed as a post-D.2a issue.
- Mouse-capture gating (WorldMouseFallThrough-style click-through
tests) — not needed until we add clickable inventory items.
Roadmap + memory + architecture doc + UI framework plan updated in the
same commit per CLAUDE.md roadmap-discipline rules. 753 tests pass
(550 Core + 192 Core.Net + 11 new UI.Abstractions), 0 build warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a7dbce3474
commit
55aaca7a14
13 changed files with 218 additions and 275 deletions
|
|
@ -46,6 +46,7 @@
|
|||
| G.1+ | Full sky visuals + weather + dynamic-light shader — SkyDescLoader parses Region 0x13000000 dat keyframes with retail fog fields (start/end/mode); WeatherSystem picks Clear/Overcast/Rain/Snow/Storm deterministically per in-game day with 10s fade; SkyRenderer draws far-plane-1e6 celestial meshes with UV scroll; SceneLightingUbo binds at std140 location=1 with 8 Light slots + fog + lightning flash; terrain.vert + mesh.frag + mesh_instanced.frag + sky.frag all consume the shared UBO; LightingHookSink auto-registers Setup.Lights per entity + flips IsLit on SetLightHook; ParticleRenderer renders rain/snow billboards; F7 cycles day time override, F10 cycles weather; WorldSession surfaces server time via ServerTimeUpdated (ConnectRequest + TimeSync flag) | Tests ✓ |
|
||||
| H.1 | Chat wire layer — Talk (0x0015) / Tell (0x005D) / ChatChannel (0x0147) outbound, HearSpeech (0x02BB local + 0x02BC ranged) inbound, ChatLog ring buffer with adapters for every chat source | Tests ✓ |
|
||||
| Glue | GameEventWiring.WireAll — single-call registration mapping parsed GameEvents → Core state classes (ChatLog, CombatState, Spellbook, ItemRepository); GameWindow exposes state classes + wires them to live session | Tests ✓ |
|
||||
| D.2a | UI scaffold — `AcDream.UI.Abstractions` stable contract (`IPanel` / `IPanelHost` / `IPanelRenderer` / `ICommandBus` + `VitalsVM` / `VitalsPanel`); `AcDream.UI.ImGui` backend on ImGui.NET + `Silk.NET.OpenGL.Extensions.ImGui` (pivoted from Hexa.NET.ImGui on 2026-04-25 — Hexa's native OpenGL3 backend resolves GL via GLFW/SDL and crashed 0xC0000005 without them); VitalsPanel wired into GameWindow behind `ACDREAM_DEVTOOLS=1` with `ImGui.WantCaptureKeyboard` WASD suppression. 11 new tests. | Live ✓ |
|
||||
|
||||
Plus polish that doesn't get its own phase number:
|
||||
- FlyCamera default speed lowered + Shift-to-boost
|
||||
|
|
@ -126,9 +127,11 @@ Plus polish that doesn't get its own phase number:
|
|||
> [`docs/plans/2026-04-24-ui-framework.md`](2026-04-24-ui-framework.md)
|
||||
> for the full design. Short version:
|
||||
>
|
||||
> 1. **D.2a — Hexa.NET.ImGui as the short-term backend.** Wire up in days,
|
||||
> 1. **D.2a — ImGui as the short-term backend.** Wire up in days,
|
||||
> iterate game logic (chat-send, inventory actions, vitals HUD reading
|
||||
> real state) in weeks. Looks like a debugger; that's fine.
|
||||
> real state) in weeks. Looks like a debugger; that's fine. *(Shipped
|
||||
> 2026-04-25 on ImGui.NET + `Silk.NET.OpenGL.Extensions.ImGui` after a
|
||||
> day-one pivot away from Hexa.NET.ImGui; see shipped table.)*
|
||||
> 2. **Stable `AcDream.UI.Abstractions` layer** — ViewModels + Commands +
|
||||
> `IPanel` / `IPanelRenderer` interfaces. Backend-agnostic. Plugin API
|
||||
> publishes against this layer and never sees ImGui.
|
||||
|
|
@ -142,7 +145,7 @@ Plus polish that doesn't get its own phase number:
|
|||
|
||||
**Sub-pieces:**
|
||||
- **D.1 — 2D ortho overlay + font rendering.** ✅ SHIPPED 2026-04-17 as the dev-facing debug overlay (StbTrueTypeSharp system-font atlas + `TextRenderer` + `DebugOverlay`).
|
||||
- **D.2a — Hexa.NET.ImGui scaffold + `AcDream.UI.Abstractions` layer.** NEW pre-piece introduced 2026-04-24. Wires Hexa.NET.ImGui as the short-term backend behind `ACDREAM_DEVTOOLS=1`. Defines `IPanel` / `IPanelHost` / `IPanelRenderer` / `ICommandBus` + the first ViewModels (`VitalsVM` etc.) in the new `AcDream.UI.Abstractions` project. First real panel: `VitalsPanel` reading HP/stam/mana from `IGameState`. This is what gets game-logic iteration moving; looks like a debugger, acceptable.
|
||||
- **✓ SHIPPED — D.2a — ImGui scaffold + `AcDream.UI.Abstractions` layer.** Shipped 2026-04-25. Wires ImGui as the short-term backend behind `ACDREAM_DEVTOOLS=1`. Defines `IPanel` / `IPanelHost` / `IPanelRenderer` / `ICommandBus` + the first ViewModel (`VitalsVM`) in the new `AcDream.UI.Abstractions` project. First real panel: `VitalsPanel` reading HP from `CombatState.GetHealthPercent`. **Backend pivoted Hexa.NET.ImGui → ImGui.NET + `Silk.NET.OpenGL.Extensions.ImGui` during integration** — Hexa's native OpenGL3 backend does its own GL function resolution via GLFW/SDL and crashed with `0xC0000005` in `ImGuiImplOpenGL3.InitNative` against Silk.NET (no GLFW/SDL present). The Silk.NET extension is purpose-built for this scenario and is the `ImGui.NET` mitigation path that `docs/plans/2026-04-24-ui-framework.md` already called out as a "one-morning operation". Stam/Mana return `float?` null in D.2a because absolute values need `LocalPlayerState` + `PlayerDescription (0x0013)` parsing (filed post-D.2a). 11 new `AcDream.UI.Abstractions.Tests` green.
|
||||
- **D.2b — Custom retail-look backend.** Implements the same `IPanel` / `IPanelRenderer` contracts using a custom retained-mode toolkit sourced from retail dat assets. Requires D.2a shipped. Panels get reskinned one at a time; ImGui stays as the `ACDREAM_DEVTOOLS=1` overlay forever. The original 2026-04-17 scaffold research (`UiRoot` / `UiElement` / `UiPanel` / `UiHost` + retail event codes + focus / drag-drop state machine + `WorldMouseFallThrough`) is the implementation foundation here — see `docs/research/retail-ui/`.
|
||||
- **D.3 — AcFont from portal.dat.** Replace stb_truetype system font with retail `Font` DBObjs (`0x40000000..0x40000FFF`) baked from `RenderSurface` source sheets — see research slice 03 §4. Preserves retail visual identity. **(D.2b dependency — needs the custom renderer.)**
|
||||
- **D.4 — Dat sprites + 9-slice panel backgrounds.** Load `RenderSurface` (`0x06xxxxxx`) as GL textures; add `DrawSprite` to `UiRenderContext`. Enables retail panel art. **(D.2b dependency.)**
|
||||
|
|
|
|||
|
|
@ -1,13 +1,51 @@
|
|||
# UI framework plan
|
||||
|
||||
**Date:** 2026-04-24
|
||||
**Status:** design — not yet implemented
|
||||
**Date:** 2026-04-24 (design), shipped 2026-04-25
|
||||
**Status:** **Phase D.2a shipped** — `AcDream.UI.Abstractions` + ImGui backend
|
||||
+ `VitalsPanel` gated on `ACDREAM_DEVTOOLS=1`. Backend pivoted from
|
||||
`Hexa.NET.ImGui` to `ImGui.NET` + `Silk.NET.OpenGL.Extensions.ImGui` during
|
||||
first-light integration — see the pivot note below. Phase D.2b (custom
|
||||
retail-look backend) remains design-only.
|
||||
**Owner:** lead engineer (erik) + Claude
|
||||
|
||||
Captures the UI strategy agreed via discussion on 2026-04-24. Documents
|
||||
the choices AND the alternatives considered so future sessions can
|
||||
re-evaluate with the same context.
|
||||
|
||||
## 2026-04-25 pivot: Hexa.NET.ImGui → ImGui.NET
|
||||
|
||||
The original choice (documented below) was `Hexa.NET.ImGui` +
|
||||
`Hexa.NET.ImGui.Backends.OpenGL3`. It did not survive first-light
|
||||
integration:
|
||||
|
||||
- First launch with Hexa's backend crashed with `0xC0000005` inside
|
||||
`Hexa.NET.ImGui.Backends.OpenGL3.ImGuiImplOpenGL3.InitNative`.
|
||||
- Root cause: Hexa's native OpenGL3 backend does its own GL function
|
||||
resolution, looking up symbols via GLFW or SDL. Silk.NET uses neither,
|
||||
so the resolved function pointers were null and the native code
|
||||
dereferenced them on init.
|
||||
- Hexa's Silk.NET examples rely on GLFW being co-loaded (its default
|
||||
on Hexa's own scenes) — not applicable here.
|
||||
|
||||
**Mitigation path** was already written into this doc (§"What we give
|
||||
up": *"switching to ImGui.NET later is a one-morning operation if Hexa
|
||||
misbehaves"*) and taken:
|
||||
|
||||
- Packages swapped → `ImGui.NET 1.91.6.1` + `Silk.NET.OpenGL.Extensions.ImGui 2.23.0`.
|
||||
- `Silk.NET.OpenGL.Extensions.ImGui.ImGuiController` handles the whole
|
||||
integration (GL backend init against the Silk.NET GL binding,
|
||||
keyboard + mouse IO event subscription). No hand-written input
|
||||
bridge needed.
|
||||
- `ImGuiBootstrapper` is a ~10-line `IDisposable` wrapping the
|
||||
`ImGuiController` instance. `ImGuiPanelRenderer` wraps `ImGuiNET.ImGui.*`.
|
||||
- Boundary discipline preserved — panels never import `ImGuiNET`
|
||||
directly; they only use `IPanelRenderer`. The backend swap is
|
||||
invisible above the abstraction layer, as designed.
|
||||
|
||||
Sections below from §"Choice: Hexa.NET.ImGui" onward are kept as the
|
||||
historical design reasoning. They remain useful if we ever re-evaluate
|
||||
native AOT / upstream-tracking tradeoffs.
|
||||
|
||||
## Goal
|
||||
|
||||
acdream needs a playable game UI: chat, vitals HUD, inventory, character
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue