diff --git a/src/core/Game2.lua b/src/core/Game2.lua index 112859eb..7871cbdd 100644 --- a/src/core/Game2.lua +++ b/src/core/Game2.lua @@ -683,7 +683,10 @@ end -- .SelectMon / PPRestoreItem_Cancel carry path: nothing spent. function Game2:usePartyItem(itemId) local ItemEffects = require("src.core.gen2.ItemEffects") - local action = ItemEffects.partyAction(itemId) + -- without the merged dataset this can only ever see RECORDS, the + -- module's own built-ins, so a mod's field item resolves to no action + -- at all and never gets past the .Oak refusal below + local action = ItemEffects.partyAction(itemId, self.data) if not action then return end local party = (self.save and self.save.party) or {} if #party == 0 then diff --git a/src/ui/gen2/BattleState.lua b/src/ui/gen2/BattleState.lua index 2d65eeda..bbbc248c 100644 --- a/src/ui/gen2/BattleState.lua +++ b/src/ui/gen2/BattleState.lua @@ -3024,8 +3024,10 @@ function BattleState:useItem(itemId) -- Everything else the pack can spend on a party mon runs the same -- item_effects.asm routine the field pack runs: the potion line and the -- drinks, the status cures and their berries, REVIVE / MAX REVIVE, and - -- the ETHER / ELIXER family. - local action = ItemEffects.partyAction(itemId) + -- the ETHER / ELIXER family. Without the merged dataset this can only + -- ever see RECORDS, the module's own built-ins, the same gap + -- Game2:usePartyItem had for the field pack. + local action = ItemEffects.partyAction(itemId, self.game and self.game.data) if action then return self:useOnPartyMon(itemId, action) end diff --git a/tests/gen2_battle_items_test.lua b/tests/gen2_battle_items_test.lua index dbfc8527..186e4596 100644 --- a/tests/gen2_battle_items_test.lua +++ b/tests/gen2_battle_items_test.lua @@ -117,6 +117,27 @@ local DATA = { fieldMenu = "ITEMMENU_PARTY", battleMenu = "ITEMMENU_NOUSE" }, OLD_ROD = { id = "OLD_ROD", pocket = "KEY", name = "OLD ROD", fieldMenu = "ITEMMENU_CURRENT", battleMenu = "ITEMMENU_NOUSE" }, + -- a mod's own battle-pack item: its action lives only in + -- gen2ItemEffects below, which ItemEffects.RECORDS (the module's + -- built-in table) has never heard of (#8) + MOD_ITEM = item("MOD_ITEM"), + }, + gen2ItemEffects = { + -- a status cure rather than an HP heal: HP is exposed to the wild + -- mon's own reply once the item spends the turn, which would make a + -- direct before/after HP check depend on incidental battle math this + -- fix has nothing to do with. Status is not. + MOD_ITEM = { + action = "status", field = true, needsTarget = true, + use = function(ctx) + local mon = ctx.mon + if mon.status ~= "poison" then + return { used = false, text = "It won't have\nany effect." } + end + mon.status = nil + return { used = true, text = "MOD ITEM used!" } + end, + }, }, } @@ -236,6 +257,33 @@ do eq(save.inventory.ANTIDOTE, 1, "with the ANTIDOTE untouched") end +-- ---- a mod's own battle-pack item (#8) ------------------------------------- +-- BattleState:useItem asked ItemEffects.partyAction for the item's family +-- with no `data` argument, the same omission Game2:usePartyItem had for the +-- field pack, so a mod item's action -- present only in the merged +-- gen2ItemEffects table -- resolved to nil and the pack fell straight to +-- "That isn't going to help here." instead of opening the party list. +do + local sick = Mon.new(DATA, "CYNDAQUIL", 10, { dvs = perfect }) + sick.moves = { { id = "TACKLE", pp = 35, maxPp = 35 } } + sick.status = "poison" + local screen, _, _, save, pushed = newScreen({ + player = sick, party = { sick }, inventory = { MOD_ITEM = 1 }, + }) + check(runToMenu(screen), "reached the menu") + + screen:useItem("MOD_ITEM") + eq(screen.phase, "submenu", + "a mod's own gen2ItemEffects record opens UseItem_SelectMon") + local picker = pushed[#pushed] + eq(getmetatable(picker), PartyMenu, "and the pick is the party screen") + if picker and picker.onChoose then + picker.onChoose(1, sick) + eq(sick.status, nil, "the mod item's own use() ran through the real screens") + eq(save.inventory.MOD_ITEM, nil, "and the mod item was spent") + end +end + -- ---- IsItemUsedOnConfusedMon: the battle-only arm -------------------------- do local screen, battle, player, save, pushed = newScreen({ diff --git a/tests/gen2_field_items_test.lua b/tests/gen2_field_items_test.lua index c3f9ab68..63e36568 100644 --- a/tests/gen2_field_items_test.lua +++ b/tests/gen2_field_items_test.lua @@ -102,6 +102,24 @@ local DATA = { TM01 = { id = "TM01", name = "TM01", pocket = "TM_HM", index = 191, fieldMenu = "ITEMMENU_PARTY", battleMenu = "ITEMMENU_NOUSE", teaches = "SWIFT" }, + -- a mod's own field item, whose action lives only in gen2ItemEffects + -- below -- ItemEffects.RECORDS (the module's built-in table) has never + -- heard of it, so resolving it at all requires the merged dataset (#8) + MOD_ITEM = { id = "MOD_ITEM", name = "MOD ITEM", pocket = "ITEM", + index = 250, fieldMenu = "ITEMMENU_PARTY", battleMenu = "ITEMMENU_PARTY" }, + }, + gen2ItemEffects = { + MOD_ITEM = { + action = "heal", field = true, needsTarget = true, + use = function(ctx) + local mon = ctx.mon + if mon.hp >= mon.maxHp then + return { used = false, text = "It won't have\nany effect." } + end + mon.hp = math.min(mon.maxHp, mon.hp + 5) + return { used = true, text = "MOD ITEM used!" } + end, + }, }, gen2MenuGfx = {}, gen2Icons = { @@ -461,6 +479,24 @@ do eq(host.save.inventory.HP_UP, nil, "and the HP UP was spent") end +do + -- #8 regression: Game2:usePartyItem asked ItemEffects.partyAction for the + -- item's family with no `data` argument, so it could only ever see + -- RECORDS -- the module's own built-ins. A mod's field item, whose + -- action exists only in the merged gen2ItemEffects table, resolved to a + -- nil action and fell straight through to the "isn't going to help here" + -- refusal instead of opening the party list at all. + local mon = fixtureMon(12, { hp = 10 }) + local host = newHost({ MOD_ITEM = 1 }, { mon }) + host:useFieldItem("MOD_ITEM") + local party = host.stack:top() + check(party ~= nil and party.prompt ~= nil, + "a mod's own gen2ItemEffects record opens the party list") + drive(host, function() return host.stack:top() ~= party end) + eq(mon.hp, 15, "the mod item's own use() ran through the real menu") + eq(host.save.inventory.MOD_ITEM, nil, "and the mod item was spent") +end + do local mon = fixtureMon(12, { statExp = { hp = 25600, attack = 0, defense = 0, speed = 0, special = 0 } })