ui: register UnitCanAssist, the CanAssist gate callers like ShaguTweaks rely on
UnitCanAssist (0x516bb0) was a nil global, so ShaguTweaks libpredict raised whenever UseAction ran. It answers the player's CanAssist verdict: 1 for a unit whose reaction toward the player is friendly (5 and up), or for itself, else nil, like UnitCanAttack's shape.
This commit is contained in:
@@ -220,6 +220,39 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
})?,
|
||||
)?;
|
||||
|
||||
// UnitCanAssist (`0x516bb0`) delegates to `CanAssist` (`0x6066f0`): the player can help a unit
|
||||
// whose reaction toward it is friendly, or itself. The snapshot's reaction is the Lua
|
||||
// `UnitReaction` scale (`0` unknown, 5 Friendly up), and the player token's own reaction stays
|
||||
// 0, so a both-arguments-`"player"` call answers the self case.
|
||||
g.set(
|
||||
"UnitCanAssist",
|
||||
lua.create_function(|lua, (a, b): (Value, Value)| {
|
||||
// Both arguments are `lua_isstring`-gated, so either one nil raises.
|
||||
let a = Some(crate::script::binding_abi::string_arg(
|
||||
lua,
|
||||
a,
|
||||
r#"Usage: UnitCanAssist("unit", "otherUnit")"#,
|
||||
)?);
|
||||
let b = Some(crate::script::binding_abi::string_arg(
|
||||
lua,
|
||||
b,
|
||||
r#"Usage: UnitCanAssist("unit", "otherUnit")"#,
|
||||
)?);
|
||||
let token = pick_unit_token(&a, &b);
|
||||
// The player can always assist itself; `"player"` resolves only when both arguments are
|
||||
// `"player"`, so it is asked outside the snapshot.
|
||||
let ok = if token
|
||||
.as_deref()
|
||||
.is_some_and(|t| t.eq_ignore_ascii_case("player"))
|
||||
{
|
||||
true
|
||||
} else {
|
||||
with_unit(lua, &token, false, |u| u.exists && u.reaction >= 5)?
|
||||
};
|
||||
Ok(flag(ok))
|
||||
})?,
|
||||
)?;
|
||||
|
||||
// GetQuestGreenRange (`0x4e17d0`): the green-to-grey boundary `QuestLogFrame.lua:593` buckets
|
||||
// by, from the table at `0x8076c0`. The reference answers 0 with no player object (`0x4e17f8`);
|
||||
// here a level of 0 reads the table's first entry, 4.
|
||||
|
||||
@@ -293,6 +293,30 @@ fn corpse_can_attack_and_green_range_bindings() {
|
||||
.unwrap(),
|
||||
1
|
||||
);
|
||||
// `UnitCanAssist` answers nil for the attackable target above, 1 for a friendly one, and the
|
||||
// player can always assist itself.
|
||||
assert!(s
|
||||
.eval::<bool>(r#"return UnitCanAssist("player", "target") == nil"#)
|
||||
.unwrap());
|
||||
s.set_unit(
|
||||
"target",
|
||||
Some(UnitState {
|
||||
exists: true,
|
||||
reaction: 5,
|
||||
..Default::default()
|
||||
}),
|
||||
);
|
||||
assert_eq!(
|
||||
s.eval::<i64>(r#"return UnitCanAssist("player", "target")"#)
|
||||
.unwrap(),
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
s.eval::<i64>(r#"return UnitCanAssist("player", "player")"#)
|
||||
.unwrap(),
|
||||
1,
|
||||
"the player can assist itself"
|
||||
);
|
||||
assert!(s
|
||||
.eval::<bool>(r#"return UnitIsCorpse("target") == nil"#)
|
||||
.unwrap());
|
||||
@@ -1435,6 +1459,7 @@ fn a_two_token_predicate_raises_on_either_nil_argument() {
|
||||
"UnitIsFriend",
|
||||
"UnitCanCooperate",
|
||||
"UnitCanAttack",
|
||||
"UnitCanAssist",
|
||||
"UnitReaction",
|
||||
] {
|
||||
assert!(
|
||||
@@ -1685,6 +1710,15 @@ fn every_unit_predicate_is_one_or_nil_and_never_a_boolean() {
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"UnitCanAssist",
|
||||
r#"UnitCanAssist("player", "target")"#,
|
||||
UnitState {
|
||||
exists: true,
|
||||
reaction: 5,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"UnitIsUnit",
|
||||
r#"UnitIsUnit("target", "target")"#,
|
||||
|
||||
Reference in New Issue
Block a user