the guild window opens: roster, ranks, notes and the message of the day
- the social window's third tab is live — the roster in both its views, the member card with its promote/demote/remove verbs, the rank editor's thirteen permissions, the guild information board, and the invite, leave, kick and note dialogs behind them - setting your hearthstone at an innkeeper now asks before it takes and says so afterwards, and the innkeeper's gossip lines get their own icons back - unavailable talents grey out, and every greyed icon in the client now goes through the same luminance fold the original used - the buff tooltip's remaining-time line reads gold with the client's own hour/minute ladder, the macro editor takes a shift-click to paste a spell, trainer rows repaint after a purchase, and a long book page stops flickering its ellipsis Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,65 @@ fn ui_cost_enabled() -> bool {
|
||||
*ON.get_or_init(|| std::env::var("WOW_UI_COST").as_deref() == Ok("1"))
|
||||
}
|
||||
|
||||
/// `WOW_UI_GATE=1` — the extract gate's miss reporter ([`report_gate_miss`]). Off by default and
|
||||
/// read once, the `[ui-cost]` meter's posture.
|
||||
fn gate_log_enabled() -> bool {
|
||||
static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
|
||||
*ON.get_or_init(|| std::env::var("WOW_UI_GATE").as_deref() == Ok("1"))
|
||||
}
|
||||
|
||||
/// One line naming why the extract gate did not skip this frame — the input that differed, and for
|
||||
/// the render list the first entry that moved. A settled UI is what makes the paint pass free; when
|
||||
/// it is never settled, this says what is moving.
|
||||
fn report_gate_miss(
|
||||
script: &UiScript,
|
||||
now: &[benilla_ui::script::ExtractedQuad],
|
||||
prev: &[benilla_ui::script::ExtractedQuad],
|
||||
dims_eq: bool,
|
||||
text_ui_eq: bool,
|
||||
portraits_eq: bool,
|
||||
) {
|
||||
if !dims_eq {
|
||||
eprintln!("[ui-gate] miss: window/scale changed");
|
||||
return;
|
||||
}
|
||||
if !text_ui_eq {
|
||||
eprintln!("[ui-gate] miss: focused editbox text-UI changed");
|
||||
return;
|
||||
}
|
||||
if !portraits_eq {
|
||||
eprintln!("[ui-gate] miss: portrait sources changed");
|
||||
return;
|
||||
}
|
||||
if now.len() != prev.len() {
|
||||
eprintln!(
|
||||
"[ui-gate] miss: render list {} -> {} entries",
|
||||
prev.len(),
|
||||
now.len()
|
||||
);
|
||||
return;
|
||||
}
|
||||
match now.iter().zip(prev).position(|(a, b)| a != b) {
|
||||
Some(i) => {
|
||||
let (a, b) = (&now[i], &prev[i]);
|
||||
let (was, now_s) = (format!("{b:?}"), format!("{a:?}"));
|
||||
let owner = match a.target {
|
||||
benilla_ui::order::ZTarget::Frame(fh) => script.frame_name(fh),
|
||||
benilla_ui::order::ZTarget::Region(_) => None,
|
||||
};
|
||||
eprintln!(
|
||||
"[ui-gate] miss: entry {i}/{} target={:?} name={owner:?}\n was {}\n now {}",
|
||||
now.len(),
|
||||
a.target,
|
||||
&was[..was.len().min(280)],
|
||||
&now_s[..now_s.len().min(280)],
|
||||
);
|
||||
}
|
||||
// Nothing in this pass's own inputs moved — the miss came from the capture-mode arm.
|
||||
None => eprintln!("[ui-gate] miss: capture mode (the gate never skips under a capture)"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Last frame's extract-gate inputs (decision 0740), held together as one `Local` — the gate
|
||||
/// compares all four or none, and a Bevy system has a hard param budget this was eating.
|
||||
#[derive(Default)]
|
||||
@@ -375,6 +434,19 @@ pub(super) fn drive_script(
|
||||
}
|
||||
return;
|
||||
}
|
||||
// `WOW_UI_GATE=1` — why the gate MISSED. A skip is worth the whole conversion, so the
|
||||
// question "what moved?" is where a per-frame cost always ends, and a `Vec` inequality does
|
||||
// not answer it. Off by default, like the `[ui-cost]` meter beside it.
|
||||
if gate_log_enabled() {
|
||||
report_gate_miss(
|
||||
&script,
|
||||
&extracted,
|
||||
&prev.extracted,
|
||||
prev.dims == Some(dims),
|
||||
text_ui == prev.text_ui,
|
||||
booths.images.0 == prev.portraits,
|
||||
);
|
||||
}
|
||||
prev.dims = Some(dims);
|
||||
prev.text_ui = text_ui.clone();
|
||||
prev.portraits = booths.images.0.clone();
|
||||
@@ -436,6 +508,7 @@ pub(super) fn drive_script(
|
||||
circular,
|
||||
portrait_unit,
|
||||
rotation,
|
||||
desaturated,
|
||||
} => {
|
||||
// A live unit portrait (`SetPortraitTexture(region, unit)`): sample this token's
|
||||
// source ([`crate::portrait::PortraitImages`]) — the off-screen model bake, or the
|
||||
@@ -555,6 +628,7 @@ pub(super) fn drive_script(
|
||||
// The engine's SetRotation is counterclockwise-positive; the quad pass spins
|
||||
// clockwise-on-screen (`UiQuad::rotation`) — negate to convert.
|
||||
rotation: -rotation,
|
||||
desaturated,
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
||||
@@ -155,6 +155,7 @@ pub(super) fn emit(
|
||||
color: [hc[0], hc[1], hc[2], hc[3] * host.alpha],
|
||||
additive: false,
|
||||
circular: false,
|
||||
desaturated: false,
|
||||
clip: text_clip,
|
||||
rotation: 0.0,
|
||||
mask: None,
|
||||
@@ -171,10 +172,11 @@ pub(super) fn emit(
|
||||
// gate inside is geometric and needs no fixed-vs-measured flag. Computed before the shadow
|
||||
// so the shadow pass draws the same display string (the client's shadow is a second draw of
|
||||
// the same truncated CGxString).
|
||||
let ellipsized = if ebox.is_none() && !matches!(host.target, ZTarget::Frame(_)) {
|
||||
crate::ui_text::ellipsize_to_fit(atlas, draw_text, draw_rect, spec)
|
||||
} else {
|
||||
None
|
||||
let ellipsized = match host.target {
|
||||
ZTarget::Region(region) if ebox.is_none() => {
|
||||
crate::ui_text::ellipsize_to_fit(atlas, region, draw_text, draw_rect, spec)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
if let Some(display) = ellipsized.as_deref() {
|
||||
draw_text = display;
|
||||
@@ -339,6 +341,7 @@ pub(super) fn emit(
|
||||
color: [1.0, 1.0, 1.0, host.alpha],
|
||||
additive: false,
|
||||
circular: false,
|
||||
desaturated: false,
|
||||
clip: text_clip,
|
||||
rotation: 0.0,
|
||||
mask: None,
|
||||
|
||||
Reference in New Issue
Block a user