Files
benilla-octo/crates/benilla-app/src/ui_script/fade_tests.rs
T
SamandClaude Fable 5 9bd2609740 windows take turns, the gossip page waits, the shot bar counts down
- the UIPanel registry is re-derived from the reference's bytes: the readable-item page finally registers, so a note or letter closes when a gossip window opens instead of interleaving with it line for line; the character sheet takes its real pushable-2 seat (an NPC window slides it over instead of replacing it), and the spellbook, quest log, talents and character sheet open for dead players again — a refused open now says "You can't do that when you're dead." instead of doing nothing
- the gossip window waits for its greeting: the first packet carries only the options and a text id, and we used to show the frame with a blank page for the round trip the text needed — the menu now exists only once the text has answered, a mid-flight click is refused, and a revisit is instant off the cache
- a bag slot whose stack count changes on the server now fires `ITEM_LOCK_CHANGED` just ahead of its `BAG_UPDATE` — the one signal 1.12 hunter addons use to detect a spent arrow — so Quiver's auto-shot bar drains per shot instead of pinning at 100%; a swap or create deliberately does not fire it, so rearranging bags can't read as a shot
- the outdoor minimap tiles' gamma byte reaches the screen exactly once — the ordinary decode arm learns the same skip the fast path already had

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

120 lines
4.2 KiB
Rust

//! The fade kit's driver (`UIFrameFade` / `BenillaFadeDriver`, UiPanels.xml) — the park/wake
//! lifecycle that keeps the per-frame fade walk off the tick while nothing is fading. Split from
//! `panel_tests.rs` (1507's round): the slot manager and the fade kit share a source file, not a
//! concern.
use benilla_ui::script::UiScript;
/// Load one shipped `assets/ui/<file>` into `s`, panicking on any loader error — the same
/// per-file loader every `ui_script` test file carries (these tests only ever need
/// `UiPanels.xml`, where the fade kit lives).
fn load_xml(s: &UiScript, file: &str) {
let text = std::fs::read_to_string(
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("assets/ui")
.join(file),
)
.unwrap();
let doc = benilla_ui::framexml::parse(&text).unwrap();
let report = benilla_ui::loader::load(s, &doc, &|_| None);
assert!(
report.errors.is_empty(),
"{file}: loader errors: {:?}",
report.errors
);
}
/// **The idle fade driver parks itself** (decision 1396's class, the audit's driver-hide item):
/// with `FADEFRAMES` empty there is nothing to walk, so the driver's OnUpdate hides its own frame
/// — and the engine dispatches OnUpdate only to visible frames (`tick.rs`), which stops the
/// per-frame walk cold. The probe wraps `UIFrameFadeUpdate` in a counter BEFORE the first tick:
/// ten idle ticks later it has never run, and the driver is hidden.
#[test]
fn an_idle_fade_driver_parks_itself_off_the_tick() {
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_xml(&s, "UiPanels.xml");
s.resolve();
s.run(
"BENILLA_TEST_FADE_TICKS = 0\n\
local real = UIFrameFadeUpdate\n\
function UIFrameFadeUpdate(elapsed)\n\
BENILLA_TEST_FADE_TICKS = BENILLA_TEST_FADE_TICKS + 1\n\
real(elapsed)\n\
end",
)
.unwrap();
for _ in 0..10 {
s.tick(0.016);
s.resolve();
}
assert!(s.errors().is_empty(), "{:?}", s.errors());
assert!(
!s.eval::<bool>("return BenillaFadeDriver:IsShown()")
.unwrap(),
"an empty FADEFRAMES parks the driver"
);
assert_eq!(
s.eval::<i64>("return BENILLA_TEST_FADE_TICKS").unwrap(),
0,
"a parked driver never runs the fade walk"
);
}
/// The control for the guard above: the gate must not cost the fades it gates. `UIFrameFade` (the
/// kit's single insertion point — `UIFrameFadeIn`/`Out` both land there) wakes the driver beside
/// its `table.insert`; the fade then ramps on real ticks, completes, and the driver re-parks on
/// the first empty tick after.
#[test]
fn a_started_fade_still_ramps_and_the_driver_reparks_after() {
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_xml(&s, "UiPanels.xml");
s.resolve();
s.run(r#"CreateFrame("Frame", "BenillaFadeProbe")"#)
.unwrap();
for _ in 0..2 {
s.tick(0.016); // settle: the driver parks
s.resolve();
}
assert!(!s
.eval::<bool>("return BenillaFadeDriver:IsShown()")
.unwrap());
s.run("UIFrameFadeIn(BenillaFadeProbe, 1.0)").unwrap();
assert!(
s.eval::<bool>("return BenillaFadeDriver:IsShown()")
.unwrap(),
"starting a fade wakes the driver"
);
assert_eq!(
s.eval::<f64>("return BenillaFadeProbe:GetAlpha()").unwrap(),
0.0,
"the fade armed at its IN startAlpha"
);
s.tick(0.5);
s.resolve();
let mid = s.eval::<f64>("return BenillaFadeProbe:GetAlpha()").unwrap();
assert!(
(mid - 0.5).abs() < 1e-3,
"half the timeToFade in, half the ramp: {mid}"
);
s.tick(0.6); // past timeToFade: the fade completes and leaves the list
s.resolve();
assert_eq!(
s.eval::<f64>("return BenillaFadeProbe:GetAlpha()").unwrap(),
1.0,
"the fade completes at its endAlpha"
);
s.tick(0.016); // the first empty tick re-parks the driver
s.resolve();
assert!(
!s.eval::<bool>("return BenillaFadeDriver:IsShown()")
.unwrap(),
"the driver re-parks once the list is empty"
);
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
}