Files
benilla-octo/crates/benilla-formats/tests/light_no_row_fallback.rs
T
SamandClaude Fable 5 6b980b9efb the player build: one binary that carries its content
- a player build ships beside the dev one — dev chords and probe surfaces go dark, and a gate enforces what a player can still reach, by medium not by module
- the binary carries its own shaders and its shipped UI, finds the WoW folder by itself, and keeps its local state in one benilla-config folder
- the glue screens get a capture shutter, and the engine stops depending on the protocol crate

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 18:18:02 +07:00

124 lines
5.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! **A map with no `Light.dbc` row of its own falls back to record ID 1, not to an invention.**
//!
//! `Light.dbc` carries rows for 26 map ids; **Deeprun Tram (369) has none** — not even the
//! falloff-0 `(0,0,0)` global that maps 0/1 carry. The client handles that in
//! `dn_light_array_build 0x6d6170`: the per-map filter (`0x6d61a9 cmp [row+4], mapId`) matches
//! nothing, and the tail (`0x6d62b2`–`0x6d62c9`) writes `idMap[1]` — the record whose **ID column**
//! is 1, not row-index 1 — into slot 0. `dn_light_select 0x6d2d00` then no-ops (count ≤ 1 ⇒ empty
//! blend heap) and the colour table commits that record whole. (wow-re
//! `system/lighting/scratch/no-light-row-fallback.md`.)
//!
//! Row 1 is the Azeroth global → LightParams **12**, whose bands are an ordinary six-key day curve.
//! Before this was wired, benilla substituted a hardcoded bright noon (fog `[140,183,234]` at
//! 1000 yd) and the Tram's undersea stretch rendered as a daylight scene against the reference's
//! deep blue. The numbers below are read straight off the shipped DBC — LightParams 12 → int bands
//! 199..216 (sub-0 diffuse, sub-1 ambient, sub-7 fog) and float bands 67..72 (sub-0 fog end in
//! inches, sub-1 start fraction) — so they pin the *identity of the record*, which is the part that
//! was wrong.
use benilla_formats::{Chain, LightCatalog, Submersion};
/// `Map.dbc` id of Deeprun Tram — the WMO-only map with no `Light.dbc` row.
const MAP_DEEPRUN_TRAM: u32 = 369;
/// Half-minutes; 1440 = noon.
const NOON: u32 = 1440;
#[test]
fn deeprun_tram_takes_light_record_1_not_an_invented_default() {
let data = benilla_formats::wow_data_or_skip!();
let mut chain = Chain::open(&data).expect("open vanilla patch chain");
let cat = LightCatalog::load(&mut chain).expect("load Light/LightParams/*Band");
// The Tram's own position is irrelevant — nothing covers it, which is the whole point.
let tram = cat.sample_blended(
MAP_DEEPRUN_TRAM,
[25.0, -1256.0, -117.0],
NOON,
false,
Submersion::Dry,
false,
);
// LightParams 12 at noon, off the shipped bands.
let rgb = |c: [f32; 3]| c.map(|v| (v * 255.0).round() as i32);
assert_eq!(
rgb(tram.fog_color),
[77, 120, 143],
"fog must be LightParams 12's own band, not the old [140,183,234] invention"
);
assert_eq!(rgb(tram.ambient), [104, 130, 154], "ambient = band sub-1");
assert_eq!(rgb(tram.sun_diffuse), [255, 136, 0], "diffuse = band sub-0");
assert!(
(tram.fog_end - 500.0).abs() < 1.0,
"fog end {} should be 18000 inches / 36 = 500 yd (was 1000)",
tram.fog_end
);
assert!(
(tram.fog_start_frac - 0.25).abs() < 1e-3,
"start fraction {} should be 0.25 (was 0.40)",
tram.fog_start_frac
);
// The fallback is a RECORD LOOKUP, not a per-map constant: every map with no row of its own
// lands on the same record, regardless of id or sample position. (Deliberately not compared
// against map 0 sampled at its origin — local Azeroth spheres cover that point and blend over
// the global, so it resolves [62,96,102], not the bare record.)
let other_rowless = cat.sample_blended(
4242,
[9000.0, -1000.0, 60.0],
NOON,
false,
Submersion::Dry,
false,
);
assert_eq!(
rgb(other_rowless.fog_color),
rgb(tram.fog_color),
"any map with no Light.dbc row takes the same record 1"
);
// And it is a real day curve, not a frozen constant: midnight differs from noon.
let midnight = cat.sample_blended(
MAP_DEEPRUN_TRAM,
[25.0, -1256.0, -117.0],
0,
false,
Submersion::Dry,
false,
);
assert_ne!(
rgb(midnight.fog_color),
rgb(tram.fog_color),
"the fallback runs the ordinary day/night pipeline, so the hours must differ"
);
}
/// The scoping guard for the fix: the seven shipped maps that carry positioned rows but **no**
/// `(0,0,0)` global are a *different* case, and one the byte finding does not settle (the reference
/// seeds the array count at 1 and fills slots 1.. with the positioned rows, leaving slot 0
/// unwritten). They must not be swept into the record-1 fallback on the strength of a guess.
#[test]
fn maps_with_positioned_rows_but_no_global_are_left_alone() {
let data = benilla_formats::wow_data_or_skip!();
let mut chain = Chain::open(&data).expect("open vanilla patch chain");
let cat = LightCatalog::load(&mut chain).expect("load Light/LightParams/*Band");
// Map 169 (Emerald Dream) carries 4 rows, none of them a global. Sampled far from all of them,
// it must NOT come back as record 1's atmosphere.
let far = cat.sample_blended(169, [0.0, 0.0, 0.0], NOON, false, Submersion::Dry, false);
let tram = cat.sample_blended(
MAP_DEEPRUN_TRAM,
[25.0, -1256.0, -117.0],
NOON,
false,
Submersion::Dry,
false,
);
let rgb = |c: [f32; 3]| c.map(|v| (v * 255.0).round() as i32);
assert_ne!(
rgb(far.fog_color),
rgb(tram.fog_color),
"a map WITH rows must not inherit the zero-match fallback"
);
}