the mailbox takes typing again, and events carry their real arguments

- the mail window's send tab takes typing again: the subject, body and money boxes were taking no input while the recipient box swallowed every key, and a sent letter clears its subject and item icon instead of putting them straight back (2145)
- seven events fire the way the reference does: UNIT_DYNAMIC_FLAGS fires at all, swapping two identical stacks between bank slots fires PLAYERBANKSLOTS_CHANGED twice, the pet sheet's training points repaint, and ITEM_LOCK_CHANGED loses its invented arguments (2140)
- nine widget methods 1.12 addons probe for are in: ShaguTweaks stops recolouring textures the reference leaves alone, pfUI and _Nameplates stop raising on a tooltip's GetAnchorType, and 63 AceEvent addons get RegisterAllEvents (2142)
- six post-1.12 methods leave the widget surface (SetSize, SetShown, HasFocus, SetFormattedText and two tooltip verbs), so an addon reaching for a later client's API finds what a real 1.12 client gives it (2142, 2146)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Sam
2026-09-11 20:52:50 +07:00
co-authored by Claude Fable 5.1
parent 3f3cf21afa
commit fed2c96ec8
100 changed files with 3837 additions and 869 deletions
+24 -24
View File
@@ -3,11 +3,11 @@
//! through the parent's `with_unit`/`pick_unit_token` helpers.
//!
//! **Every predicate here returns through one function** — [`super::unit_predicate`] when it reads
//! a snapshot field, [`predicate`] when it computes its own bool. Neither ever hands mlua a Rust
//! `bool`: all 29 of the reference's unit predicates push the constant double `1.0`
//! (`lua_pushnumber 0x6f3810`) or `nil` (`lua_pushnil 0x6f37f0`), exactly one value at every live
//! `ret`, and **no binding in the 83-entry table at `0x850438` calls `lua_pushboolean 0x6f39f0` at
//! all** (decisions 1830, 2043, 2048). A new predicate that open-codes
//! a snapshot field, [`flag`](super::super::binding_abi::flag) when it computes its own bool.
//! Neither ever hands mlua a Rust `bool`: all 29 of the reference's unit predicates push the
//! constant double `1.0` (`lua_pushnumber 0x6f3810`) or `nil` (`lua_pushnil 0x6f37f0`), one value
//! at every live `ret`, and **no binding in the 83-entry table at `0x850438` calls
//! `lua_pushboolean 0x6f39f0` at all** (decisions 1830, 2043, 2048). A new predicate that open-codes
//! `Value::Integer(1)`/`Value::Nil`, or returns a `bool`, is the drift those records exist to stop.
//!
//! **The scope of that claim is the unit table, not "the binding surface"** — 2043 said the wider
@@ -17,7 +17,7 @@
use mlua::{Lua, Value};
use super::super::binding_abi::predicate;
use super::super::binding_abi::flag;
use super::super::Model;
use super::{
check_unit_token, classification_word, grey_band, level_reads_unknown, pick_unit_token,
@@ -112,7 +112,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
})?;
// One value, and it is the NUMBER 1 or nil — never a boolean, the same shape the
// rest of this family answers in.
Ok(predicate(hit))
Ok(flag(hit))
})?,
)?;
}
@@ -154,7 +154,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
.as_ref()
.and_then(|t| model.unit(t))
.map_or(0, |u| u.guid);
Ok(predicate(by_flag || guid == model.party.leader_guid))
Ok(flag(by_flag || guid == model.party.leader_guid))
})?,
)?;
@@ -417,7 +417,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
)?);
let token = pick_unit_token(&a, &b);
let r = with_unit(lua, &token, 0u8, |u| u.reaction)?;
Ok(predicate((1..=2).contains(&r)))
Ok(flag((1..=2).contains(&r)))
})?,
)?;
g.set(
@@ -437,7 +437,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
)?);
let token = pick_unit_token(&a, &b);
let r = with_unit(lua, &token, 0u8, |u| u.reaction)?;
Ok(predicate(r >= 5))
Ok(flag(r >= 5))
})?,
)?;
@@ -467,7 +467,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
)?);
let model = lua.app_data_ref::<Model>().expect("model app_data");
let player_level = model.player_req.level;
Ok(predicate(
Ok(flag(
unit.and_then(|u| model.unit(&u))
.is_some_and(|u| super::is_civilian_kill(u, player_level)),
))
@@ -482,7 +482,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
"UnitPlayerControlled",
lua.create_function(|lua, unit: Option<String>| {
let model = lua.app_data_ref::<Model>().expect("model app_data");
Ok(predicate(
Ok(flag(
unit.and_then(|u| model.unit(&u))
.is_some_and(|u| u.player_controlled),
))
@@ -525,7 +525,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
if !ua.exists || !ub.exists {
return Ok(Value::Nil);
}
Ok(predicate(a == b || (ua.guid != 0 && ua.guid == ub.guid)))
Ok(flag(a == b || (ua.guid != 0 && ua.guid == ub.guid)))
})?,
)?;
@@ -549,7 +549,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
"Usage: UnitAffectingCombat(\"unit\")",
)?;
let hot = with_unit(lua, &Some(token), false, |u| u.exists && u.in_combat)?;
Ok(predicate(hot))
Ok(flag(hot))
})?,
)?;
@@ -578,7 +578,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
"HasFullControl",
lua.create_function(|lua, ()| {
let model = lua.app_data_ref::<Model>().expect("model app_data");
Ok(predicate(model.player_control))
Ok(flag(model.player_control))
})?,
)?;
@@ -617,7 +617,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
!model.party.members.is_empty()
};
let hit = grouped && (in_group(u.guid) || in_group(u.owner));
Ok(predicate(hit))
Ok(flag(hit))
})?,
)?;
}
@@ -638,7 +638,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
.iter()
.any(|m| m.guid != 0 && m.guid == u.guid)
});
Ok(predicate(hit))
Ok(flag(hit))
})?,
)?;
@@ -668,7 +668,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
.members
.iter()
.any(|m| m.guid != 0 && m.guid == u.guid)));
Ok(predicate(hit))
Ok(flag(hit))
})?,
)?;
@@ -695,7 +695,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
let ok = with_unit(lua, &token, false, |u| {
u.exists && u.is_player && u.reaction >= 5
})?;
Ok(predicate(ok))
Ok(flag(ok))
})?,
)?;
@@ -895,7 +895,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
r#"Usage: UnitHasRelicSlot("unit")"#,
)?);
let has = with_unit(lua, &token, false, |u| u.has_relic_slot)?;
Ok(predicate(has))
Ok(flag(has))
})?,
)?;
@@ -1210,7 +1210,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
"IsResting",
lua.create_function(|lua, ()| {
let model = lua.app_data_ref::<Model>().expect("model app_data");
Ok(predicate(model.resting))
Ok(flag(model.resting))
})?,
)?;
// PartialPlayTime() / NoPlayTime() → 1/nil: the two anti-addiction play-time regimes, read
@@ -1223,14 +1223,14 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
"PartialPlayTime",
lua.create_function(|lua, ()| {
let model = lua.app_data_ref::<Model>().expect("model app_data");
Ok(predicate(model.partial_play_time))
Ok(flag(model.partial_play_time))
})?,
)?;
g.set(
"NoPlayTime",
lua.create_function(|lua, ()| {
let model = lua.app_data_ref::<Model>().expect("model app_data");
Ok(predicate(model.no_play_time))
Ok(flag(model.no_play_time))
})?,
)?;
// GetBillingTimeRested() → the account's rested billing MINUTES, as one number, always
@@ -1456,7 +1456,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
if had {
model.target_clear = true;
}
Ok(predicate(had))
Ok(flag(had))
})?,
)?;
+21 -5
View File
@@ -16,7 +16,7 @@
//!
//! **Every predicate in this family answers the number `1` or `nil`, never a Lua boolean**, and it
//! answers it through the one helper — [`unit_predicate`], over
//! [`super::binding_abi::predicate`]. Decision 2043; the law is 1830's, which settled the same
//! [`super::binding_abi::flag`]. Decision 2043; the law is 1830's, which settled the same
//! question for the widget predicates a family earlier.
//!
//! v1 gaps, stated not hidden: the snapshot carries only the *active* power slot (see the
@@ -217,6 +217,24 @@ pub struct UnitState {
/// and `0x1000`/`0x2000` (the play-time regimes) all move without touching either. Firing off
/// the decoded pair would silently under-announce every one of them.
pub player_flags: u32,
/// `UNIT_DYNAMIC_FLAGS` (descriptor index 143), raw — what **`UNIT_DYNAMIC_FLAGS`** the
/// *event* fires on, the [`Self::flags`] pattern a hundred fields over.
///
/// The event is id **137**, and the id is not arbitrary: the reference's generic bridge
/// `0x51bbb0` registers one watch per unit-window field whose name-table slot is non-NULL, and
/// the id it dispatches is that field's own window index — `137 + 6 = 143`, this field.
/// Byte-verified here rather than taken from the note: `0xbe1198 + 137*4 = 0xbe13bc` has
/// exactly one writer image-wide (`0x51ad8b`), and the pointer it stores resolves to
/// `"UNIT_DYNAMIC_FLAGS"`. The watch **length** is 4 — selector byte `[0x51bc98 + 137] = 4`,
/// jump-table entry `0x51bbf9 mov eax,4` — so the gate is a `repe cmpsb` over exactly this one
/// dword against the object's shadow copy (`0x4655bb`), and **any** bit moving fires it.
///
/// Raw for the same reason [`Self::player_flags`] is, and the cost of getting it wrong is
/// larger here: this struct decodes four of its bits ([`Self::tapped`],
/// [`Self::tapped_by_player`], and — through the store — lootable `0x1` and dead-looking
/// `0x20`), while `0x2` (tracked, Hunter's Mark) and `0x10` moves without touching any of
/// them. Firing off the decoded subset would silently under-announce those.
pub dynamic_flags: u32,
/// The unit's owner — `UNIT_FIELD_SUMMONEDBY`, else its charmer, else its creator; `0` for
/// nobody's. What `UnitPlayerOrPetInParty`/`InRaid` read for the "or pet" half (1958).
pub owner: u64,
@@ -757,7 +775,7 @@ fn with_unit<T>(
/// Rust `bool`, and mlua pushes tag 1. That is the one shape 1.12 cannot produce: `UnitExists`
/// (`0x515fb0`) pushes `lua_pushnumber` (`0x6f3810`, tag 3, the double `1.0`) or `lua_pushnil`
/// (`0x6f37f0`, tag 0) and nothing else, and so does every other predicate in the family. So the
/// whole family goes through [`super::binding_abi::predicate`] — 1830's widget law, which is the
/// whole family goes through [`super::binding_abi::flag`] — 1830's widget law, which is the
/// *same* law, finally applied to the unit surface (decision 2043). A predicate that hand-computes
/// its own bool calls that helper directly; one that reads a snapshot field calls this. Both end at
/// the one push site, which is what stops the family drifting apart again.
@@ -770,9 +788,7 @@ fn unit_predicate(
token: &Option<String>,
f: impl FnOnce(&UnitState) -> bool,
) -> mlua::Result<mlua::Value> {
Ok(super::binding_abi::predicate(with_unit(
lua, token, false, f,
)?))
Ok(super::binding_abi::flag(with_unit(lua, token, false, f)?))
}
/// The `Unit*`/`GetQuestGreenRange` Lua binding registrations — split from this module's