From 7861ab2710874df168e2a617624294fbb13a6766 Mon Sep 17 00:00:00 2001 From: grom Date: Fri, 23 Oct 2026 21:53:10 -0600 Subject: [PATCH] ui: ClassicAPI's GetCraftReagentItemID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The one column 1.12's `GetCraftReagentInfo` does not answer: it gives the reagent's name, icon and the needed/held counts, and no item id at all, so nothing that keys a table on an item — pfUI's reagent counter — can use the stock verb. Same `(craftIndex, reagentIndex)` pair and the same rows that verb walks, so the two cannot disagree about which reagent is meant. Out of range on either index is nil, matching the stock verb's own nil-first return. All nine gates green. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn --- .../src/script/compat/classic_api/unit.rs | 91 +++++++++++++++++++ crates/benilla-ui/src/script/craft.rs | 2 +- .../src/script/tests/reference_surface.rs | 4 + 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/crates/benilla-ui/src/script/compat/classic_api/unit.rs b/crates/benilla-ui/src/script/compat/classic_api/unit.rs index dcc7f2ec..a5fecd1d 100644 --- a/crates/benilla-ui/src/script/compat/classic_api/unit.rs +++ b/crates/benilla-ui/src/script/compat/classic_api/unit.rs @@ -41,6 +41,31 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> { })?, )?; + // `GetCraftReagentItemID(craftIndex, reagentIndex)` -> the reagent's item id. 1.12's + // `GetCraftReagentInfo` answers its name, icon, needed and held counts and no id at all, so + // nothing that keys a table on an item — pfUI's reagent counter does — can use the stock verb. + // + // The same `(index, reagentIndex)` pair and the same rows that verb walks + // (`script/craft.rs`), so the two cannot disagree about which reagent is meant. Out of range + // on either index is nil, as the stock verb's own nil-first return is. + g.set( + "GetCraftReagentItemID", + lua.create_function( + |lua, (index, reagent_index): (Option, Option)| { + let model = lua.app_data_ref::().expect("model app_data"); + Ok(index + .zip(reagent_index) + .and_then(|(i, r)| { + crate::script::craft::recipe(&model, i) + .zip(r.checked_sub(1)) + .and_then(|(recipe, r)| recipe.reagents.get(r)) + }) + .map(|reagent| reagent.item) + .filter(|&id| id != 0)) + }, + )?, + )?; + // `CastSpellNoToggle(spellId)`: cast without the stock toggle-off, for a spell whose aura is // already up. The toggle lives in the client's cast path (`0x6e5100`'s autorepeat and aura // tests), and benilla's cast queue does not toggle anything — a press is a send — so this is @@ -135,6 +160,72 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> { mod tests { use crate::script::{UiScript, UnitState}; + /// `GetCraftReagentItemID`: the one column 1.12's `GetCraftReagentInfo` does not answer. It + /// reads the same rows that verb walks, so the two agree about which reagent is meant — and + /// anything keying a table on an item (pfUI's reagent counter) needs the id, not the name. + #[test] + fn get_craft_reagent_item_id_matches_the_stock_reagent_row() { + let mut s = UiScript::new().unwrap(); + assert_eq!( + s.eval::>("return GetCraftReagentItemID(1, 1)") + .unwrap(), + None, + "no craft window open" + ); + s.set_craft(Some(crate::script::CraftState { + name: "Enchanting".into(), + rank: 57, + max_rank: 75, + craft_type: 2, + recipes: vec![crate::script::CraftRecipe { + spell_id: 683, + tooltip: crate::script::CraftTooltip::Spell(683), + name: "Enchant Weapon - Minor Health".into(), + sub_name: String::new(), + difficulty: crate::script::TradeSkillDifficulty::Optimal, + num_available: 1, + icon: None, + description: None, + needs_item_target: true, + reagents: vec![crate::script::CraftReagent { + item: 10940, + name: Some("Illusion Dust".into()), + icon: None, + need: 1, + have: 3, + }], + tools: Vec::new(), + spell_level: 0, + }], + })); + + assert_eq!( + s.eval::>("return GetCraftReagentItemID(1, 1)") + .unwrap(), + Some(10940) + ); + // The stock verb names the same reagent at the same pair. + assert_eq!( + s.eval::("return (GetCraftReagentInfo(1, 1))") + .unwrap(), + "Illusion Dust" + ); + // Out of range on either index, and a missing argument, are nil. + for call in [ + "GetCraftReagentItemID(1, 2)", + "GetCraftReagentItemID(2, 1)", + "GetCraftReagentItemID(1, 0)", + "GetCraftReagentItemID(1)", + "GetCraftReagentItemID()", + ] { + assert_eq!( + s.eval::>(&format!("return {call}")).unwrap(), + None, + "{call}" + ); + } + } + /// `GetPlayerInfoByGUID`'s seven, for a unit this client holds. 1.12 has no verb that takes a /// GUID at all, so every column here comes from the snapshot the unit surface already carries /// — and the GUID resolves through the same `Model::unit` as every other token spelling, so a diff --git a/crates/benilla-ui/src/script/craft.rs b/crates/benilla-ui/src/script/craft.rs index 19a74b3d..a7a4538e 100644 --- a/crates/benilla-ui/src/script/craft.rs +++ b/crates/benilla-ui/src/script/craft.rs @@ -161,7 +161,7 @@ impl super::UiScript { } /// The recipe at a 1-based index. -fn recipe(model: &Model, index: usize) -> Option<&CraftRecipe> { +pub(in crate::script) fn recipe(model: &Model, index: usize) -> Option<&CraftRecipe> { let n = index.checked_sub(1)?; model.craft.as_ref()?.recipes.get(n) } diff --git a/crates/benilla-ui/src/script/tests/reference_surface.rs b/crates/benilla-ui/src/script/tests/reference_surface.rs index 61b13e7f..03361efb 100644 --- a/crates/benilla-ui/src/script/tests/reference_surface.rs +++ b/crates/benilla-ui/src/script/tests/reference_surface.rs @@ -323,6 +323,10 @@ fn allowed_beyond_1_12() -> HashSet<&'static str> { // (script/compat/classic_api/unit.rs). 1.12 has no verb that takes a GUID at all, and no // cast path that skips its own toggle — benilla's queue does not toggle, so the second is // the plain cast by id, the same reasoning nampower's `CastSpellNoQueue` carries. + // ClassicAPI's `GetCraftReagentItemID(craftIndex, reagentIndex)` (same file): the one + // column 1.12's `GetCraftReagentInfo` does not answer — it gives the name, icon and the + // two counts and no id at all, so nothing keying a table on an item can use it. + "GetCraftReagentItemID", "CastSpellNoToggle", "GetPlayerInfoByGUID", "GetQuestIDForLogIndex",