fishing becomes whole: the bite, the line, the splash and the reel

- the whole loop plays: the line arcs rod tip to bobber, the bite splashes with its own sound, and clicking the bobber reels in
- the verdicts read like 1.12: yellow "your fish got away" lines, the reel-in sound, the fishing loot window
- "You feel rested." prints on the transition, rest states come from the client's own Exhaustion table, and level 60 trades the XP strip for the max-level rail
- the Skills pane lists what the real client lists: the hidden rows, the sort, gray numberless single-rank lines

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Sam
2026-08-07 16:03:16 +07:00
co-authored by Claude Fable 5
parent f35c3cb0f6
commit 19461d1e0f
81 changed files with 4052 additions and 660 deletions
@@ -450,3 +450,79 @@ fn region_alpha_is_its_own_and_multiplies_the_owner_frames() {
"a hidden region emits no quad"
);
}
/// **The texture-colour composition law** (wow-re `system/ui/scratch/texture-color-composition.md`,
/// VERIFIED): a region's own solid colour is a real *texel* (`SetTexture(r,g,b,a)` generates an 8×8
/// block at `+0xcc`), its vertex colour is a *separate* slot (`+0xb8`), and the draw
/// **multiplies** them per channel, alpha included — it does not replace.
///
/// This is the reference `SkillFrame` row trough, verbatim: declared `<Color 1,1,1,0.2>`, then
/// `SetVertexColor(0, 0, 0.75, 0.5)`'d. It draws at alpha `0.2 × 0.5 = 0.1`. benilla stored ONE
/// colour slot until this test, which made the second call replace the first and drew it at 0.5.
#[test]
fn a_solid_colour_texel_multiplies_with_the_vertex_colour() {
let mut s = script();
s.set_screen_size(800.0, 600.0);
s.run(
r#"
local f = CreateFrame("Frame", "Owner")
f:SetPoint("BOTTOMLEFT", 0, 0)
f:SetWidth(100) f:SetHeight(50)
trough = f:CreateTexture("Trough", "BACKGROUND")
trough:SetTexture(1, 1, 1, 0.2)
"#,
)
.unwrap();
s.resolve();
let solid = |s: &UiScript| {
s.extract()
.iter()
.find_map(|q| match &q.content {
QuadContent::Texture {
path: None, color, ..
} => Some(*color),
_ => None,
})
.expect("solid-colour quad")
};
assert_eq!(
solid(&s),
Some([1.0, 1.0, 1.0, 0.2]),
"untinted, the texel draws as declared"
);
s.run("trough:SetVertexColor(0, 0, 0.75, 0.5)").unwrap();
assert_eq!(
solid(&s),
Some([0.0, 0.0, 0.75, 0.1]),
"texel x vertex, alpha included: 0.2 x 0.5 = 0.1, NOT 0.5"
);
// The API readback is the vertex slot itself, not the product — the two are distinct storage.
assert_eq!(
s.eval::<(f32, f32, f32, f32)>("return trough:GetVertexColor()")
.unwrap(),
(0.0, 0.0, 0.75, 0.5)
);
// Art and a solid colour share the `+0xcc` slot: setting a path releases the generated texel,
// and what is left is the tint alone for the renderer to modulate the sample by.
s.run(r#"trough:SetTexture("Interface\\Bar.blp")"#).unwrap();
let tinted = s
.extract()
.iter()
.find_map(|q| match &q.content {
QuadContent::Texture {
path: Some(p),
color,
..
} if p.contains("Bar") => Some(*color),
_ => None,
})
.expect("art quad");
assert_eq!(
tinted,
Some([0.0, 0.0, 0.75, 0.5]),
"the vertex colour outlives the texel it was multiplying"
);
}