benilla-ui: unused globals and queues removed, one true/nil helper, three tests that test their claim (#305)
Code health in `benilla-ui`, found while its comments were rewritten. No behaviour a player can see changes.
- **Unused globals and queues removed.**
- The Rust `ChatFrame_OpenChat` and `ChatFrame_SendTell` queued text that nothing drained. The stock `ChatFrame.lua` defines both globals and replaces them when it loads, as the reference's FrameXML does.
- `BenillaGetItemStats` had no Lua caller. Its test of the item cache's miss, ask and push now runs through `GetItemInfo`, which shares that path.
- `UiScript::queue_social_request` had no caller.
- **One true/nil helper.** Seven modules each carried their own copy of `binding_abi::flag`, the reference's `1`-or-nil boolean. They call it now.
- **Three tests test their claim.**
- `click_focuses_regardless_of_autofocus_…` built both boxes with autoFocus on. One is now off.
- `level_shift_saturates_at_zero` never saturated. Its child now drops past 0.
- The layout profiler's `plan` bucket was never set, so it always printed 0. It is gone.
- **Smaller fixes.** An empty `if buyback {}` branch is gone, and a doc comment no longer sits on a `use` line instead of on `install_justify`.
Checked with `cargo clippy -p benilla-ui --all-targets -D warnings` and `cargo test -p benilla-ui` (1513 passed) before the land gates.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
co-authored by
Claude Opus 5.5
parent
036e3d0240
commit
9863554f69
@@ -1,4 +1,4 @@
|
||||
//! The chat window settings bindings: `GetChatWindowInfo`, its setters and `ChatFrame_OpenChat`.
|
||||
//! The chat window settings bindings: `GetChatWindowInfo` and its setters.
|
||||
//!
|
||||
//! The reference keeps one record per window (`0xb4fe50`, stride 0x98, 10 slots), loaded from and
|
||||
//! saved to `chat-cache.txt`: name `+0x00`, message-group flags `+0x20`, channel names and ids
|
||||
@@ -331,13 +331,6 @@ impl super::UiScript {
|
||||
}
|
||||
}
|
||||
|
||||
impl super::UiScript {
|
||||
/// Drains the texts `ChatFrame_OpenChat` queued since the last call.
|
||||
pub fn take_open_chat_requests(&mut self) -> Vec<String> {
|
||||
std::mem::take(&mut self.model_mut().open_chat_requests)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
// GetChatWindowInfo(id) → name, fontSize, r, g, b, a, shown, locked, docked.
|
||||
lua.globals().set(
|
||||
@@ -470,19 +463,6 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
})?,
|
||||
)?;
|
||||
|
||||
// ChatFrame_OpenChat(text, chatFrame) queues `text` for the host to open the chat box with. The
|
||||
// frame is inert: in 1.12 every chat frame's `editBox` is the one `ChatFrameEditBox`
|
||||
// (FloatingChatFrame.lua:30, FloatingChatFrame.xml:742). The stock ChatFrame.lua:1545 defines
|
||||
// the same global and replaces this one when it loads.
|
||||
lua.globals().set(
|
||||
"ChatFrame_OpenChat",
|
||||
lua.create_function(|lua, (text, _chat_frame): (Option<String>, Value)| {
|
||||
let mut model = lua.app_data_mut::<Model>().expect("model app_data");
|
||||
model.open_chat_requests.push(text.unwrap_or_default());
|
||||
Ok(())
|
||||
})?,
|
||||
)?;
|
||||
|
||||
// ── The rest of the record: name, shown, message groups, channels ──────────────────────
|
||||
//
|
||||
// `SetChatWindowName` `0x4a13f0`, `SetChatWindowShown` `0x4a1730`,
|
||||
@@ -930,21 +910,6 @@ mod tests {
|
||||
s.run("SetChatWindowSize(1, 0)").unwrap();
|
||||
assert!(s.take_chat_window_changes().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chat_frame_open_chat_queues_its_text_and_ignores_the_frame() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.run("ChatFrame_OpenChat('/w Bob ')").unwrap();
|
||||
s.run("ChatFrame_OpenChat('', 'not even a frame')").unwrap();
|
||||
assert_eq!(
|
||||
s.take_open_chat_requests(),
|
||||
vec!["/w Bob ".to_string(), String::new()]
|
||||
);
|
||||
assert!(
|
||||
s.take_open_chat_requests().is_empty(),
|
||||
"the drain is a take"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
use mlua::{Lua, MultiValue, Value, Variadic};
|
||||
|
||||
use super::binding_abi::number_arg;
|
||||
use super::binding_abi::{self, number_arg};
|
||||
use super::item_stats::item_link;
|
||||
use super::Model;
|
||||
|
||||
@@ -177,15 +177,6 @@ fn opt_str(lua: &Lua, s: Option<&String>) -> mlua::Result<Value> {
|
||||
})
|
||||
}
|
||||
|
||||
/// A `bool` as the reference's `1` or nil.
|
||||
fn era_bool(b: bool) -> Value {
|
||||
if b {
|
||||
Value::Integer(1)
|
||||
} else {
|
||||
Value::Nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Register the craft globals.
|
||||
pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
let g = lua.globals();
|
||||
@@ -362,7 +353,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
if let Some(r) = recipe(&model, index) {
|
||||
for (name, have) in &r.tools {
|
||||
out.push(Value::String(lua.create_string(name)?));
|
||||
out.push(era_bool(*have));
|
||||
out.push(binding_abi::flag(*have));
|
||||
}
|
||||
}
|
||||
Ok(MultiValue::from_vec(out))
|
||||
|
||||
@@ -178,15 +178,16 @@ fn click_focuses_regardless_of_autofocus_and_transition_order_is_lost_then_gaine
|
||||
s.run(
|
||||
r#"
|
||||
log = {}
|
||||
local function wire(name, y)
|
||||
local function wire(name, y, autoFocus)
|
||||
local f = CreateFrame("EditBox", name)
|
||||
f:SetAutoFocus(autoFocus)
|
||||
f:SetPoint("BOTTOMLEFT", nil, "BOTTOMLEFT", 0, y)
|
||||
f:SetWidth(100); f:SetHeight(20)
|
||||
f:SetScript("OnEditFocusGained", function() table.insert(log, "gained"..name) end)
|
||||
f:SetScript("OnEditFocusLost", function() table.insert(log, "lost"..name) end)
|
||||
end
|
||||
wire("A", 0) -- rect bottom 0..20
|
||||
wire("B", 100) -- rect bottom 100..120
|
||||
wire("A", 0, true) -- rect bottom 0..20
|
||||
wire("B", 100, false) -- rect bottom 100..120: a click focuses it without autoFocus
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
use mlua::{Lua, MultiValue, Value};
|
||||
|
||||
use super::binding_abi;
|
||||
use super::Model;
|
||||
|
||||
/// The thirteen rank rights in `GuildControlGetRankFlags`' return order, which is also the
|
||||
@@ -258,16 +259,6 @@ fn truthy(value: &Value) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// The 1.12 boolean, `1` or `nil`: the FrameXML stores what it tests, so a `false` would survive
|
||||
/// where the reference's `nil` does not.
|
||||
fn era_bool(on: bool) -> Value {
|
||||
if on {
|
||||
Value::Integer(1)
|
||||
} else {
|
||||
Value::Nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the player's own rank holds right `index`, 1-based into [`RANK_RIGHT_BITS`].
|
||||
fn has_right(model: &Model, index: usize) -> bool {
|
||||
model.guild.in_guild && model.guild.rights & RANK_RIGHT_BITS[index - 1] != 0
|
||||
@@ -305,7 +296,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
"IsInGuild",
|
||||
lua.create_function(|lua, ()| {
|
||||
let model = lua.app_data_ref::<Model>().expect("model app_data");
|
||||
Ok(era_bool(model.guild.in_guild))
|
||||
Ok(binding_abi::flag(model.guild.in_guild))
|
||||
})?,
|
||||
)?;
|
||||
|
||||
@@ -314,7 +305,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
"IsGuildLeader",
|
||||
lua.create_function(|lua, ()| {
|
||||
let model = lua.app_data_ref::<Model>().expect("model app_data");
|
||||
Ok(era_bool(model.guild.is_leader))
|
||||
Ok(binding_abi::flag(model.guild.is_leader))
|
||||
})?,
|
||||
)?;
|
||||
|
||||
@@ -375,7 +366,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
Value::String(lua.create_string(&m.zone)?),
|
||||
Value::String(lua.create_string(&m.note)?),
|
||||
Value::String(lua.create_string(&m.officer_note)?),
|
||||
era_bool(m.online),
|
||||
binding_abi::flag(m.online),
|
||||
Value::String(lua.create_string(&m.status)?),
|
||||
]))
|
||||
})?,
|
||||
@@ -444,7 +435,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
"GetGuildRosterShowOffline",
|
||||
lua.create_function(|lua, ()| {
|
||||
let model = lua.app_data_ref::<Model>().expect("model app_data");
|
||||
Ok(era_bool(model.guild.show_offline))
|
||||
Ok(binding_abi::flag(model.guild.show_offline))
|
||||
})?,
|
||||
)?;
|
||||
// With no argument it turns show-offline on, unlike an explicit `nil`, hence `MultiValue`.
|
||||
@@ -593,7 +584,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
global,
|
||||
lua.create_function(move |lua, ()| {
|
||||
let model = lua.app_data_ref::<Model>().expect("model app_data");
|
||||
Ok(era_bool(has_right(&model, index)))
|
||||
Ok(binding_abi::flag(has_right(&model, index)))
|
||||
})?,
|
||||
)?;
|
||||
}
|
||||
@@ -653,7 +644,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
Ok(MultiValue::from_vec(
|
||||
RANK_RIGHT_BITS
|
||||
.iter()
|
||||
.map(|bit| era_bool(rights & bit != 0))
|
||||
.map(|bit| binding_abi::flag(rights & bit != 0))
|
||||
.collect(),
|
||||
))
|
||||
})?,
|
||||
|
||||
@@ -448,39 +448,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
})?,
|
||||
)?;
|
||||
|
||||
// BenillaGetItemStats(itemId) → name, quality, invType, class, subclass, dmgMin, dmgMax,
|
||||
// dmgType, delayMs, armor, block, sellPrice, or nil and an ask.
|
||||
lua.globals().set(
|
||||
"BenillaGetItemStats",
|
||||
lua.create_function(|lua, item_id: u32| {
|
||||
let view = {
|
||||
let mut model = lua.app_data_mut::<Model>().expect("model app_data");
|
||||
let v = model.item_templates.get(&item_id).cloned();
|
||||
if v.is_none() && item_id != 0 {
|
||||
model.item_stat_asks.insert(item_id);
|
||||
}
|
||||
v
|
||||
};
|
||||
let Some(v) = view else {
|
||||
return Ok(MultiValue::from_vec(vec![Value::Nil]));
|
||||
};
|
||||
let (dmg_min, dmg_max, dmg_type) = v.damages.first().copied().unwrap_or_default();
|
||||
Ok(MultiValue::from_vec(vec![
|
||||
Value::String(lua.create_string(&v.name)?),
|
||||
Value::Integer(i64::from(v.quality)),
|
||||
Value::Integer(i64::from(v.inventory_type)),
|
||||
Value::Integer(i64::from(v.class)),
|
||||
Value::Integer(i64::from(v.subclass)),
|
||||
Value::Number(f64::from(dmg_min)),
|
||||
Value::Number(f64::from(dmg_max)),
|
||||
Value::Integer(i64::from(dmg_type)),
|
||||
Value::Integer(i64::from(v.delay_ms)),
|
||||
Value::Integer(i64::from(v.armor)),
|
||||
Value::Integer(i64::from(v.block)),
|
||||
Value::Integer(i64::from(v.sell_price)),
|
||||
]))
|
||||
})?,
|
||||
)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -580,9 +548,7 @@ mod tests {
|
||||
#[test]
|
||||
fn miss_records_ask_and_push_serves_the_stats() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
assert!(s
|
||||
.eval::<bool>("return BenillaGetItemStats(25) == nil")
|
||||
.unwrap());
|
||||
assert!(s.eval::<bool>("return GetItemInfo(25) == nil").unwrap());
|
||||
assert_eq!(s.take_item_stat_asks(), vec![25]);
|
||||
|
||||
s.set_item_template(
|
||||
@@ -598,14 +564,13 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let (name, quality, inv): (String, i64, i64) =
|
||||
s.eval("return BenillaGetItemStats(25)").unwrap();
|
||||
assert_eq!((name.as_str(), quality, inv), ("Worn Shortsword", 1, 21));
|
||||
let (name, quality): (String, i64) = s
|
||||
.eval("local n, _, q = GetItemInfo(25) return n, q")
|
||||
.unwrap();
|
||||
assert_eq!((name.as_str(), quality), ("Worn Shortsword", 1));
|
||||
assert!(s.take_item_stat_asks().is_empty(), "push cleared the ask");
|
||||
// Id 0 records no ask.
|
||||
assert!(s
|
||||
.eval::<bool>("return BenillaGetItemStats(0) == nil")
|
||||
.unwrap());
|
||||
assert!(s.eval::<bool>("return GetItemInfo(0) == nil").unwrap());
|
||||
assert!(s.take_item_stat_asks().is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,8 +403,6 @@ struct PreambleProf {
|
||||
scroll: u128,
|
||||
/// `region_resolved`'s liveness retain.
|
||||
retain: u128,
|
||||
/// Never set, so always 0: the frame roster is built inside `fp_frames`.
|
||||
plan: u128,
|
||||
/// `LayoutScope::begin_full`.
|
||||
begin: u128,
|
||||
/// The fingerprint and scope walk over every live frame.
|
||||
@@ -447,14 +445,13 @@ impl PreambleProf {
|
||||
+ self.ids
|
||||
+ self.scroll
|
||||
+ self.retain
|
||||
+ self.plan
|
||||
+ self.begin
|
||||
+ self.fp_frames
|
||||
+ self.fp_regions
|
||||
+ self.seed;
|
||||
eprintln!(
|
||||
"[layout-pre] skips={} incr={} frames={frames} anchored={regions} total_us={total} \
|
||||
tooltip={} watched={} ids={} scroll={} retain={} plan={} begin={} \
|
||||
tooltip={} watched={} ids={} scroll={} retain={} begin={} \
|
||||
fp_frames={} fp_regions={} seed={}",
|
||||
u8::from(skips),
|
||||
u8::from(self.incremental),
|
||||
@@ -463,7 +460,6 @@ impl PreambleProf {
|
||||
self.ids,
|
||||
self.scroll,
|
||||
self.retain,
|
||||
self.plan,
|
||||
self.begin,
|
||||
self.fp_frames,
|
||||
self.fp_regions,
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
use crate::layout::Rect;
|
||||
use crate::order::ZTarget;
|
||||
use crate::script::object::frame_handle_of;
|
||||
use crate::widget::{FrameHandle, InsertMode, KindState, MessageLine};
|
||||
|
||||
use super::layout::FramePaint;
|
||||
@@ -38,8 +39,6 @@ pub(super) fn message_text(lua: &mlua::Lua, v: &mlua::Value) -> Option<String> {
|
||||
/// Install `SetJustifyH`, `GetJustifyH`, `SetJustifyV` and `GetJustifyV`, which both classes carry,
|
||||
/// under [`crate::justify`]'s rules: an unknown token raises the usage line, and a token of the
|
||||
/// other axis clears this one, so `GetJustifyH()` then answers `"UNKNOWN"`.
|
||||
use crate::script::object::frame_handle_of;
|
||||
|
||||
pub(super) fn install_justify(
|
||||
lua: &mlua::Lua,
|
||||
m: &mlua::Table,
|
||||
|
||||
@@ -280,10 +280,6 @@ pub(crate) struct Model {
|
||||
pub(crate) petition: petition::PetitionState,
|
||||
/// Charter calls (`BuyGuildCharter`, `SignPetition`, `TurnInGuildCharter`, …) queued.
|
||||
pub(crate) petition_requests: Vec<petition::PetitionRequest>,
|
||||
/// Names `ChatFrame_SendTell` queued; the app opens the chat box prefilled `/w <name> `.
|
||||
pub(crate) tell_requests: Vec<String>,
|
||||
/// Draft lines `ChatFrame_OpenChat` queued, for the app to prefill the chat box.
|
||||
pub(crate) open_chat_requests: Vec<String>,
|
||||
/// Per chat window from `ChatFrame1`, the tint, alpha and font size its tab menu can change.
|
||||
pub(crate) chat_window_looks: [chat_window::ChatWindowLook; chat_window::NUM_CHAT_WINDOWS],
|
||||
/// 0-based windows whose look Lua changed, the persist cue.
|
||||
@@ -1080,8 +1076,6 @@ impl Model {
|
||||
guild_requests: Vec::new(),
|
||||
petition: petition::PetitionState::default(),
|
||||
petition_requests: Vec::new(),
|
||||
tell_requests: Vec::new(),
|
||||
open_chat_requests: Vec::new(),
|
||||
// Per window, as the stock dock differs: 1 and 2 are the dock, the rest undocked.
|
||||
chat_window_looks: std::array::from_fn(chat_window::ChatWindowLook::stock),
|
||||
chat_window_changes: HashSet::new(),
|
||||
|
||||
@@ -232,12 +232,6 @@ impl super::UiScript {
|
||||
pub fn set_saved_instances(&mut self, saved: Vec<SavedInstanceInfo>) {
|
||||
self.model_mut().saved_instances = saved;
|
||||
}
|
||||
|
||||
/// Drain the names `ChatFrame_SendTell` queued; the app opens the chat edit box on
|
||||
/// `/w <name> ` for each.
|
||||
pub fn take_tell_requests(&mut self) -> Vec<String> {
|
||||
std::mem::take(&mut self.model_mut().tell_requests)
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the player leads the group. `IsRaidLeader` (`0x4bb8c0`) and `IsPartyLeader`
|
||||
@@ -703,18 +697,6 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
})?,
|
||||
)?;
|
||||
|
||||
// ChatFrame_SendTell(name) is FrameXML in the reference (`ChatFrame.lua:1606`, opening the
|
||||
// edit box on `/w name `); the chat edit box is app-side here, so this queues the name for
|
||||
// `UiScript::take_tell_requests`.
|
||||
g.set(
|
||||
"ChatFrame_SendTell",
|
||||
lua.create_function(|lua, name: String| {
|
||||
let mut model = lua.app_data_mut::<Model>().expect("model app_data");
|
||||
model.tell_requests.push(name);
|
||||
Ok(())
|
||||
})?,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1167,15 +1149,6 @@ mod tests {
|
||||
assert!(s.eval::<bool>("return IsRaidOfficer() == nil").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chat_frame_send_tell_queues_the_name() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
assert!(s.take_tell_requests().is_empty());
|
||||
s.run(r#"ChatFrame_SendTell("Alice")"#).unwrap();
|
||||
assert_eq!(s.take_tell_requests(), vec!["Alice".to_string()]);
|
||||
assert!(s.take_tell_requests().is_empty());
|
||||
}
|
||||
|
||||
// ── The identity predicates ─────────────────────────────────────────────
|
||||
|
||||
fn unit(exists: bool, guid: u64) -> crate::script::UnitState {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
use mlua::{Lua, MultiValue, Value};
|
||||
|
||||
use super::binding_abi;
|
||||
use super::Model;
|
||||
|
||||
/// `GetPetitionInfo`'s first return when the record's charter bit is set (`0x84cf8c`, selected at
|
||||
@@ -131,15 +132,6 @@ pub fn validate_guild_name(name: &str) -> Result<(), &'static str> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 1.12's `1` or nil, never `true`/`false`: this client has no `lua_pushboolean`.
|
||||
fn era_bool(on: bool) -> Value {
|
||||
if on {
|
||||
Value::Integer(1)
|
||||
} else {
|
||||
Value::Nil
|
||||
}
|
||||
}
|
||||
|
||||
/// A cached name as a string, an uncached one as nil.
|
||||
fn name_value(lua: &Lua, name: Option<&String>) -> mlua::Result<Value> {
|
||||
match name {
|
||||
@@ -176,7 +168,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
Value::String(lua.create_string(&r.body_text)?),
|
||||
Value::Integer(i64::from(r.max_signatures)),
|
||||
name_value(lua, r.originator.as_ref())?,
|
||||
era_bool(r.is_originator),
|
||||
binding_abi::flag(r.is_originator),
|
||||
]))
|
||||
})?,
|
||||
)?;
|
||||
@@ -215,7 +207,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
"CanSignPetition",
|
||||
lua.create_function(|lua, ()| {
|
||||
let model = lua.app_data_ref::<Model>().expect("model app_data");
|
||||
Ok(era_bool(model.petition.can_sign))
|
||||
Ok(binding_abi::flag(model.petition.can_sign))
|
||||
})?,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -379,7 +379,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
Value::Integer(i64::from(it.count)),
|
||||
Value::Integer(i64::from(it.quality)),
|
||||
Value::Boolean(it.usable),
|
||||
// The item id, `BenillaGetItemStats`'s key, a sixth return that is not 1.12's.
|
||||
// The item id, a sixth return that is not 1.12's.
|
||||
Value::Integer(i64::from(it.item_id)),
|
||||
]))
|
||||
})?,
|
||||
|
||||
@@ -12,6 +12,7 @@ use std::collections::HashMap;
|
||||
|
||||
use mlua::{Lua, MultiValue, Value};
|
||||
|
||||
use super::binding_abi;
|
||||
use super::Model;
|
||||
|
||||
/// The synthetic header key for factions the player moved to the inactive bucket.
|
||||
@@ -146,15 +147,6 @@ fn collate(a: &str, b: &str) -> std::cmp::Ordering {
|
||||
.then_with(|| a.as_bytes().cmp(b.as_bytes()))
|
||||
}
|
||||
|
||||
/// `1` or nil, never `0`: `0` is truthy in Lua, and the stock pane tests `if ( atWarWith )`.
|
||||
fn era_bool(b: bool) -> Value {
|
||||
if b {
|
||||
Value::Integer(1)
|
||||
} else {
|
||||
Value::Nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the header groups: the visible non-header rows by [`header_key`], sorted by name. A group
|
||||
/// is named by its pushed header row (the reference synthesizes a missing one, `0x4d5a70`), or left
|
||||
/// empty for a synthetic key; a childless header is no group, as the client's count drops it.
|
||||
@@ -305,7 +297,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
Value::Nil, // atWarWith
|
||||
Value::Nil, // canToggleAtWar
|
||||
Value::Integer(1), // isHeader
|
||||
era_bool(collapsed), // isCollapsed
|
||||
binding_abi::flag(collapsed), // isCollapsed
|
||||
Value::Nil, // isWatched
|
||||
]))
|
||||
}
|
||||
@@ -318,11 +310,11 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
Value::Integer(i64::from(e.bar_min)),
|
||||
Value::Integer(i64::from(e.bar_max)),
|
||||
Value::Integer(i64::from(e.standing)),
|
||||
era_bool(e.at_war),
|
||||
era_bool(e.can_toggle_at_war),
|
||||
binding_abi::flag(e.at_war),
|
||||
binding_abi::flag(e.can_toggle_at_war),
|
||||
Value::Nil, // isHeader
|
||||
Value::Nil, // isCollapsed
|
||||
era_bool(model.reputation.watched == Some(e.rep_list_id)),
|
||||
binding_abi::flag(model.reputation.watched == Some(e.rep_list_id)),
|
||||
]))
|
||||
}
|
||||
}
|
||||
@@ -412,7 +404,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
"IsFactionInactive",
|
||||
lua.create_function(|lua, index: usize| {
|
||||
let model = lua.app_data_ref::<Model>().expect("model app_data");
|
||||
Ok(era_bool(
|
||||
Ok(binding_abi::flag(
|
||||
entry_at(&model, index).is_some_and(|e| e.inactive),
|
||||
))
|
||||
})?,
|
||||
|
||||
@@ -16,6 +16,7 @@ use std::collections::HashMap;
|
||||
|
||||
use mlua::{Lua, MultiValue, Value};
|
||||
|
||||
use super::binding_abi;
|
||||
use super::Model;
|
||||
|
||||
/// One known skill line off the player's `PLAYER_SKILL_INFO` block, resolved by the app.
|
||||
@@ -261,15 +262,6 @@ fn displayed_ranks(e: &SkillEntry) -> (i64, i64) {
|
||||
(rank, max)
|
||||
}
|
||||
|
||||
/// A `bool` as the 1.12 `1`/nil shape.
|
||||
fn era_bool(b: bool) -> Value {
|
||||
if b {
|
||||
Value::Integer(1)
|
||||
} else {
|
||||
Value::Nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Register the skills-pane globals.
|
||||
pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
let g = lua.globals();
|
||||
@@ -303,7 +295,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
Ok(MultiValue::from_vec(vec![
|
||||
Value::String(lua.create_string(&grp.name)?),
|
||||
Value::Integer(1), // isHeader
|
||||
era_bool(expanded),
|
||||
binding_abi::flag(expanded),
|
||||
Value::Integer(0), // skillRank
|
||||
Value::Integer(0), // numTempPoints
|
||||
Value::Integer(0), // skillModifier
|
||||
@@ -326,7 +318,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
Value::Integer(0), // numTempPoints
|
||||
Value::Integer(i64::from(e.temp_bonus)), // skillModifier: temp only
|
||||
Value::Integer(max),
|
||||
era_bool(e.abandonable), // isAbandonable
|
||||
binding_abi::flag(e.abandonable), // isAbandonable
|
||||
// stepCost, rankCost: nil, since Lua reads `0` as true.
|
||||
Value::Nil,
|
||||
Value::Nil,
|
||||
|
||||
@@ -95,11 +95,6 @@ impl super::UiScript {
|
||||
pub fn take_social_requests(&mut self) -> Vec<SocialRequest> {
|
||||
std::mem::take(&mut self.model_mut().social_requests)
|
||||
}
|
||||
|
||||
/// Queue an intent from the app side.
|
||||
pub fn queue_social_request(&mut self, request: SocialRequest) {
|
||||
self.model_mut().social_requests.push(request);
|
||||
}
|
||||
}
|
||||
|
||||
/// Register the social globals against the snapshot store.
|
||||
|
||||
@@ -846,7 +846,6 @@ pub(super) fn install_methods(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
render_view(lua, &this, &v, BuilderFlags::default(), None)?;
|
||||
// Nothing to arm: the vendor tab's row seats `ShoppingTooltip1/2` itself
|
||||
// (`MerchantFrame.xml:67-80`), and the buyback tab seats none.
|
||||
if buyback {}
|
||||
}
|
||||
None => {
|
||||
// Template in flight: the row's own stat head as a minimal view, the
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use mlua::{Lua, MultiValue, Value};
|
||||
|
||||
use crate::script::binding_abi::number_arg;
|
||||
use crate::script::binding_abi::{self, number_arg};
|
||||
use crate::script::item_stats::item_link;
|
||||
use crate::script::Model;
|
||||
|
||||
@@ -19,15 +19,6 @@ fn opt_str(lua: &Lua, s: Option<&String>) -> mlua::Result<Value> {
|
||||
})
|
||||
}
|
||||
|
||||
/// A `bool` as the 1.12 API's `1` or `nil`.
|
||||
fn era_bool(b: bool) -> Value {
|
||||
if b {
|
||||
Value::Integer(1)
|
||||
} else {
|
||||
Value::Nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Register the tradeskill globals.
|
||||
pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
let g = lua.globals();
|
||||
@@ -139,7 +130,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
Value::String(lua.create_string(&name)?),
|
||||
Value::String(lua.create_string("header")?),
|
||||
Value::Integer(0),
|
||||
era_bool(expanded),
|
||||
binding_abi::flag(expanded),
|
||||
]))
|
||||
}
|
||||
Row::Entry(ei) => {
|
||||
@@ -276,7 +267,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
if let Some(r) = recipe_at(&model, index) {
|
||||
for (name, have) in &r.tools {
|
||||
out.push(Value::String(lua.create_string(name)?));
|
||||
out.push(era_bool(*have));
|
||||
out.push(binding_abi::flag(*have));
|
||||
}
|
||||
}
|
||||
Ok(MultiValue::from_vec(out))
|
||||
@@ -349,16 +340,16 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
lua.create_function(|lua, index: usize| {
|
||||
let model = lua.app_data_ref::<Model>().expect("model app_data");
|
||||
let Some(t) = model.trade_skill.as_ref() else {
|
||||
return Ok(era_bool(false));
|
||||
return Ok(binding_abi::flag(false));
|
||||
};
|
||||
let groups = build_groups(&t.recipes);
|
||||
Ok(match index.checked_sub(1) {
|
||||
None => era_bool(
|
||||
None => binding_abi::flag(
|
||||
!groups
|
||||
.iter()
|
||||
.any(|g| model.trade_skill_subclass_hidden.contains(&g.key)),
|
||||
),
|
||||
Some(n) => era_bool(
|
||||
Some(n) => binding_abi::flag(
|
||||
groups
|
||||
.get(n)
|
||||
.is_some_and(|g| !model.trade_skill_subclass_hidden.contains(&g.key)),
|
||||
@@ -425,16 +416,16 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
lua.create_function(|lua, index: usize| {
|
||||
let model = lua.app_data_ref::<Model>().expect("model app_data");
|
||||
if model.trade_skill.is_none() {
|
||||
return Ok(era_bool(false));
|
||||
return Ok(binding_abi::flag(false));
|
||||
}
|
||||
let bits = present_inv_slots(&model);
|
||||
Ok(match index.checked_sub(1) {
|
||||
// Everything shown: (present & mask) == present (`0x4fffd0`).
|
||||
None => era_bool(
|
||||
None => binding_abi::flag(
|
||||
bits.iter()
|
||||
.all(|&b| model.trade_skill_invslot_mask & (1 << b) != 0),
|
||||
),
|
||||
Some(n) => era_bool(
|
||||
Some(n) => binding_abi::flag(
|
||||
bits.get(n)
|
||||
.is_some_and(|&b| model.trade_skill_invslot_mask & (1 << b) != 0),
|
||||
),
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
use mlua::{Lua, MultiValue, Value};
|
||||
|
||||
use super::binding_abi;
|
||||
use super::Model;
|
||||
|
||||
/// A service's state (the wire's `TrainerSpellState`): green learnable, red gated, gray known.
|
||||
@@ -424,15 +425,6 @@ fn opt_str(lua: &Lua, s: Option<&String>) -> mlua::Result<Value> {
|
||||
})
|
||||
}
|
||||
|
||||
/// A `bool` as the reference's `1` or nil.
|
||||
fn era_bool(b: bool) -> Value {
|
||||
if b {
|
||||
Value::Integer(1)
|
||||
} else {
|
||||
Value::Nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Register the trainer globals.
|
||||
pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
let g = lua.globals();
|
||||
@@ -468,7 +460,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
Value::String(lua.create_string(&g.name)?),
|
||||
Value::Nil,
|
||||
Value::String(lua.create_string("header")?),
|
||||
era_bool(expanded),
|
||||
binding_abi::flag(expanded),
|
||||
]))
|
||||
}
|
||||
Row::Service(si) => {
|
||||
@@ -560,7 +552,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
Ok(MultiValue::from_vec(vec![
|
||||
Value::String(lua.create_string(&req.name)?),
|
||||
Value::Integer(i64::from(req.rank)),
|
||||
era_bool(req.met),
|
||||
binding_abi::flag(req.met),
|
||||
]))
|
||||
})?,
|
||||
)?;
|
||||
@@ -584,7 +576,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
};
|
||||
Ok(MultiValue::from_vec(vec![
|
||||
Value::String(lua.create_string(&req.name)?),
|
||||
era_bool(req.met),
|
||||
binding_abi::flag(req.met),
|
||||
]))
|
||||
})?,
|
||||
)?;
|
||||
@@ -599,7 +591,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
"IsTrainerServiceTradeSkill",
|
||||
lua.create_function(|lua, index: usize| {
|
||||
let model = lua.app_data_ref::<Model>().expect("model app_data");
|
||||
Ok(era_bool(
|
||||
Ok(binding_abi::flag(
|
||||
service(&model, index).is_some_and(|s| s.is_trade_skill),
|
||||
))
|
||||
})?,
|
||||
@@ -612,7 +604,10 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
lua.create_function(|lua, index: usize| {
|
||||
let model = lua.app_data_ref::<Model>().expect("model app_data");
|
||||
let learn = service(&model, index).is_some_and(|s| !s.is_trade_skill);
|
||||
Ok(MultiValue::from_vec(vec![era_bool(learn), Value::Nil]))
|
||||
Ok(MultiValue::from_vec(vec![
|
||||
binding_abi::flag(learn),
|
||||
Value::Nil,
|
||||
]))
|
||||
})?,
|
||||
)?;
|
||||
|
||||
@@ -621,7 +616,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
"IsTradeskillTrainer",
|
||||
lua.create_function(|lua, ()| {
|
||||
let model = lua.app_data_ref::<Model>().expect("model app_data");
|
||||
Ok(era_bool(model.trainer.as_ref().is_some_and(|t| {
|
||||
Ok(binding_abi::flag(model.trainer.as_ref().is_some_and(|t| {
|
||||
t.trainer_type == TRAINER_TYPE_TRADESKILL
|
||||
})))
|
||||
})?,
|
||||
@@ -632,7 +627,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
"IsTalentTrainer",
|
||||
lua.create_function(|lua, ()| {
|
||||
let model = lua.app_data_ref::<Model>().expect("model app_data");
|
||||
Ok(era_bool(
|
||||
Ok(binding_abi::flag(
|
||||
model.trainer.as_ref().is_some_and(|t| t.trainer_type == 1),
|
||||
))
|
||||
})?,
|
||||
@@ -656,7 +651,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
lua.create_function(|lua, kind: String| {
|
||||
let model = lua.app_data_ref::<Model>().expect("model app_data");
|
||||
Ok(match TrainerServiceCategory::from_filter_str(&kind) {
|
||||
Some(c) => era_bool(model.trainer_filter[c.filter_slot()]),
|
||||
Some(c) => binding_abi::flag(model.trainer_filter[c.filter_slot()]),
|
||||
None => Value::Nil,
|
||||
})
|
||||
})?,
|
||||
|
||||
@@ -114,10 +114,10 @@ fn level_shift_saturates_at_zero() {
|
||||
let root = a.create(FrameKind::Frame, None, None);
|
||||
let child = a.create(FrameKind::Frame, None, Some(root));
|
||||
a.set_frame_level(root, 10, true);
|
||||
a.set_frame_level(child, 12, true);
|
||||
// Root drops by 10, taking the child from 12 to 2.
|
||||
a.set_frame_level(child, 3, true);
|
||||
// Root drops by 10, which would take the child to -7: it stops at 0.
|
||||
a.set_frame_level(root, 0, true);
|
||||
assert_eq!(a.frame(child).unwrap().level, 2);
|
||||
assert_eq!(a.frame(child).unwrap().level, 0);
|
||||
}
|
||||
|
||||
// ── Scale ────────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user