fix #391: curated modern-only resolution list from the monitor's modes

User-directed (2026-08-13): "we should only support modern resolutions.
Not any old format." New DisplayModeCatalog enumerates the window's
monitor (Silk IMonitor.GetAllVideoModes) once at GameWindow load and
curates via a pure, tested rule: modern widescreen families only
(16:9/16:10/21:9/32:9 within 2.5%), at least 1280 wide, must fit the
desktop (an impossible windowed pick is not offered - the measured
3840x2160-on-2560x1440 silent clamp class), desktop mode always
included, refresh-rate duplicates collapsed, ascending order.

The Config Resolution row consumes the catalog through two new optional
Bind parameters; its Defaults value becomes the desktop's own mode.
Fixture/headless callers keep the static preset ladder, which now drops
800x600 and is pinned by test to pass the same curation rule (the OP6 S4
"default must be re-selectable" invariant holds on both paths).

Deliberate retail deviation, register row IA-22: retail listed the
adapter's complete enumeration including 4:3 legacy modes and authored
800x600 as the Config default (gmConfigUI::InitOptions
SetDefaultValue(0x03200258); gmClient::Init @0x004047af). The catalog is
also the designated fullscreen mode-switch validation source for
#376/#388 - an offered mode is supported by construction.

Tests: DisplayModeCatalogTests (8 - filter/clamp/dedupe/sort/ultrawide/
desktop-inclusion/fallback-consistency); ConfigOptionsPageControllerTests
row-12 default updated. App suite 4,961/3 skips; UI.Abstractions 916.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-13 17:16:09 +02:00
parent 7e0c130344
commit 13d388e5a9
9 changed files with 324 additions and 23 deletions

View file

@ -26,7 +26,21 @@ What does NOT go here:
## #391 — Resolution list: offer only modern modes from the monitor's real mode list (user-directed curation)
**Status:** OPEN — filed 2026-08-13, user-directed ("we should only
**Status:** DONE 2026-08-13 (this commit) — display block slice 2, pending
the user's gate. `DisplayModeCatalog` (App/Rendering) enumerates the
window's monitor via Silk (`IMonitor.GetAllVideoModes`), curates through
the pure `Curate` rule (modern families 16:9/16:10/21:9/32:9 ±2.5%,
≥1280 wide, fits the desktop, desktop mode always included, refresh-rate
duplicates collapsed, ascending), and is installed once at `GameWindow`
load. The Config Resolution row takes the curated list + the desktop-mode
Defaults value through two new optional `Bind` parameters; fixture
callers keep the static ladder (800x600 now removed from it — the ladder
itself passes the curation rule, pinned by test). Register row IA-22
carries the deliberate deviation (retail listed every adapter mode and
authored 800x600 as the default). The same catalog is the designated
mode-validation source for #376/#388. Original filing below.
**Original filing:** OPEN — filed 2026-08-13, user-directed ("we should only
support modern resolutions. Not any old format"). Today's Resolution
dropdown offers a list that includes legacy 4:3 modes (800x600 was
pickable) and modes the desktop cannot host (3840x2160 on a 2560x1440

View file

@ -37,7 +37,7 @@ accepted-divergence entries (#96, #49, #50).
---
## 1. Intentional architecture (IA) — 18 active rows
## 1. Intentional architecture (IA) — 19 active rows (IA-22 filed 2026-08-13 — the #391 user-directed modern-only curated resolution list + desktop-mode default, replacing retail's full adapter enumeration + authored 800x600 default)
| # | Divergence | Where (file:line) | Why it is safe / justified | Risk if assumption breaks | Retail oracle |
|---|---|---|---|---|---|
@ -59,6 +59,7 @@ accepted-divergence entries (#96, #49, #50).
| 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`). This row is auto-acquisition-only: as of #298, explicit-target admission and the combat camera route through the separate, retail-exact `WorldSelectionQuery.IsAttackableTarget` (`ObjectIsAttackable`-backed) instead, so a compatible-PK player is a valid manual attack/camera target — do not assume one predicate still serves both concerns. | 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` |
| IA-22 | **Filed 2026-08-13 (#391, user-directed: "we should only support modern resolutions. Not any old format").** The Config Resolution dropdown offers a CURATED list — the monitor's real mode enumeration filtered to modern widescreen families (16:9/16:10/21:9/32:9, ≥1280 wide, fitting the desktop; `DisplayModeCatalog.Curate`) — and its Defaults value is the desktop's own mode. Retail offered the adapter's complete enumeration including 4:3 legacy modes and authored `800x600` as the row default (`gmConfigUI::InitOptions SetDefaultValue(0x03200258)`; `gmClient::Init @0x004047af` `Device::ForceDisplayResolution(1, 0x320, 0x258)`). | `src/AcDream.App/Rendering/DisplayModeCatalog.cs`; `src/AcDream.App/UI/Layout/ConfigOptionsPageController.cs` (Resolution row); fixture fallback `src/AcDream.UI.Abstractions/Panels/Settings/DisplaySettings.cs` (`AvailableResolutions`, 800x600 removed) | Explicit product direction; the curated list is also the fullscreen mode-switch validation source (#376/#388), so an offered mode is supported by construction — "Graphics mode not supported" crashes become unreachable from the dropdown. | A user wanting a genuine legacy 4:3 mode cannot pick it; retail-parity comparisons of the Config tab's list/default will show the deviation. | decomp sites in the Divergence column; ISSUES #391 |
---