diff --git a/crates/benilla-app/assets/ui/OptionsFrame.xml b/crates/benilla-app/assets/ui/OptionsFrame.xml index 1431e746..7bbd9a60 100644 --- a/crates/benilla-app/assets/ui/OptionsFrame.xml +++ b/crates/benilla-app/assets/ui/OptionsFrame.xml @@ -545,6 +545,19 @@ torchShadowStrength = "Torch Shadow Darkness", everyFrame = "Every frame", hz = "%d per second", + -- GFX (interior rig): the ten rows of the interior lighting system a player + -- could not reach. Named for what they DO to a room, not for the quantity they + -- scale, since the quantity is the renderer's word and the room is the player's. + interiorAmbient = "Interior Ambient Light", + interiorDaylight = "Indoor Daylight", + interiorFill = "Interior Bounce Light", + interiorBakeFloor = "Unlit Room Floor", + interiorExposure = "Interior Light Budget", + interiorAttenScale = "Interior Light Reach", + interiorRoomGate = "Lights Stay in Their Room", + interiorShadowCasters = "Indoor Shadow Maps", + interiorShadowDynamic = "Moving Shadows Indoors", + interiorShadowEntityRate = "Moving Shadow Updates", interiorLight = "Dynamic Interior Lighting", interiorShadows = "Indoor Torch Shadows", exteriorShadows = "Outdoor Torch Shadows", @@ -709,6 +722,70 @@ .. "The default leaves some light standing, because this renderer has no " .. "bounce light to fill a shadow back in - at full strength the shadows " .. "read as scars cut into the floor rather than as shadows.", + INTERIOR_AMBIENT = + "The light a room has before any lamp, candle or window is counted - the " + .. "floor under everything else indoors.\n\n" + .. "At 0 a corner no light reaches is truly black. Raising it lifts every " + .. "indoor surface at once and flattens the room, so a little goes a long " + .. "way. Interior Darkness scales the result of all of this; this sets " + .. "what it scales.", + INDOOR_DAYLIGHT = + "Daylight for rooms the renderer cannot find a doorway into.\n\n" + .. "Some buildings describe no door, window or opening anywhere in their " + .. "art - the Goldshire inn's entry is one - so by day they stay as dark " + .. "as a cellar. This lifts those rooms while the sun is up, and is " + .. "exactly nothing at night, so the night look never moves. At 0 only " + .. "rooms with a findable opening get daylight.", + INTERIOR_BOUNCE = + "How much light each indoor flame throws back off the walls and floor " + .. "around it, standing in for light bouncing around a room.\n\n" + .. "Higher spreads a torch's glow further into the room and softens the " + .. "contrast between lit and unlit; 0 leaves only the direct light.", + UNLIT_ROOM_FLOOR = + "How much of a room's painted-in lighting survives where no lamp or " + .. "candle reaches it.\n\n" + .. "The original client draws every room at the lighting its artists " + .. "painted in. This one lets the actual lamps decide instead, which " + .. "leaves a room nobody lit rendering black - something the original " + .. "cannot do. This keeps a share of the painted lighting in exactly " + .. "those rooms. At 0 they go black.", + INTERIOR_LIGHT_BUDGET = + "How much light indoor sources are allowed to add before the result is " + .. "rolled back into range.\n\n" + .. "Higher makes lit rooms brighter and more contrasty; lower makes them " + .. "flatter and dimmer. This is the main dial for how dramatic indoor " + .. "lighting looks.", + INTERIOR_LIGHT_REACH = + "How far each indoor flame's light carries past the distance its artist " + .. "marked.\n\n" + .. "Those marks are where FULL brightness ends, not where light stops, so " + .. "at 100% a torch draws a hard-edged disc with blackness just beyond it. " + .. "Above that the light tails off smoothly instead. At 0 the limit is " + .. "ignored entirely and every indoor light reaches the whole room.", + LIGHTS_STAY_IN_THEIR_ROOM = + "Keeps an indoor light inside the rooms it belongs to, instead of lighting " + .. "anything nearby.\n\n" + .. "With this off, an inn's ground-floor candles light its cellar through " + .. "the floor. With it on, a room no light belongs to is left to the " + .. "settings above - so if a room looks wrongly dark, turning this off for " + .. "a moment tells you whether this is why.", + INDOOR_SHADOW_MAPS = + "How many indoor flames may cast real shadows at once.\n\n" + .. "The nearest get them; the rest still light the room, they just do not " + .. "cast. Each one costs memory and drawing time whether or not anything " + .. "is moving near it.", + MOVING_SHADOWS_INDOORS = + "How many of those flames also cast shadows from things that MOVE - you, " + .. "other players, creatures.\n\n" + .. "The nearest are promoted. Lower is cheaper and leaves moving " + .. "characters unshadowed by the more distant fires in a room.", + MOVING_SHADOW_UPDATES = + "How often the moving things casting indoor shadows are re-gathered, in " + .. "times per second.\n\n" + .. "This is the one indoor shadow setting that reliably costs frame time: " + .. "the gathering happens once a frame no matter how many maps or how " + .. "many flames are set above. Lower first if indoor shadows cost too " + .. "much.", TORCH_SOFT = "How soft the edge of a torch shadow is where the object meets the " .. "ground. Low is hard and crisp; high spreads the edge the way a real " @@ -851,6 +928,17 @@ torchShadowStrength = "Темнота теней от огня", everyFrame = "Каждый кадр", hz = "%d в секунду", + -- GFX (interior rig) + interiorAmbient = "Общий свет в помещениях", + interiorDaylight = "Дневной свет в помещениях", + interiorFill = "Отражённый свет в помещениях", + interiorBakeFloor = "Освещение комнат без источников", + interiorExposure = "Световой бюджет помещений", + interiorAttenScale = "Дальность света в помещениях", + interiorRoomGate = "Свет не проходит сквозь стены", + interiorShadowCasters = "Карты теней в помещениях", + interiorShadowDynamic = "Движущиеся тени в помещениях", + interiorShadowEntityRate = "Обновление движущихся теней", interiorLight = "Динамическое освещение помещений", interiorShadows = "Тени факелов в помещениях", exteriorShadows = "Тени огней под открытым небом", @@ -1022,6 +1110,70 @@ .. "По умолчанию часть света остаётся: этому движку нечем подсветить тень " .. "отражённым светом, и на полной силе тени выглядят как шрамы на полу, " .. "а не как тени.", + INTERIOR_AMBIENT = + "Свет, который есть в комнате ещё до всех ламп, свечей и окон — основа " + .. "всего освещения в помещении.\n\n" + .. "При 0 угол, куда не достаёт свет, по-настоящему чёрный. Увеличение " + .. "поднимает сразу все поверхности и делает комнату плоской, поэтому " + .. "хватает малого. «Темнота помещений» масштабирует итог; здесь задаётся " + .. "то, что она масштабирует.", + INDOOR_DAYLIGHT = + "Дневной свет для комнат, в которые движок не может найти вход.\n\n" + .. "В графике некоторых зданий нет ни двери, ни окна, ни проёма — таков, " + .. "например, вход таверны Златоземья, — и днём там темно, как в погребе. " + .. "Это подсвечивает такие комнаты, пока светит солнце, и ровно ничего не " + .. "делает ночью. При 0 дневной свет получают лишь комнаты с найденным " + .. "проёмом.", + INTERIOR_BOUNCE = + "Сколько света каждое пламя в помещении отбрасывает обратно от стен и " + .. "пола вокруг себя — замена отражённому свету.\n\n" + .. "Больше — свечение факела уходит дальше вглубь комнаты и смягчает " + .. "границу света и тьмы; 0 оставляет только прямой свет.", + UNLIT_ROOM_FLOOR = + "Сколько нарисованного художниками освещения комнаты сохраняется там, " + .. "куда не достаёт ни лампа, ни свеча.\n\n" + .. "Оригинальный клиент рисует каждую комнату с тем светом, который " + .. "нарисовали художники. Здесь решают сами лампы, и комната, которую " + .. "никто не освещает, становится чёрной — чего оригинал не умеет. Это " + .. "сохраняет долю нарисованного света именно в таких комнатах. При 0 они " + .. "чернеют.", + INTERIOR_LIGHT_BUDGET = + "Сколько света источникам в помещении позволено добавить, прежде чем " + .. "результат мягко возвращается в диапазон.\n\n" + .. "Больше — освещённые комнаты ярче и контрастнее; меньше — площе и " + .. "тусклее. Это главный регулятор того, насколько выразительно выглядит " + .. "свет в помещениях.", + INTERIOR_LIGHT_REACH = + "Насколько далеко свет каждого пламени в помещении уходит за отметку, " + .. "поставленную художником.\n\n" + .. "Эти отметки — там, где кончается ПОЛНАЯ яркость, а не сам свет, " + .. "поэтому при 100% факел рисует диск с резким краем и чернотой сразу за " + .. "ним. Выше — свет плавно сходит на нет. При 0 предел не учитывается " + .. "вовсе и свет достаёт до всей комнаты.", + LIGHTS_STAY_IN_THEIR_ROOM = + "Удерживает свет в тех комнатах, которым он принадлежит, вместо того " + .. "чтобы освещать всё поблизости.\n\n" + .. "Если выключено, свечи первого этажа таверны освещают подвал сквозь " + .. "пол. Если включено, комната без собственных источников остаётся на " + .. "настройках выше — поэтому, если комната кажется слишком тёмной, " + .. "выключите это на минуту и сразу поймёте, в этом ли дело.", + INDOOR_SHADOW_MAPS = + "Сколько источников пламени в помещении могут отбрасывать настоящие тени " + .. "одновременно.\n\n" + .. "Их получают ближайшие; остальные по-прежнему освещают комнату, просто " + .. "без теней. Каждая карта стоит памяти и времени отрисовки, даже если " + .. "рядом ничего не движется.", + MOVING_SHADOWS_INDOORS = + "Сколько из этих источников отбрасывают ещё и тени от ДВИЖУЩЕГОСЯ — вас, " + .. "других игроков, существ.\n\n" + .. "Повышаются ближайшие. Меньше — дешевле, и движущиеся персонажи не " + .. "отбрасывают тень от более далёких огней комнаты.", + MOVING_SHADOW_UPDATES = + "Сколько раз в секунду заново собирается движущееся, отбрасывающее тени " + .. "в помещении.\n\n" + .. "Это единственная настройка теней в помещении, которая заметно стоит " + .. "времени кадра: сбор происходит раз в кадр независимо от числа карт и " + .. "источников выше. Понижайте её первой, если тени в помещениях дороги.", TORCH_SOFT = "Насколько мягок край тени от огня там, где предмет касается пола. " .. "Низкое значение — резкий край, высокое — размытый, как от " @@ -1216,6 +1368,12 @@ "RowShadowFilter", "RowShadowCasterReach", "RowCharacterShadowRate", "RowWorldShadowRate", "RowTorchShadowDarkness", + -- GFX (interior rig): the ten interior-lighting rows, likewise appended. + "RowInteriorExposure", "RowInteriorAmbient", "RowInteriorDaylight", + "RowInteriorFill", "RowInteriorAttenScale", "RowInteriorBakeFloor", + "RowInteriorRoomGate", + "RowInteriorShadowCasters", "RowInteriorShadowDynamic", + "RowInteriorShadowEntityRate", }, -- Guild Names is a child of Player Names (UIOptionsFrame.lua:703-709). Nameplates = { @@ -1860,6 +2018,21 @@ GetCVar("worldShadows") ~= "0") local interior = GetCVar("interiorLight") ~= "0" OptionsRow_SetEnabled(getglobal(base .. "RowInteriorShadows"), interior) + -- GFX (interior rig): every row below shapes what the live-fixture lane draws, so + -- the lane's own switch is their master — the same rule `RowInteriorShadows` + -- above already follows. + for _, rkey in ipairs({"RowInteriorExposure", "RowInteriorAmbient", + "RowInteriorDaylight", "RowInteriorFill", "RowInteriorAttenScale", + "RowInteriorBakeFloor", "RowInteriorRoomGate"}) do + OptionsRow_SetEnabled(getglobal(base .. rkey), interior) + end + -- The three shadow budgets need the shadows themselves, which already need the + -- lane: a tighter master, and the honest one. + local indoorShadows = interior and GetCVar("interiorShadows") ~= "0" + for _, rkey in ipairs({"RowInteriorShadowCasters", "RowInteriorShadowDynamic", + "RowInteriorShadowEntityRate"}) do + OptionsRow_SetEnabled(getglobal(base .. rkey), indoorShadows) + end local torch = (interior and GetCVar("interiorShadows") ~= "0") or GetCVar("exteriorShadows") ~= "0" OptionsRow_SetEnabled(getglobal(base .. "RowTorchSoft"), torch) @@ -3552,11 +3725,125 @@ "BENILLA_TOOLTIP_INTERIOR_LIGHT") - + + + -- The CVar's own clamp, 0.25..8, and the + -- shipped 2.5 lands on a stop. No `fmt`: the value + -- IS a multiplier, so the default percent reads + -- "250%" and means it. + OptionsRow_OnLoad(self, "interiorExposure", + BENILLA_ADVGFX.interiorExposure, + "BENILLA_TOOLTIP_INTERIOR_LIGHT_BUDGET") + OptionsSliderRow_Setup(self, 0.25, 8, 0.25) + + + + + + + + + -- The groove offers 0..0.1 inside the CVar's + -- own 0..1, as `RowRenderScale` offers 50..200% + -- inside a 0.25..4 clamp: ambient is a FLOOR under + -- every indoor surface, the shipped value is + -- 0.015, and past about 0.1 a room is not lit, it + -- is fogged. `/console` still reaches the rest. + -- "travel", because the number has no unit a + -- player could read — the dial's own position is + -- the honest readout. + OptionsRow_OnLoad(self, "interiorAmbient", + BENILLA_ADVGFX.interiorAmbient, + "BENILLA_TOOLTIP_INTERIOR_AMBIENT") + OptionsSliderRow_Setup(self, 0, 0.1, 0.005, "travel") + + + + + + + + + -- In `interiorAmbient`'s own units, so it + -- takes that row's groove. Ships at 0, which is + -- the restore: the lane adds nothing until asked. + OptionsRow_OnLoad(self, "interiorDaylight", + BENILLA_ADVGFX.interiorDaylight, + "BENILLA_TOOLTIP_INDOOR_DAYLIGHT") + OptionsSliderRow_Setup(self, 0, 0.1, 0.005, "travel") + + + + + + + + + -- 0..2, the CVar's clamp; the shipped 0.08 + -- lands on a stop and reads "8%". + OptionsRow_OnLoad(self, "interiorFill", + BENILLA_ADVGFX.interiorFill, + "BENILLA_TOOLTIP_INTERIOR_BOUNCE") + OptionsSliderRow_Setup(self, 0, 2, 0.02) + + + + + + + + + -- 0..8, the CVar's clamp. 0 is not "no + -- light": it switches the authored window OFF, so + -- every fixture reaches the whole room — which is + -- why the description says so rather than letting + -- the left end read as darkness. + OptionsRow_OnLoad(self, "interiorAttenScale", + BENILLA_ADVGFX.interiorAttenScale, + "BENILLA_TOOLTIP_INTERIOR_LIGHT_REACH") + OptionsSliderRow_Setup(self, 0, 8, 0.1) + + + + + + + + + -- A SHARE of the batch's own bake, so the + -- default percent is the right readout: the + -- shipped 0.12 reads "12%". + OptionsRow_OnLoad(self, "interiorBakeFloor", + BENILLA_ADVGFX.interiorBakeFloor, + "BENILLA_TOOLTIP_UNLIT_ROOM_FLOOR") + OptionsSliderRow_Setup(self, 0, 1, 0.02) + + + + + + + + + -- Kept as a dial because a room the gate + -- leaves on ambient alone looks exactly like a + -- bug, and this tells the two apart in one click + -- — which is a thing to be able to do from the + -- options window, not only from `/console`. + OptionsRow_OnLoad(self, "interiorRoomGate", + BENILLA_ADVGFX.interiorRoomGate, + "BENILLA_TOOLTIP_LIGHTS_STAY_IN_THEIR_ROOM") + + + + + + + OptionsRow_OnLoad(self, "interiorShadows", BENILLA_ADVGFX.interiorShadows, @@ -3620,11 +3907,62 @@ OptionsSliderRow_Setup(self, 0, 1, 0.05) - + + + -- 1..16, the CVar's own clamp — the RESIDENT + -- map budget. "raw", because this is a count of + -- maps and a percentage of one would be nonsense. + OptionsRow_OnLoad(self, "interiorShadowCasters", + BENILLA_ADVGFX.interiorShadowCasters, + "BENILLA_TOOLTIP_INDOOR_SHADOW_MAPS") + OptionsSliderRow_Setup(self, 1, 16, 1, "raw") + + + + + + + + + -- 1..8 (`MAX_TORCH_DYNAMIC`), not the 0..16 the + -- registration's help line says: the observer + -- arm raises 0 to 1 and clamps to the cube-array + -- the depth pass actually has room for, so a + -- groove to 16 would offer seven stops that all + -- mean 8 and a zero that means 1. + OptionsRow_OnLoad(self, "interiorShadowDynamic", + BENILLA_ADVGFX.interiorShadowDynamic, + "BENILLA_TOOLTIP_MOVING_SHADOWS_INDOORS") + OptionsSliderRow_Setup(self, 1, 8, 1, "raw") + + + + + + + + + -- The same groove and the same `hz` readout as + -- the two sun-lane rate caps, inside this CVar's + -- wider 0..240; `0` is every frame there too. + -- This is the row that actually costs frame time + -- in the indoor lane — neither count dial above + -- moved it — which the description says. + OptionsRow_OnLoad(self, "interiorShadowEntityRate", + BENILLA_ADVGFX.interiorShadowEntityRate, + "BENILLA_TOOLTIP_MOVING_SHADOW_UPDATES") + OptionsSliderRow_Setup(self, 0, 120, 10, "hz") + + + + + + + -- Full 0..4 registry range, in five-percent steps. OptionsRow_OnLoad(self, "fireLightGain", diff --git a/crates/benilla-app/src/ui_script/options_tests.rs b/crates/benilla-app/src/ui_script/options_tests.rs index 2a149201..be873254 100644 --- a/crates/benilla-app/src/ui_script/options_tests.rs +++ b/crates/benilla-app/src/ui_script/options_tests.rs @@ -1980,6 +1980,122 @@ fn the_shadow_rig_rows_are_localised() { } } +/// GFX (interior rig): the ten interior-lighting rows. The thing to hold is the two-level master +/// — every row needs the live-fixture lane, and the three shadow budgets need the shadows on top +/// of it — because a budget row left live while its shadows are off is a dial that does nothing, +/// which is how `/console`-only settings earn their reputation. +#[test] +fn the_interior_rows_ride_the_lane_and_its_shadows_as_two_masters() { + benilla_formats::wow_data_or_skip!(); + let base = "BenillaOptionsFrameContainerBodyAdvancedGraphics"; + let mut s = audio_harness(); + s.set_cvar_host("interiorLight", "1"); + s.set_cvar_host("interiorShadows", "1"); + let mut s = harness_on(s); + s.run("ShowUIPanel(BenillaOptionsFrame) BenillaOptionsFrameCategoryListRowAdvancedGraphics:Click()") + .unwrap(); + + let live = |s: &mut UiScript, row: &str| { + s.eval::(&format!( + "local t = getglobal(\"{base}Row{row}ControlSliderThumb\") \ + if t then return t:IsShown() and true or false end \ + return getglobal(\"{base}Row{row}Check\"):IsEnabled() == 1" + )) + .unwrap() + }; + let readout = |s: &mut UiScript, row: &str| { + s.eval::(&format!("return {base}Row{row}ControlValue:GetText()")) + .unwrap() + }; + + // A count of shadow maps reads as a count, and a multiplier as a percentage of itself. + assert_eq!(readout(&mut s, "InteriorShadowCasters"), "12"); + assert_eq!(readout(&mut s, "InteriorExposure"), "250%"); + assert_eq!(readout(&mut s, "InteriorShadowEntityRate"), "30 per second"); + // Ambient has no unit a player could read, so the row reports the dial's own position: the + // shipped 0.015 is 15% of a 0..0.1 groove. + assert_eq!(readout(&mut s, "InteriorAmbient"), "15%"); + + for row in ["InteriorExposure", "InteriorAmbient", "InteriorRoomGate"] { + assert!(live(&mut s, row), "{row} is live with the lane on"); + } + + // Sub-master: the shadows alone, which must take the three budgets and leave the look rows. + s.run("SetCVar('interiorShadows', '0') OptionsPage_Refresh(\"AdvancedGraphics\")") + .unwrap(); + for row in [ + "InteriorShadowCasters", + "InteriorShadowDynamic", + "InteriorShadowEntityRate", + ] { + assert!(!live(&mut s, row), "{row} needs indoor shadows"); + } + assert!( + live(&mut s, "InteriorExposure"), + "the look rows do not need shadows, only the lane" + ); + + // Master: the lane itself takes everything, the three budgets included. + s.run("SetCVar('interiorLight', '0') OptionsPage_Refresh(\"AdvancedGraphics\")") + .unwrap(); + for row in [ + "InteriorExposure", + "InteriorAmbient", + "InteriorDaylight", + "InteriorFill", + "InteriorAttenScale", + "InteriorBakeFloor", + "InteriorRoomGate", + "InteriorShadowCasters", + ] { + assert!(!live(&mut s, row), "{row} needs the live-fixture lane"); + } + // Greyed, never rewritten: the lane coming back finds every value where it was left. + assert_eq!( + s.eval::("return GetCVar('interiorExposure')") + .unwrap(), + "2.5" + ); + assert!(s.errors().is_empty(), "script errors: {:?}", s.errors()); +} + +/// The interior rows' labels in both locales, as every Advanced Graphics row carries them. +#[test] +fn the_interior_rig_rows_are_localised() { + benilla_formats::wow_data_or_skip!(); + let base = "BenillaOptionsFrameContainerBodyAdvancedGraphics"; + for (locale, gate, maps) in [ + ("enUS", "Lights Stay in Their Room", "Indoor Shadow Maps"), + ( + "ruRU", + "Свет не проходит сквозь стены", + "Карты теней в помещениях", + ), + ] { + let s = audio_harness(); + s.run(&format!("function GetLocale() return '{locale}' end")) + .unwrap(); + let s = harness_on(s); + s.run("ShowUIPanel(BenillaOptionsFrame) BenillaOptionsFrameCategoryListRowAdvancedGraphics:Click()") + .unwrap(); + assert_eq!( + s.eval::(&format!("return {base}RowInteriorRoomGateLabel:GetText()")) + .unwrap(), + gate, + "{locale}" + ); + assert_eq!( + s.eval::(&format!( + "return {base}RowInteriorShadowCastersLabel:GetText()" + )) + .unwrap(), + maps, + "{locale}" + ); + assert!(s.errors().is_empty(), "{locale}: {:?}", s.errors()); + } +} + /// Pending edits are panel-wide, as the era's modified table: a category switch keeps them, and /// hiding the window discards them. #[test] @@ -2383,10 +2499,11 @@ fn water_quality_writes_numeric_tiers_with_localised_labels() { // rows append after those: 33 -> 37. // GFX (shadow rig): the five shadow rows a player could not reach — the filter, the // caster reach, the two update-rate caps and the torch darkness: 37 -> 42. + // GFX (interior rig): the ten interior-lighting rows: 42 -> 52. assert_eq!( s.eval::("return table.getn(OPTIONS_PAGE_ROWS.AdvancedGraphics)") .unwrap(), - 42 + 52 ); assert_eq!( s.eval::("return OPTIONS_PAGE_ROWS.AdvancedGraphics[4]") @@ -3424,6 +3541,48 @@ fn every_row_tooltip_key_resolves_in_the_real_global_strings() { "BENILLA_TOOLTIP_TORCH_SHADOW_DARKNESS", "AdvancedGraphicsRowTorchShadowDarkness", ), + // GFX (interior rig): the ten interior-lighting rows, likewise ours — 1.12 has none + // of these settings, so it has no string for any of them. + ( + "BENILLA_TOOLTIP_INTERIOR_LIGHT_BUDGET", + "AdvancedGraphicsRowInteriorExposure", + ), + ( + "BENILLA_TOOLTIP_INTERIOR_AMBIENT", + "AdvancedGraphicsRowInteriorAmbient", + ), + ( + "BENILLA_TOOLTIP_INDOOR_DAYLIGHT", + "AdvancedGraphicsRowInteriorDaylight", + ), + ( + "BENILLA_TOOLTIP_INTERIOR_BOUNCE", + "AdvancedGraphicsRowInteriorFill", + ), + ( + "BENILLA_TOOLTIP_INTERIOR_LIGHT_REACH", + "AdvancedGraphicsRowInteriorAttenScale", + ), + ( + "BENILLA_TOOLTIP_UNLIT_ROOM_FLOOR", + "AdvancedGraphicsRowInteriorBakeFloor", + ), + ( + "BENILLA_TOOLTIP_LIGHTS_STAY_IN_THEIR_ROOM", + "AdvancedGraphicsRowInteriorRoomGate", + ), + ( + "BENILLA_TOOLTIP_INDOOR_SHADOW_MAPS", + "AdvancedGraphicsRowInteriorShadowCasters", + ), + ( + "BENILLA_TOOLTIP_MOVING_SHADOWS_INDOORS", + "AdvancedGraphicsRowInteriorShadowDynamic", + ), + ( + "BENILLA_TOOLTIP_MOVING_SHADOW_UPDATES", + "AdvancedGraphicsRowInteriorShadowEntityRate", + ), ("BENILLA_TOOLTIP_RENDER_SCALE", "GraphicsRowRenderScale"), // GFX (anisotropic): ours, because 1.12's own text describes a four-stop slider // that needs a restart and this row is neither. @@ -3573,7 +3732,8 @@ fn every_row_tooltip_key_resolves_in_the_real_global_strings() { // GFX: Trilinear Filtering and Multisampling, the two other video rows a player could not // reach. 117 -> 119. // GFX (shadow rig): the five Advanced Graphics shadow rows. 119 -> 124. - assert_eq!(checked, 124, "every tipped row carries a live key"); + // GFX (interior rig): the ten interior-lighting rows. 124 -> 134. + assert_eq!(checked, 134, "every tipped row carries a live key"); assert_eq!( untipped, vec![ @@ -3680,7 +3840,8 @@ fn every_flavor_of_row_raises_its_plate_from_the_page_it_lives_on() { // GFX (anisotropic): the Graphics page's anisotropy slider. 116 -> 117. // GFX: Trilinear Filtering and Multisampling. 117 -> 119. // GFX (shadow rig): the five Advanced Graphics shadow rows. 119 -> 124. - assert_eq!(raised, 124, "every row but Auto Loot raises a description"); + // GFX (interior rig): the ten interior-lighting rows. 124 -> 134. + assert_eq!(raised, 134, "every row but Auto Loot raises a description"); } /// 1.12's AdvancedOptionsCombatText box as saved-global rows: a click writes the global, never a