MONKEY (integration): merge graphics-programme-presets
Graphics Preset ladder + Render Distance (farclip ceiling 1497). Conflict in options_tests: the Advanced page now has 33 rows (preset + render distance lead, then the 31 programme rows). The three round-3 rows join the ladder: ambientOcclusion 0/0/1/2/2, zoneSkyboxes 0/0/1/1/1, lampFog 0/0/1/2/2 (Classic/Low/Medium/High/Ultra); the preset tooltip names them (EN+RU). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+45
-1
@@ -38,9 +38,53 @@ behind each constant.
|
||||
| Zone skyboxes | the painted sky a zone's `LightParams` names (clear-weather slot, storm slot lerped by the storm blend) for the living, fading in by the Light sphere falloff with the modern client's crossfade (ghost > WMO > zone); extended `LightSkybox.dbc` flags `0x1` day cycle, `0x2` keep sun/moon/stars/clouds, `0x4` fog-colour horizon cone, plus a celestial second model; every skybox now plays its bones, texture transforms, colour and alpha tracks (`benilla-world/src/skybox.rs`, `skybox_anim.rs`, `benilla-formats/src/light/skybox.rs`) | Advanced Graphics → Zone Skyboxes, cvar `zoneSkyboxes` (0 Off default, 1 On; High = 1), env `WOW_ZONE_SKYBOXES=0\|1` |
|
||||
| Night and interior level | global dimming of the night sky term and of interior ambient | `nightGain`, `interiorGain`, `interiorBakeFloor` |
|
||||
|
||||
Players reach all of it from **Options -> Advanced Graphics** (a Lighting Quality preset Off / Low / Medium / High plus the individual rows; Off is the original client look). The dev build has a panel for all of it: **Ctrl+Shift+D → Lighting & shadows**, with Dim / Default /
|
||||
Players reach all of it from **Options -> Advanced Graphics** (a Graphics Preset over everything, a Render Distance slider, a Lighting Quality preset Off / Low / Medium / High / Ultra plus the individual rows; Classic / Off is the original client look). The dev build has a panel for all of it: **Ctrl+Shift+D → Lighting & shadows**, with Dim / Default /
|
||||
Bright presets.
|
||||
|
||||
## Graphics Preset
|
||||
|
||||
MONKEY (presets). The top row of Advanced Graphics, cvar `graphicsQuality`: Classic / Low / Medium /
|
||||
High / Ultra, or Custom when the rows match none of them. The table is `GRAPHICS_PRESETS` in
|
||||
`benilla-app/src/cvars.rs` (one line per governed row); choosing a rung writes each row through
|
||||
the ordinary cvar path, and the label is re-derived every frame, so editing any governed row
|
||||
(or any Lighting Quality member) shows Custom. `lightingQuality` is itself a governed row, so
|
||||
every row the lighting ladder owns (`LIGHTING_PRESETS`: both sun lanes, shadow resolution, the
|
||||
room and torch lanes, moon shadows, `waterQuality`, `volumetricFog`, `bloom`, `sunShafts`,
|
||||
`colorGrading`, `foliageWind`, the fire/spell/lava gains, `nightGain`, `interiorGain`) follows it.
|
||||
|
||||
| row | Classic | Low | Medium | High | Ultra |
|
||||
|---|---|---|---|---|---|
|
||||
| `lightingQuality` | Off | Low | Medium | High | Ultra |
|
||||
| `farclip` (Render Distance, yd) | 350 | 350 | 477 | 777 | 1497 |
|
||||
| `skyQuality` | 0 | 1 | 1 | 2 | 2 |
|
||||
| `skyDither` | 0 | 1 | 1 | 1 | 1 |
|
||||
| `fogModel` | 0 | 1 | 1 | 1 | 1 |
|
||||
| `rainSurfaces` | 0 | 1 | 1 | 1 | 1 |
|
||||
| `torchTerrainShadows` | 0 | 0 | 0 | 1 | 1 |
|
||||
| `daylightWindowSplit` | 0 | 1 | 1 | 1 | 1 |
|
||||
| `ambientOcclusion` | 0 | 0 | 1 | 2 | 2 |
|
||||
| `zoneSkyboxes` | 0 | 0 | 1 | 1 | 1 |
|
||||
| `lampFog` | 0 | 0 | 1 | 2 | 2 |
|
||||
|
||||
What the lighting rungs set, beyond Off (everything off, gains 1.0) and High (the registered
|
||||
defaults): Low = character shadows only at a 1024 map, no torch or moon shadows, bloom Low;
|
||||
Medium = both sun lanes, indoor torch shadows (6 resident / 2 moving), moon shadows, bloom Low,
|
||||
no sun shafts or colour grading; Ultra = High with a 4096 sun map, 16 / 8 torch maps, High water
|
||||
and High volumetric fog. No rung decides `shadowDistance` or `interiorShadowSoft`.
|
||||
|
||||
**Default: High.** A player whose `config.toml` names no `graphicsQuality` is seeded to the High
|
||||
column at startup (`Cvars::seed_graphics_preset`) on every row the file does not carry; the rows'
|
||||
registered defaults stay their per-lane ones (`farclip` the reference's 350), and captures and
|
||||
tests, which never seed, keep them. The Advanced page's Defaults button lands on High too.
|
||||
|
||||
**Render Distance** is `farclip`, the same cvar as the Graphics page's Terrain Distance (which
|
||||
keeps the stock 177–777 slider). Its clamp (`view::FARCLIP_RANGE`) now reaches `FARCLIP_MAX` =
|
||||
1497 yd, the 1.12 grid of 60 carried on. Terrain residency (`terrain_stream/window.rs`), the
|
||||
model/WMO and particle walls (`view::within_farclip`) and the art-cache sweep radius (now 7 tiles,
|
||||
`art_scope.rs`) all follow it; at 1497 the residency window is at most 7×7 ADT tiles against
|
||||
777's 5×5, which is the memory bound. Past 777 only Modern fog (`fog_model::modern_fog_end`)
|
||||
stretches the fog end with it; under Classic fog the zone's fog end still hides the extra reach.
|
||||
|
||||
The optional enhanced water (Water Quality, refraction, caustics, High reflections, lava glow)
|
||||
is its own module, documented in `WATER.md`.
|
||||
|
||||
|
||||
@@ -492,6 +492,11 @@
|
||||
enUS = {
|
||||
page = "Advanced Graphics",
|
||||
lightingQuality = "Lighting Quality",
|
||||
-- MONKEY (presets): the Graphics Preset and Render Distance rows.
|
||||
graphicsQuality = "Graphics Preset",
|
||||
renderDistance = "Render Distance",
|
||||
pClassic = "Classic",
|
||||
qUltra = "Ultra",
|
||||
-- MONKEY (volumetric fog): localised atmospheric quality row.
|
||||
volumetricFog = "Volumetric Fog",
|
||||
-- MONKEY (post): HDR excess + halo quality.
|
||||
@@ -546,6 +551,24 @@
|
||||
daylightWindowSplit = "City Window Daylight",
|
||||
yards = "%d yd",
|
||||
tips = {
|
||||
-- MONKEY (presets)
|
||||
GRAPHICS_QUALITY =
|
||||
"Sets every graphics setting at once: Lighting Quality and everything it sets, "
|
||||
.. "Render Distance, Sky Quality, Sky Dithering, Modern Fog, Wet Surfaces in Rain, "
|
||||
.. "Terrain Blocks Torchlight, the daylight through city windows, Ambient Occlusion, Lamp Fog "
|
||||
.. "and Zone Skyboxes.\n\n"
|
||||
.. "Classic is the original client: every enhancement off and the original 350 yd "
|
||||
.. "view distance. Low and Medium add the enhancements that cost almost nothing; High "
|
||||
.. "is the recommended look; Ultra raises every quality to its maximum and the view "
|
||||
.. "distance to 1497 yd, and costs the most.\n\n"
|
||||
.. "Shadow Distance and Torch Shadow Softness stay as you set them. Changing any "
|
||||
.. "setting the preset sets shows Custom.",
|
||||
RENDER_DISTANCE =
|
||||
"How far the detailed world is drawn, in yards: the same setting as Terrain "
|
||||
.. "Distance on the Graphics page, with a longer reach. Past 777 yd, the original "
|
||||
.. "client's limit, the extra distance shows through Modern Fog; with the original "
|
||||
.. "fog the zone's own fog hides it.\n\n"
|
||||
.. "Farther costs more memory, loading and GPU time.",
|
||||
LIGHTING_QUALITY =
|
||||
"Sets character, world, indoor torch and outdoor torch shadows; dynamic "
|
||||
.. "interior lighting; moon shadows; volumetric fog; water quality; lava glow; fire and spell brightness; fire flicker; "
|
||||
@@ -555,6 +578,8 @@
|
||||
.. "night gain 1.0 and interior gain 1.0. All other settings, including "
|
||||
.. "Shadow Distance and Torch Shadow Softness, stay as you set them. "
|
||||
.. "Low, Medium and High use Enhanced water; mirror reflections stay opt-in. "
|
||||
.. "Ultra also raises shadow resolution to 4096 and the torch shadow counts to 16 and 8, "
|
||||
.. "and uses High water and High volumetric fog. "
|
||||
.. "Changing a preset member shows Custom when no preset matches.",
|
||||
-- MONKEY (volumetric fog): explain cost and the shadow-map dependency.
|
||||
-- MONKEY (post): Off guarantees the pre-HDR image.
|
||||
@@ -706,6 +731,11 @@
|
||||
ruRU = {
|
||||
page = "Расширенная графика",
|
||||
lightingQuality = "Качество освещения",
|
||||
-- MONKEY (presets): the Graphics Preset and Render Distance rows.
|
||||
graphicsQuality = "Набор графики",
|
||||
renderDistance = "Дальность прорисовки",
|
||||
pClassic = "Классика",
|
||||
qUltra = "Ультра",
|
||||
-- MONKEY (post): Russian HDR excess + halo quality row.
|
||||
bloom = "Свечение",
|
||||
sunShafts = "Солнечные лучи",
|
||||
@@ -760,6 +790,24 @@
|
||||
daylightWindowSplit = "Дневной свет через окна города",
|
||||
yards = "%d ярд.",
|
||||
tips = {
|
||||
-- MONKEY (presets)
|
||||
GRAPHICS_QUALITY =
|
||||
"Задаёт все настройки графики сразу: качество освещения и всё, что оно задаёт, "
|
||||
.. "дальность прорисовки, качество неба, дизеринг неба, современный туман, мокрые "
|
||||
.. "поверхности в дождь, заслонение огней рельефом, дневной свет в окнах городов, "
|
||||
.. "затенение окружения, свечение фонарей в тумане и небосводы зон.\n\n"
|
||||
.. "«Классика» — оригинальный клиент: все улучшения выключены, дальность обзора "
|
||||
.. "прежние 350 ярдов. «Низкое» и «Среднее» добавляют почти бесплатные улучшения; "
|
||||
.. "«Высокое» — рекомендуемый вид; «Ультра» поднимает всё качество до максимума, а "
|
||||
.. "дальность — до 1497 ярдов, и стоит дороже всего.\n\n"
|
||||
.. "Дальность теней и мягкость теней от огня сохраняют ваши значения. Изменение "
|
||||
.. "любой настройки набора показывает «Своё».",
|
||||
RENDER_DISTANCE =
|
||||
"Как далеко рисуется подробный мир, в ярдах: та же настройка, что «Дальность "
|
||||
.. "обзора» на странице «Графика», но с большим пределом. Дальше 777 ярдов, предела "
|
||||
.. "оригинального клиента, дополнительное расстояние видно с современным туманом; "
|
||||
.. "с оригинальным туманом его скрывает туман зоны.\n\n"
|
||||
.. "Чем дальше, тем больше памяти, загрузки и работы видеокарты.",
|
||||
LIGHTING_QUALITY =
|
||||
"Задаёт тени персонажей, мира и огней внутри и снаружи помещений; "
|
||||
.. "динамическое освещение помещений; лунные тени; объёмный туман; качество воды; свечение лавы; яркость огня и заклинаний; "
|
||||
@@ -770,7 +818,9 @@
|
||||
.. "источников света, с коэффициентами яркости ночи и помещений 1.0. "
|
||||
.. "Все остальные настройки, включая дальность теней и мягкость теней от огня, "
|
||||
.. "сохраняют ваши значения. «Низкое», «Среднее» и «Высокое» используют улучшенную воду; "
|
||||
.. "зеркальные отражения включаются отдельно. Изменение настройки набора показывает «Своё», "
|
||||
.. "зеркальные отражения включаются отдельно. «Ультра» также поднимает разрешение теней "
|
||||
.. "до 4096 и число теней от огней до 16 и 8, а вода и объёмный туман — «Высокое». "
|
||||
.. "Изменение настройки набора показывает «Своё», "
|
||||
.. "если ни один набор больше не совпадает.",
|
||||
-- MONKEY (post): localised bloom effect and Off contract.
|
||||
BLOOM =
|
||||
@@ -976,6 +1026,8 @@
|
||||
-- the rig they share, and the Graphics page had become the place where a player found
|
||||
-- three of eighteen shadow settings and no hint that the other fifteen existed.
|
||||
AdvancedGraphics = {
|
||||
-- MONKEY (presets): the ladder over every row below, then the view distance.
|
||||
"RowGraphicsQuality", "RowRenderDistance",
|
||||
-- MONKEY (volumetric fog): include in refresh, defaults and search.
|
||||
"RowLightingQuality", "RowWaterQuality", "RowVolumetricFog",
|
||||
-- MONKEY (sky): the sky tier row.
|
||||
@@ -1615,7 +1667,20 @@
|
||||
for _, rkey in ipairs(OPTIONS_PAGE_ROWS[key]) do
|
||||
local row = getglobal(base .. rkey)
|
||||
row.pending = nil
|
||||
OptionsRow_Set(row, OptionsRow_Default(row))
|
||||
if not row.preset then
|
||||
OptionsRow_Set(row, OptionsRow_Default(row))
|
||||
end
|
||||
end
|
||||
-- MONKEY (presets): a preset row goes last, so its default rung (the Graphics
|
||||
-- Preset's High) is what the page lands on rather than its members' own defaults.
|
||||
-- `SetCVar` queues only a moved value, so "Custom" (which the host applies as
|
||||
-- nothing) goes first: the rung is then always a queued write, after the members.
|
||||
for _, rkey in ipairs(OPTIONS_PAGE_ROWS[key]) do
|
||||
local row = getglobal(base .. rkey)
|
||||
if row.preset then
|
||||
OptionsRow_Set(row, "Custom")
|
||||
OptionsRow_Set(row, OptionsRow_Default(row))
|
||||
end
|
||||
end
|
||||
OptionsPage_Refresh(key)
|
||||
end
|
||||
@@ -2744,11 +2809,51 @@
|
||||
<Anchor point="BOTTOMRIGHT"/>
|
||||
</Anchors>
|
||||
<Frames>
|
||||
<Frame name="$parentRowLightingQuality" inherits="OptionsDropdownRowTemplate">
|
||||
<!-- MONKEY (presets): the Graphics Preset. Like Lighting
|
||||
Quality below it, ONE CVar: the host's
|
||||
`cvars::GRAPHICS_PRESETS` writes the rows and
|
||||
re-derives this one every frame. -->
|
||||
<Frame name="$parentRowGraphicsQuality" inherits="OptionsDropdownRowTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="10" y="-12"/></Offset></Anchor>
|
||||
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-20" y="-12"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Scripts><OnLoad>
|
||||
OptionsRow_OnLoad(self, "graphicsQuality",
|
||||
BENILLA_ADVGFX.graphicsQuality,
|
||||
"BENILLA_TOOLTIP_GRAPHICS_QUALITY")
|
||||
self.preset = 1
|
||||
OptionsDropdownRow_Setup(self, {
|
||||
{ text = BENILLA_ADVGFX.pClassic, value = "Classic" },
|
||||
{ text = BENILLA_ADVGFX.qLow, value = "Low" },
|
||||
{ text = BENILLA_ADVGFX.qMedium, value = "Medium" },
|
||||
{ text = BENILLA_ADVGFX.qHigh, value = "High" },
|
||||
{ text = BENILLA_ADVGFX.qUltra, value = "Ultra" },
|
||||
{ text = BENILLA_ADVGFX.qCustom, value = "Custom" },
|
||||
})
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<!-- MONKEY (presets): `farclip`, the Graphics page's Terrain
|
||||
Distance, on the extended range (`view::FARCLIP_RANGE`,
|
||||
177..1497 on the 1.12 grid of 60). Live like its
|
||||
Graphics twin; both read the one CVar. -->
|
||||
<Frame name="$parentRowRenderDistance" inherits="OptionsSliderRowTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="$parentRowGraphicsQuality" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||||
<Anchor point="TOPRIGHT" relativeTo="$parentRowGraphicsQuality" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Scripts><OnLoad>
|
||||
OptionsRow_OnLoad(self, "farclip",
|
||||
BENILLA_ADVGFX.renderDistance,
|
||||
"BENILLA_TOOLTIP_RENDER_DISTANCE")
|
||||
OptionsSliderRow_Setup(self, 177, 1497, 60, "yd")
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<Frame name="$parentRowLightingQuality" inherits="OptionsDropdownRowTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="$parentRowRenderDistance" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||||
<Anchor point="TOPRIGHT" relativeTo="$parentRowRenderDistance" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Scripts><OnLoad>
|
||||
-- The preset ladder. This row writes ONE CVar and
|
||||
-- nothing else: the host's own table
|
||||
@@ -2773,6 +2878,7 @@
|
||||
{ text = BENILLA_ADVGFX.qLow, value = "Low" },
|
||||
{ text = BENILLA_ADVGFX.qMedium, value = "Medium" },
|
||||
{ text = BENILLA_ADVGFX.qHigh, value = "High" },
|
||||
{ text = BENILLA_ADVGFX.qUltra, value = "Ultra" },
|
||||
{ text = BENILLA_ADVGFX.qCustom, value = "Custom" },
|
||||
})
|
||||
</OnLoad></Scripts>
|
||||
|
||||
@@ -916,9 +916,22 @@ pub(crate) const REGISTERED: &[Registered] = &[
|
||||
"lightingQuality",
|
||||
"High",
|
||||
"benilla's own: the preset ladder over the dynamic light + shadow rows — Off / Low / \
|
||||
Medium / High, or Custom when the members match none of them; the reference has no \
|
||||
Medium / High / Ultra, or Custom when the members match none of them; the reference has no \
|
||||
realtime light or shadow system to preset",
|
||||
),
|
||||
// MONKEY (presets): the Graphics Preset — `lightingQuality`'s posture one level up, a NAME
|
||||
// for every graphics row at once ([`GRAPHICS_PRESETS`]), re-derived every frame. "High" is
|
||||
// what a player with no saved preset boots into: [`Cvars::seed_graphics_preset`] writes the
|
||||
// High column over every row their `config.toml` does not carry, so the rows themselves keep
|
||||
// their per-lane defaults (Classic-leaning, and `farclip` the reference's 350) and a capture
|
||||
// or a test, which never seeds, keeps them too.
|
||||
ours(
|
||||
"graphicsQuality",
|
||||
"High",
|
||||
"benilla's own: the preset ladder over every graphics row — Classic / Low / Medium / High \
|
||||
/ Ultra, or Custom when the rows match none of them; the reference's options have no \
|
||||
such preset",
|
||||
),
|
||||
// MONKEY (volumetric fog): saved live tier; capture override stays session-only.
|
||||
ours("volumetricFog", "1", "benilla's own: near-field volumetric fog, 0 Off / 1 Low / 2 High"),
|
||||
// MONKEY (lampfog): opt-in point-light halos; High graphics preset value is 2.
|
||||
@@ -1747,6 +1760,9 @@ impl Cvars {
|
||||
if name.eq_ignore_ascii_case("lightingQuality") {
|
||||
apply_lighting_preset(self, value);
|
||||
}
|
||||
if name.eq_ignore_ascii_case("graphicsQuality") {
|
||||
apply_graphics_preset(self, value);
|
||||
}
|
||||
outcome
|
||||
}
|
||||
|
||||
@@ -1758,9 +1774,38 @@ impl Cvars {
|
||||
if name.eq_ignore_ascii_case("lightingQuality") {
|
||||
apply_lighting_preset(self, value);
|
||||
}
|
||||
if name.eq_ignore_ascii_case("graphicsQuality") {
|
||||
apply_graphics_preset(self, value);
|
||||
}
|
||||
outcome
|
||||
}
|
||||
|
||||
/// MONKEY (presets): **the first boot's Graphics Preset** — a player whose `config.toml`
|
||||
/// names no `graphicsQuality` (a new player, or one from before the ladder) gets the
|
||||
/// [`GRAPHICS_DEFAULT`] column written over every governed row the file does not carry and the
|
||||
/// session does not own. Rows the file carries are the player's and stay; the lighting rung
|
||||
/// is left alone, because High on that ladder IS the registered defaults
|
||||
/// (`the_high_preset_is_the_registered_defaults`). Returns how many rows moved.
|
||||
pub(crate) fn seed_graphics_preset(&mut self) -> usize {
|
||||
let carried = |cvars: &Self, k: &str| {
|
||||
cvars.file.keys().any(|f| f.eq_ignore_ascii_case(k)) || cvars.is_session_owned(k)
|
||||
};
|
||||
if carried(self, "graphicsQuality") {
|
||||
return 0;
|
||||
}
|
||||
let col = graphics_column(GRAPHICS_DEFAULT).expect("the default is a rung");
|
||||
let mut moved = 0;
|
||||
for (k, values) in GRAPHICS_PRESETS {
|
||||
if k.eq_ignore_ascii_case("lightingQuality") || carried(self, k) {
|
||||
continue;
|
||||
}
|
||||
if self.set(k, values[col]) == SetOutcome::Changed {
|
||||
moved += 1;
|
||||
}
|
||||
}
|
||||
moved
|
||||
}
|
||||
|
||||
/// **The table follows a value the engine already applied** — a mirror, not a write: the
|
||||
/// applied value moves, the config dirties, the VM's mirror learns it, and **no observer
|
||||
/// fires**, because the knob is already there. For a second registered spelling of one knob
|
||||
@@ -2180,6 +2225,35 @@ pub(crate) const LIGHTING_PRESETS: &[(&str, &[(&str, &str)])] = &[
|
||||
("foliageWind", "2"),
|
||||
],
|
||||
),
|
||||
// MONKEY (presets): High plus every lane at its maximum — the 4096 sun map, all sixteen
|
||||
// resident torch maps with eight of them tracking movers, mirror-reflection water and the
|
||||
// High fog march. Disjoint from High on `shadowMapSize`, so derivation never confuses them.
|
||||
(
|
||||
"Ultra",
|
||||
&[
|
||||
("characterShadows", "1"),
|
||||
("worldShadows", "1"),
|
||||
("shadowMapSize", "4096"),
|
||||
("interiorLight", "1"),
|
||||
("interiorShadows", "1"),
|
||||
("interiorShadowCasters", "16"),
|
||||
("interiorShadowDynamic", "8"),
|
||||
("exteriorShadows", "1"),
|
||||
("spellLightGain", "1"),
|
||||
("waterQuality", "2"),
|
||||
("volumetricFog", "2"),
|
||||
("bloom", "2"),
|
||||
("sunShafts", "1"),
|
||||
("colorGrading", "1"),
|
||||
("lavaLightGain", "1"),
|
||||
("fireLightGain", "1"),
|
||||
("nightGain", "0.45"),
|
||||
("interiorGain", "0.5"),
|
||||
("moonShadowStrength", "0.35"),
|
||||
("fireFlicker", "1"),
|
||||
("foliageWind", "2"),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
/// What the ladder shows when the members match no preset. Not a preset: selecting it writes
|
||||
@@ -2233,6 +2307,82 @@ fn lighting_quality(cvars: &mut Cvars) {
|
||||
let derived = derive_lighting_quality(cvars);
|
||||
// A derived label is a mirror, never another request to apply a preset.
|
||||
cvars.mirror("lightingQuality", derived);
|
||||
// MONKEY (presets): after the lighting label, which the graphics ladder reads as a member.
|
||||
let graphics = derive_graphics_quality(cvars);
|
||||
cvars.mirror("graphicsQuality", graphics);
|
||||
}
|
||||
|
||||
// ─── MONKEY (presets): the Graphics Preset ladder ─────────────────────────────────────────────
|
||||
|
||||
/// The Graphics Preset's rungs, in the order [`derive_graphics_quality`] tries them and the
|
||||
/// column order of [`GRAPHICS_PRESETS`].
|
||||
pub(crate) const GRAPHICS_PRESET_NAMES: [&str; 5] = ["Classic", "Low", "Medium", "High", "Ultra"];
|
||||
|
||||
/// The rung a player with no saved preset boots into ([`Cvars::seed_graphics_preset`]).
|
||||
pub(crate) const GRAPHICS_DEFAULT: &str = "High";
|
||||
|
||||
/// **The one place the Graphics Preset is written down**: one line per governed row, one column
|
||||
/// per rung of [`GRAPHICS_PRESET_NAMES`]. `lightingQuality` is itself a row — writing its rung
|
||||
/// writes [`LIGHTING_PRESETS`]' members — so every lighting, water, fog, post and wind row the
|
||||
/// lighting ladder owns is governed here without a second copy. The other rows are disjoint from
|
||||
/// that ladder, which is what lets both labels be right at once.
|
||||
///
|
||||
/// Classic is the reference client: every lane off, `farclip` at its registered 350. High is
|
||||
/// each lane's documented High (LIGHTING.md, WATER.md); Ultra is High at every maximum, and the
|
||||
/// only rung past the reference's 777 yd (`FARCLIP_RANGE`); Low and Medium keep the practically
|
||||
/// free lanes (sky, dither, modern fog, wet surfaces) and leave the costly ones to High.
|
||||
///
|
||||
/// A row that lands later is ONE line here, and only once it is registered (an unregistered
|
||||
/// member would never match, and the ladder would read Custom forever).
|
||||
#[rustfmt::skip]
|
||||
pub(crate) const GRAPHICS_PRESETS: &[(&str, [&str; 5])] = &[
|
||||
// Classic Low Medium High Ultra
|
||||
("lightingQuality", ["Off", "Low", "Medium", "High", "Ultra"]),
|
||||
("farclip", ["350", "350", "477", "777", "1497"]),
|
||||
("skyQuality", ["0", "1", "1", "2", "2"]),
|
||||
("skyDither", ["0", "1", "1", "1", "1"]),
|
||||
("fogModel", ["0", "1", "1", "1", "1"]),
|
||||
("rainSurfaces", ["0", "1", "1", "1", "1"]),
|
||||
("torchTerrainShadows", ["0", "0", "0", "1", "1"]),
|
||||
("daylightWindowSplit", ["0", "1", "1", "1", "1"]),
|
||||
// MONKEY (integration): the three rows that landed in round 3. Low keeps them off (AO and
|
||||
// the lamp halos are GPU passes; the zone skybox is a look change), Medium takes the Low tiers.
|
||||
("ambientOcclusion", ["0", "0", "1", "2", "2"]),
|
||||
("zoneSkyboxes", ["0", "0", "1", "1", "1"]),
|
||||
("lampFog", ["0", "0", "1", "2", "2"]),
|
||||
];
|
||||
|
||||
/// The column of a rung, matched case-insensitively; `None` for `Custom` or anything else.
|
||||
fn graphics_column(name: &str) -> Option<usize> {
|
||||
GRAPHICS_PRESET_NAMES
|
||||
.iter()
|
||||
.position(|n| n.eq_ignore_ascii_case(name))
|
||||
}
|
||||
|
||||
/// The rung every governed row currently spells, or [`LIGHTING_CUSTOM`] (the one "Custom" both
|
||||
/// ladders show). Reads `lightingQuality`'s DERIVED label, so run it after that is mirrored.
|
||||
pub(crate) fn derive_graphics_quality(cvars: &Cvars) -> &'static str {
|
||||
for (col, name) in GRAPHICS_PRESET_NAMES.iter().enumerate() {
|
||||
let matched = GRAPHICS_PRESETS
|
||||
.iter()
|
||||
.all(|(k, v)| cvars.get(k).is_some_and(|live| same_value(live, v[col])));
|
||||
if matched {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
LIGHTING_CUSTOM
|
||||
}
|
||||
|
||||
/// Write one rung's column through [`Cvars::set`]; `false` (and nothing written) for an unknown
|
||||
/// name, `Custom` included.
|
||||
pub(crate) fn apply_graphics_preset(cvars: &mut Cvars, name: &str) -> bool {
|
||||
let Some(col) = graphics_column(name) else {
|
||||
return false;
|
||||
};
|
||||
for (k, values) in GRAPHICS_PRESETS {
|
||||
cvars.set(k, values[col]);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// **What the environment took for this session, and what it set it to** — read off the knobs
|
||||
@@ -2327,6 +2477,11 @@ fn session_values(world: &World) -> Vec<(&'static str, Option<String>)> {
|
||||
fn load_config(world: &mut World) {
|
||||
let session = session_values(world);
|
||||
let stored = stored_config();
|
||||
let stored_kind = match &stored {
|
||||
StoredConfig::Absent => StoredKind::Absent,
|
||||
StoredConfig::Table(_) => StoredKind::Table,
|
||||
StoredConfig::Bad(_) => StoredKind::Bad,
|
||||
};
|
||||
let events = {
|
||||
let mut cvars = world.resource_mut::<Cvars>();
|
||||
for (name, value) in session {
|
||||
@@ -2348,6 +2503,11 @@ fn load_config(world: &mut World) {
|
||||
}
|
||||
StoredConfig::Table(table) => cvars.load_file(table),
|
||||
}
|
||||
// MONKEY (presets): a player's own run (never a capture, never a malformed file) boots
|
||||
// into the default Graphics Preset over whatever its file leaves unsaid.
|
||||
if seeds_graphics_preset(&stored_kind) {
|
||||
cvars.seed_graphics_preset();
|
||||
}
|
||||
cvars.take_events()
|
||||
};
|
||||
for event in events {
|
||||
@@ -2355,6 +2515,22 @@ fn load_config(world: &mut World) {
|
||||
}
|
||||
}
|
||||
|
||||
/// MONKEY (presets): which [`StoredConfig`] arm a boot took, kept past the table's move.
|
||||
enum StoredKind {
|
||||
Absent,
|
||||
Table,
|
||||
Bad,
|
||||
}
|
||||
|
||||
/// MONKEY (presets): whether this boot seeds the default Graphics Preset — a run that reads and
|
||||
/// saves a player's `config.toml` and found it absent or well-formed. A capture (hermetic, or on
|
||||
/// an explicit fixture) keeps the registered defaults so its A/B stays exact, and a malformed
|
||||
/// file is left for the player rather than papered over.
|
||||
fn seeds_graphics_preset(kind: &StoredKind) -> bool {
|
||||
let players_file = std::env::var_os("WOW_CAPTURE").is_none() && config_read_path().is_some();
|
||||
players_file && matches!(kind, StoredKind::Absent | StoredKind::Table)
|
||||
}
|
||||
|
||||
/// What the one read of `config.toml` found.
|
||||
enum StoredConfig {
|
||||
/// No file, no install, or a capture without an explicit fixture — use registered defaults.
|
||||
@@ -3127,6 +3303,8 @@ mod tests {
|
||||
for member in ["fireLightGain", "waterQuality", "volumetricFog", "lavaLightGain", "nightGain", "interiorGain"] {
|
||||
let expected = if *name == "Off" {
|
||||
if matches!(member, "fireLightGain" | "waterQuality" | "volumetricFog" | "lavaLightGain") { "0" } else { "1.0" }
|
||||
} else if *name == "Ultra" && matches!(member, "waterQuality" | "volumetricFog") {
|
||||
"2" // MONKEY (presets): Ultra's two maxima over the defaults.
|
||||
} else {
|
||||
cvars.default_of(member).unwrap()
|
||||
};
|
||||
@@ -3211,10 +3389,212 @@ mod tests {
|
||||
let mut cvars = fresh_registry();
|
||||
apply_lighting_preset(&mut cvars, "Medium");
|
||||
assert!(!apply_lighting_preset(&mut cvars, LIGHTING_CUSTOM));
|
||||
assert!(!apply_lighting_preset(&mut cvars, "Ultra"));
|
||||
assert!(!apply_lighting_preset(&mut cvars, "Epic"));
|
||||
assert_eq!(derive_lighting_quality(&cvars), "Medium");
|
||||
}
|
||||
|
||||
// ── MONKEY (presets): the Graphics Preset ladder ──────────────────────────────────────────
|
||||
|
||||
/// Every governed row is registered and names no row the lighting ladder already owns — the
|
||||
/// disjointness that lets both labels be right at once.
|
||||
#[test]
|
||||
fn graphics_rows_are_registered_and_disjoint_from_the_lighting_ladder() {
|
||||
assert_eq!(GRAPHICS_PRESETS.len(), 11, "add the row count with the row");
|
||||
let cvars = fresh_registry();
|
||||
for (k, values) in GRAPHICS_PRESETS {
|
||||
assert!(cvars.get(k).is_some(), "{k}: not registered");
|
||||
for (rung, members) in LIGHTING_PRESETS {
|
||||
assert!(
|
||||
members.iter().all(|(m, _)| !m.eq_ignore_ascii_case(k)),
|
||||
"{k} is also a member of lighting {rung}"
|
||||
);
|
||||
}
|
||||
if *k == "lightingQuality" {
|
||||
for v in values {
|
||||
assert!(
|
||||
LIGHTING_PRESETS.iter().any(|(n, _)| n == v),
|
||||
"{v}: a lighting rung"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// The rungs are distinct columns, so no two can derive to the same name.
|
||||
for a in 0..GRAPHICS_PRESET_NAMES.len() {
|
||||
for b in a + 1..GRAPHICS_PRESET_NAMES.len() {
|
||||
assert!(
|
||||
GRAPHICS_PRESETS
|
||||
.iter()
|
||||
.any(|(_, v)| !same_value(v[a], v[b])),
|
||||
"{} and {} are the same column",
|
||||
GRAPHICS_PRESET_NAMES[a],
|
||||
GRAPHICS_PRESET_NAMES[b]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// **Preset → rows → the same preset**, from every rung to every rung, through the ordinary
|
||||
/// `set` path — with the two rows no preset decides left where the player put them.
|
||||
#[test]
|
||||
fn every_graphics_preset_derives_back_to_its_own_name() {
|
||||
for from in GRAPHICS_PRESET_NAMES {
|
||||
for name in GRAPHICS_PRESET_NAMES {
|
||||
let mut cvars = fresh_registry();
|
||||
cvars.set("graphicsQuality", from);
|
||||
cvars.set("shadowDistance", "120");
|
||||
cvars.set("interiorShadowSoft", "2.5");
|
||||
cvars.set("graphicsQuality", name);
|
||||
lighting_quality(&mut cvars);
|
||||
assert_eq!(derive_graphics_quality(&cvars), name, "{from} -> {name}");
|
||||
assert_eq!(cvars.get("graphicsQuality"), Some(name));
|
||||
let col = graphics_column(name).unwrap();
|
||||
assert_eq!(
|
||||
cvars.get("lightingQuality"),
|
||||
Some(GRAPHICS_PRESETS[0].1[col])
|
||||
);
|
||||
assert_eq!(cvars.get("shadowDistance"), Some("120"));
|
||||
assert_eq!(cvars.get("interiorShadowSoft"), Some("2.5"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Classic is the reference client: the lighting ladder's Off, every programme lane off and
|
||||
/// `farclip` at the reference's registered 350.
|
||||
#[test]
|
||||
fn the_classic_preset_is_every_lane_off_at_the_reference_view_distance() {
|
||||
let mut cvars = fresh_registry();
|
||||
assert!(apply_graphics_preset(&mut cvars, "Classic"));
|
||||
assert_eq!(cvars.get("lightingQuality"), Some("Off"));
|
||||
assert_eq!(cvars.get("farclip"), cvars.default_of("farclip"));
|
||||
for (k, values) in GRAPHICS_PRESETS {
|
||||
if !matches!(*k, "lightingQuality" | "farclip") {
|
||||
assert!(same_value(values[0], "0"), "{k}: Classic leaves it on");
|
||||
}
|
||||
}
|
||||
// Ultra is the one rung past the reference's 777, and inside the extended clamp.
|
||||
let ultra = GRAPHICS_PRESETS
|
||||
.iter()
|
||||
.find(|(k, _)| *k == "farclip")
|
||||
.unwrap()
|
||||
.1[4];
|
||||
let ultra: f32 = ultra.parse().unwrap();
|
||||
assert!(ultra > 777.0 && FARCLIP_RANGE.contains(&ultra));
|
||||
}
|
||||
|
||||
/// **Editing one governed row is Custom** — for every rung and every row of it, the lighting
|
||||
/// rung included (moved by one of ITS members, as the page's lighting rows would).
|
||||
#[test]
|
||||
fn one_changed_graphics_row_derives_custom() {
|
||||
for name in GRAPHICS_PRESET_NAMES {
|
||||
let col = graphics_column(name).unwrap();
|
||||
for (k, values) in GRAPHICS_PRESETS {
|
||||
let mut cvars = fresh_registry();
|
||||
apply_graphics_preset(&mut cvars, name);
|
||||
let (row, moved) = if *k == "lightingQuality" {
|
||||
let to = if values[col] == "Off" { "1" } else { "0" };
|
||||
("characterShadows", to.to_string())
|
||||
} else {
|
||||
let v: f32 = values[col].parse().unwrap();
|
||||
let to = if *k == "farclip" {
|
||||
v + 60.0
|
||||
} else if v == 0.0 {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
(*k, to.to_string())
|
||||
};
|
||||
assert_eq!(cvars.set(row, &moved), SetOutcome::Changed, "{name}/{row}");
|
||||
lighting_quality(&mut cvars);
|
||||
assert_eq!(
|
||||
cvars.get("graphicsQuality"),
|
||||
Some(LIGHTING_CUSTOM),
|
||||
"{name}/{row}"
|
||||
);
|
||||
// …and picking the rung again puts every row back.
|
||||
assert!(apply_graphics_preset(&mut cvars, name));
|
||||
lighting_quality(&mut cvars);
|
||||
assert_eq!(
|
||||
cvars.get("graphicsQuality"),
|
||||
Some(name),
|
||||
"{name}/{row} re-pick"
|
||||
);
|
||||
}
|
||||
}
|
||||
let mut cvars = fresh_registry();
|
||||
assert!(!apply_graphics_preset(&mut cvars, LIGHTING_CUSTOM));
|
||||
assert!(!apply_graphics_preset(&mut cvars, "Epic"));
|
||||
}
|
||||
|
||||
/// **A new player boots into High**: the seed writes the High column over a registry with no
|
||||
/// file, and the registered default of the label agrees, so it never reaches `config.toml`.
|
||||
#[test]
|
||||
fn a_fresh_player_is_seeded_to_the_high_graphics_preset() {
|
||||
assert_eq!(
|
||||
fresh_registry().default_of("graphicsQuality"),
|
||||
Some(GRAPHICS_DEFAULT)
|
||||
);
|
||||
let mut cvars = fresh_registry();
|
||||
assert!(cvars.seed_graphics_preset() > 0);
|
||||
lighting_quality(&mut cvars);
|
||||
assert_eq!(cvars.get("graphicsQuality"), Some("High"));
|
||||
assert_eq!(cvars.get("lightingQuality"), Some("High"));
|
||||
assert_eq!(cvars.get("farclip"), Some("777"));
|
||||
assert!(
|
||||
!cvars.compose().contains_key("graphicsQuality"),
|
||||
"the default label is not saved"
|
||||
);
|
||||
// A second boot over what the first one saved seeds nothing.
|
||||
let mut again = fresh_registry();
|
||||
again.load_file(cvars.compose());
|
||||
assert_eq!(again.seed_graphics_preset(), 0);
|
||||
lighting_quality(&mut again);
|
||||
assert_eq!(again.get("graphicsQuality"), Some("High"));
|
||||
}
|
||||
|
||||
/// The seed never overrides the player: a saved preset stops it outright, a saved row keeps
|
||||
/// its value, and a session-owned row (an env lever) is left to the session.
|
||||
#[test]
|
||||
fn the_graphics_seed_leaves_saved_and_session_rows_alone() {
|
||||
let mut saved = fresh_registry();
|
||||
saved.load_file(BTreeMap::from([(
|
||||
"graphicsQuality".into(),
|
||||
"Classic".into(),
|
||||
)]));
|
||||
assert_eq!(saved.seed_graphics_preset(), 0);
|
||||
assert_eq!(saved.get("skyQuality"), saved.default_of("skyQuality"));
|
||||
|
||||
let mut row = fresh_registry();
|
||||
row.load_file(BTreeMap::from([("skyQuality".into(), "1".into())]));
|
||||
row.own_for_session("farclip", Some("500"));
|
||||
row.seed_graphics_preset();
|
||||
lighting_quality(&mut row);
|
||||
assert_eq!(row.get("skyQuality"), Some("1"));
|
||||
assert_eq!(row.get("farclip"), Some("500"));
|
||||
assert_eq!(row.get("fogModel"), Some("1"));
|
||||
assert_eq!(row.get("graphicsQuality"), Some(LIGHTING_CUSTOM));
|
||||
}
|
||||
|
||||
/// Ultra's `farclip` survives the real observer (the extended clamp), and every governed
|
||||
/// row's rung values do too.
|
||||
#[test]
|
||||
fn graphics_rows_pass_their_observers_unclamped() {
|
||||
let mut app = cvar_app();
|
||||
for (col, name) in GRAPHICS_PRESET_NAMES.iter().enumerate() {
|
||||
let farclip = GRAPHICS_PRESETS
|
||||
.iter()
|
||||
.find(|(k, _)| *k == "farclip")
|
||||
.unwrap()
|
||||
.1[col];
|
||||
apply(&mut app, "farclip", farclip);
|
||||
assert_eq!(
|
||||
res::<ViewDistance>(&app).farclip,
|
||||
farclip.parse::<f32>().unwrap(),
|
||||
"{name}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Every member every rung names is a REGISTERED row — a typo'd member would otherwise make
|
||||
/// its whole preset underivable (a `cvars.get` that answers `None` never matches), and the
|
||||
/// only symptom would be a dropdown stuck on `Custom`.
|
||||
@@ -3741,7 +4121,16 @@ mod tests {
|
||||
assert!(text.contains("MusicVolume = \"0.75\""), "{text}");
|
||||
assert!(!text.contains("MasterVolume"), "defaults stay out:\n{text}");
|
||||
let back: LocalConfig = toml::from_str(&text).unwrap();
|
||||
assert_eq!(back.cvars.len(), 1, "a diff, not a dump: {text}");
|
||||
// MONKEY (presets): the file named no `graphicsQuality`, so the first boot seeded the
|
||||
// High rungs over the governed rows; those are the only other entries, and the label
|
||||
// itself, at its default, stays out.
|
||||
let player: Vec<&String> = back
|
||||
.cvars
|
||||
.keys()
|
||||
.filter(|k| !GRAPHICS_PRESETS.iter().any(|(g, _)| g.eq_ignore_ascii_case(k)))
|
||||
.collect();
|
||||
assert_eq!(player.len(), 1, "a diff, not a dump: {text}");
|
||||
assert!(!text.contains("graphicsQuality"), "{text}");
|
||||
std::fs::remove_dir_all(&tmp).ok();
|
||||
}
|
||||
|
||||
@@ -4115,7 +4504,14 @@ mod tests {
|
||||
strings.sort_unstable(); // the list is the claim, not where the rows sit in the table
|
||||
assert_eq!(
|
||||
strings,
|
||||
vec!["gxApi", "gxResolution", "lightingQuality", "realmList", "realmName"]
|
||||
vec![
|
||||
"graphicsQuality",
|
||||
"gxApi",
|
||||
"gxResolution",
|
||||
"lightingQuality",
|
||||
"realmList",
|
||||
"realmName"
|
||||
]
|
||||
);
|
||||
let default_of = |name: &str| {
|
||||
REGISTERED
|
||||
@@ -4432,6 +4828,11 @@ mod tests {
|
||||
"GameTooltip's binding-line gate, stock and pfUI (1316)",
|
||||
),
|
||||
("gxApi", "pfUI's system tooltip names the backend (2151)"),
|
||||
(
|
||||
"graphicsQuality",
|
||||
"MONKEY (presets): the Advanced Graphics page's Graphics Preset row; the host \
|
||||
writes and derives it here in cvars.rs",
|
||||
),
|
||||
(
|
||||
"useUiScale",
|
||||
"UIOptionsFrame.lua and OptionsFrame.lua branch on it to gate the uiScale slider",
|
||||
|
||||
@@ -2042,7 +2042,7 @@ fn volumetric_fog_dropdown_is_localised_and_live() {
|
||||
s.run(&format!("function GetLocale() return '{locale}' end")).unwrap();
|
||||
let mut s = harness_on(s);
|
||||
s.run("ShowUIPanel(BenillaOptionsFrame) BenillaOptionsFrameCategoryListRowAdvancedGraphics:Click()").unwrap();
|
||||
assert_eq!(s.eval::<String>("return OPTIONS_PAGE_ROWS.AdvancedGraphics[3]").unwrap(), "RowVolumetricFog");
|
||||
assert_eq!(s.eval::<String>("return OPTIONS_PAGE_ROWS.AdvancedGraphics[5]").unwrap(), "RowVolumetricFog");
|
||||
assert_eq!(s.eval::<String>(&format!("return {ADVGFX}RowVolumetricFogLabel:GetText()")).unwrap(), title);
|
||||
assert_eq!(s.eval::<String>(&format!("return {ADVGFX}RowVolumetricFogDropdownText:GetText()")).unwrap(), labels[1]);
|
||||
let _ = s.take_cvar_changes();
|
||||
@@ -2164,10 +2164,10 @@ fn water_quality_writes_numeric_tiers_with_localised_labels() {
|
||||
let mut s = harness_on(s);
|
||||
s.run("ShowUIPanel(BenillaOptionsFrame) BenillaOptionsFrameCategoryListRowAdvancedGraphics:Click()").unwrap();
|
||||
// MONKEY (volumetric fog): account for the atmosphere row after water.
|
||||
// MONKEY (integration): all programme rows (sky, post, dither, fog, wet, wind, ao, lamp fog, window split, zone skyboxes) counted.
|
||||
assert_eq!(s.eval::<usize>("return table.getn(OPTIONS_PAGE_ROWS.AdvancedGraphics)").unwrap(), 31);
|
||||
assert_eq!(s.eval::<String>("return OPTIONS_PAGE_ROWS.AdvancedGraphics[2]").unwrap(), "RowWaterQuality");
|
||||
assert_eq!(s.eval::<String>("return OPTIONS_PAGE_ROWS.AdvancedGraphics[21]").unwrap(), "RowLavaGlow");
|
||||
// MONKEY (integration): Graphics Preset + Render Distance lead the page, then every programme row (sky, post, dither, fog, wet, wind, ao, lamp fog, window split, zone skyboxes): 33.
|
||||
assert_eq!(s.eval::<usize>("return table.getn(OPTIONS_PAGE_ROWS.AdvancedGraphics)").unwrap(), 33);
|
||||
assert_eq!(s.eval::<String>("return OPTIONS_PAGE_ROWS.AdvancedGraphics[4]").unwrap(), "RowWaterQuality");
|
||||
assert_eq!(s.eval::<String>("return OPTIONS_PAGE_ROWS.AdvancedGraphics[23]").unwrap(), "RowLavaGlow");
|
||||
assert_eq!(s.eval::<String>(&format!("return {ADVGFX}RowWaterQualityDropdownText:GetText()")).unwrap(), labels[1]);
|
||||
assert_eq!(s.eval::<String>(&format!("return {ADVGFX}RowLavaGlowLabel:GetText()")).unwrap(), lava_label);
|
||||
assert!(s.eval::<bool>("return BENILLA_TOOLTIP_WATER_QUALITY == BENILLA_ADVGFX.tips.WATER_QUALITY and BENILLA_TOOLTIP_LAVA_GLOW == BENILLA_ADVGFX.tips.LAVA_GLOW").unwrap());
|
||||
@@ -3139,6 +3139,15 @@ fn every_row_tooltip_key_resolves_in_the_real_global_strings() {
|
||||
"BENILLA_TOOLTIP_LIGHTING_QUALITY",
|
||||
"AdvancedGraphicsRowLightingQuality",
|
||||
),
|
||||
// MONKEY (presets)
|
||||
(
|
||||
"BENILLA_TOOLTIP_GRAPHICS_QUALITY",
|
||||
"AdvancedGraphicsRowGraphicsQuality",
|
||||
),
|
||||
(
|
||||
"BENILLA_TOOLTIP_RENDER_DISTANCE",
|
||||
"AdvancedGraphicsRowRenderDistance",
|
||||
),
|
||||
(
|
||||
"BENILLA_TOOLTIP_SHADOW_RESOLUTION",
|
||||
"AdvancedGraphicsRowShadowResolution",
|
||||
@@ -3249,7 +3258,8 @@ fn every_row_tooltip_key_resolves_in_the_real_global_strings() {
|
||||
// changing pages does not change this count, only its entry in BENILLA_OWNED. 81 -> 93.
|
||||
// Water Quality and Lava Glow add two more: 93 -> 95.
|
||||
// MONKEY (daylight: terrain torch casters): Terrain Blocks Torchlight. 95 -> 96.
|
||||
assert_eq!(checked, 96, "every tipped row carries a live key");
|
||||
// MONKEY (presets): Graphics Preset and Render Distance. +2.
|
||||
assert_eq!(checked, 98, "every tipped row carries a live key");
|
||||
assert_eq!(
|
||||
untipped,
|
||||
vec![
|
||||
|
||||
@@ -17,8 +17,9 @@ use benilla_formats::TILE_SIZE;
|
||||
/// Real-clock seconds between sweeps (housekeeping, not world time); also the stamp's resolution.
|
||||
const SWEEP_SECS: f32 = 1.0;
|
||||
|
||||
/// Five ADT tiles (2667 yd), the smallest round tile multiple clear of [`radius_floor`].
|
||||
const DEFAULT_RADIUS_YD: f32 = 5.0 * TILE_SIZE;
|
||||
/// Seven ADT tiles (3733 yd), the smallest round tile multiple clear of [`radius_floor`]
|
||||
/// (MONKEY (presets): five until `farclip`'s ceiling rose past 777).
|
||||
const DEFAULT_RADIUS_YD: f32 = 7.0 * TILE_SIZE;
|
||||
|
||||
/// The far corner of the widest tile block the residency window keeps (at the `farclip` clamp's
|
||||
/// maximum), plus a tile: a stamp is where the viewer stood, so no smaller radius is safe.
|
||||
@@ -223,8 +224,8 @@ mod tests {
|
||||
"default {DEFAULT_RADIUS_YD} must clear the floor {}",
|
||||
radius_floor()
|
||||
);
|
||||
// (26 + 1 + 16) chunks each way at farclip 777, to the corner, plus a tile: ~2560 yd.
|
||||
assert!((radius_floor() - 2560.0).abs() < 1.0, "{}", radius_floor());
|
||||
// (47 + 1 + 16) chunks each way at farclip 1497, to the corner, plus a tile: ~3550 yd.
|
||||
assert!((radius_floor() - 3550.3).abs() < 1.0, "{}", radius_floor());
|
||||
}
|
||||
|
||||
/// `$WOW_ART_RADIUS` is unset in the test binary, as in every ordinary run.
|
||||
|
||||
@@ -75,8 +75,18 @@ pub struct ViewDistance {
|
||||
|
||||
/// The settable range of [`ViewDistance::farclip`]: the reference's `farclip` clamp,
|
||||
/// `[0x81021c]` = 177 to `[0x80fed8]` = 777 in its validate callback `0x688d40`; shared by the
|
||||
/// CVar apply, the options row and `$WOW_FARCLIP`.
|
||||
pub const FARCLIP_RANGE: std::ops::RangeInclusive<f32> = 177.0..=777.0;
|
||||
/// CVar apply, the options rows and `$WOW_FARCLIP`.
|
||||
///
|
||||
/// MONKEY (presets) deviation: the ceiling is [`FARCLIP_MAX`], not 777. Modern fog
|
||||
/// (`fog_model::modern_fog_end`) stretches the fog end past 777 with the view distance, so a
|
||||
/// longer view is visible rather than fogged; the residency window and the model/particle walls
|
||||
/// all follow `farclip`, and the ceiling is what bounds their memory (see [`FARCLIP_MAX`]).
|
||||
pub const FARCLIP_RANGE: std::ops::RangeInclusive<f32> = 177.0..=FARCLIP_MAX;
|
||||
|
||||
/// MONKEY (presets): the extended `farclip` ceiling — the reference's slider grid (177 + n·60)
|
||||
/// carried on to n = 22. The residency window there is ±47 chunks, at most 7×7 ADT tiles against
|
||||
/// 777's 5×5: about twice the reference maximum's terrain, doodads and WMOs, and no more.
|
||||
pub const FARCLIP_MAX: f32 = 1497.0;
|
||||
|
||||
/// The settable range of [`ViewDistance::nearclip`]: the bounds in the reference's `nearclip`
|
||||
/// change callback `0x688d90`, `[0x8029d0]` = 0.01 and `[0x808300]` = 0.33.
|
||||
|
||||
Reference in New Issue
Block a user