mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
305 lines
11 KiB
Lua
305 lines
11 KiB
Lua
-- Test suite for ItemUse and Oak's Lab parcel delivery scene.
|
|
|
|
require("tests.game3_cache").requireData("game3_item_use_and_parcel_test")
|
|
local GameVersion = require("src.core.GameVersion")
|
|
GameVersion.set("firered")
|
|
local ItemUse = require("src.core.game3.item_use")
|
|
local ItemsData = require("src.core.game3.items_data")
|
|
local Bag = require("src.core.game3.bag")
|
|
local Pokemon = require("src.core.game3.pokemon")
|
|
local Evolution = require("src.core.game3.evolution")
|
|
local Flags = require("src.core.game3.scripting.flags")
|
|
local Ctx = require("src.core.game3.scripting.ctx")
|
|
local Vm = require("src.core.game3.scripting.vm")
|
|
local Space = require("src.core.game3.scripting.space")
|
|
|
|
print("[test] 1. ItemUse.needsPartyTarget")
|
|
assert(ItemUse.needsPartyTarget("POTION") == true, "Potion needs party target")
|
|
assert(ItemUse.needsPartyTarget(13) == true, "Potion numeric ID needs party target")
|
|
assert(ItemUse.needsPartyTarget("SUPER_POTION") == true, "Super Potion needs party target")
|
|
assert(ItemUse.needsPartyTarget("ANTIDOTE") == true, "Antidote needs party target")
|
|
assert(ItemUse.needsPartyTarget("REVIVE") == true, "Revive needs party target")
|
|
assert(ItemUse.needsPartyTarget("RARE_CANDY") == true, "Rare Candy needs party target")
|
|
assert(ItemUse.needsPartyTarget("FIRE_STONE") == true, "Fire Stone needs party target")
|
|
assert(ItemUse.needsPartyTarget("TM01") == true, "TM01 needs party target")
|
|
assert(ItemUse.needsPartyTarget("BICYCLE") == false, "Bicycle does not need party target")
|
|
assert(ItemUse.needsPartyTarget("ESCAPE_ROPE") == false, "Escape rope does not need party target")
|
|
assert(ItemUse.needsPartyTarget("REPEL") == false, "Repel does not need party target")
|
|
print("[ok] needsPartyTarget correctly identifies party items vs field items")
|
|
|
|
print("[test] 2. ItemUse on party Pokémon")
|
|
Pokemon.install(nil)
|
|
local session = {
|
|
name = "RED",
|
|
party = {
|
|
{
|
|
species = 1, -- Bulbasaur
|
|
speciesId = 1,
|
|
level = 5,
|
|
hp = 10,
|
|
maxHp = 20,
|
|
status = "PSN",
|
|
sleep = 0,
|
|
moves = { { id = 33, pp = 35 } },
|
|
},
|
|
{
|
|
species = 4, -- Charmander
|
|
speciesId = 4,
|
|
level = 5,
|
|
hp = 0,
|
|
maxHp = 19,
|
|
moves = { { id = 10, pp = 35 } },
|
|
}
|
|
}
|
|
}
|
|
local bag = Bag.new()
|
|
Bag.add(bag, "POTION", 2)
|
|
Bag.add(bag, "ANTIDOTE", 1)
|
|
Bag.add(bag, "REVIVE", 1)
|
|
Bag.add(bag, "RARE_CANDY", 1)
|
|
|
|
-- Heal Mon 1 with Potion
|
|
local ok, res = ItemUse.useField(session, bag, "POTION", 1)
|
|
assert(ok == true, "Potion used successfully")
|
|
assert(session.party[1].hp == 20, "HP restored to max (10+20 capped at 20)")
|
|
assert(Bag.get(bag, "POTION") == 1, "1 Potion remaining in bag")
|
|
|
|
-- Clear status on Mon 1 with Antidote
|
|
ok, res = ItemUse.useField(session, bag, "ANTIDOTE", 1)
|
|
assert(ok == true, "Antidote used successfully")
|
|
assert(session.party[1].status == nil, "Poison cleared")
|
|
assert(Bag.get(bag, "ANTIDOTE") == 0, "Antidote consumed")
|
|
|
|
-- Revive Mon 2
|
|
ok, res = ItemUse.useField(session, bag, "REVIVE", 2)
|
|
assert(ok == true, "Revive used successfully")
|
|
assert(session.party[2].hp == 9, "Half HP restored on faint mon")
|
|
assert(Bag.get(bag, "REVIVE") == 0, "Revive consumed")
|
|
|
|
-- Rare Candy Mon 1
|
|
ok, res = ItemUse.useField(session, bag, "RARE_CANDY", 1)
|
|
assert(ok == true, "Rare Candy used successfully")
|
|
assert(session.party[1].level == 6, "Level increased to 6")
|
|
assert(Bag.get(bag, "RARE_CANDY") == 0, "Rare Candy consumed")
|
|
|
|
print("[ok] party healing, status cure, reviving, and rare candy all passed")
|
|
|
|
print("[test] 3. VAR_FACING preservation in Vm")
|
|
local vm = Vm.new({
|
|
store = Flags.newStore(),
|
|
scripts = {
|
|
test_facing = {
|
|
{ op = "compare_var_to_value", var = Ctx.VAR_FACING, value = 2 },
|
|
{ op = "call_if", cond = 1, target = "matched_north" },
|
|
{ op = "end" },
|
|
},
|
|
matched_north = {
|
|
{ op = "setvar", var = 0x4000, value = 99 },
|
|
{ op = "return" },
|
|
}
|
|
}
|
|
})
|
|
vm:startTalk("test_facing", 1, 2) -- Facing DIR_NORTH (2)
|
|
assert(Flags.getVar(vm.store, vm.ctx, 0x4000) == 99, "VAR_FACING branch correctly executed")
|
|
print("[ok] VAR_FACING correctly passed and matched conditional branches")
|
|
|
|
print("[test] 4. Script extraction chunk size and complete ReceiveDexScene")
|
|
local Dataset = require("src.core.game3.dataset")
|
|
Dataset.mountExtractRoots()
|
|
local bundle = Space.ensureBundle(nil)
|
|
assert(bundle and bundle.scripts, "Bundle scripts exist")
|
|
local receiveDex = bundle.scripts["g3:0816961e"]
|
|
assert(receiveDex, "ReceiveDexScene script exists in bundle")
|
|
local lastOp = receiveDex[#receiveDex]
|
|
assert(lastOp and lastOp.op == "end", "ReceiveDexScene ends with 'end' (not truncated mid-script)")
|
|
|
|
-- Check that the end of ReceiveDexScene contains removeobject (8) and setvar (PALLET_TOWN_PROFESSOR_OAKS_LAB = 6)
|
|
local hasRemoveObject8 = false
|
|
local hasSetVarOakLab = false
|
|
for _, row in ipairs(receiveDex) do
|
|
if row.op == "removeobject" and (row.localId == 8 or row[1] == 8) then
|
|
hasRemoveObject8 = true
|
|
end
|
|
if row.op == "setvar" and (row.var == 0x4055 or row[1] == 0x4055 or row.var == 16469 or row[1] == 16469) and (row.value == 6 or row[2] == 6) then
|
|
hasSetVarOakLab = true
|
|
end
|
|
end
|
|
assert(hasRemoveObject8, "ReceiveDexScene contains removeobject 8 (Rival exits)")
|
|
assert(hasSetVarOakLab, "ReceiveDexScene contains setvar VAR_MAP_SCENE_PALLET_TOWN_PROFESSOR_OAKS_LAB, 6")
|
|
print("[ok] ReceiveDexScene is fully extracted and non-truncated")
|
|
|
|
print("[test] 5. PartyMenu item usage, give, take, and message dismiss")
|
|
local PartyMenu = require("src.ui.game3.party_menu")
|
|
local BagMenu = require("src.ui.game3.bag_menu")
|
|
|
|
-- Setup mock input
|
|
local mockInput = {
|
|
_pressed = {},
|
|
wasPressed = function(self, key) return self._pressed[key] == true end,
|
|
press = function(self, key) self._pressed = { [key] = true } end,
|
|
clear = function(self) self._pressed = {} end,
|
|
}
|
|
|
|
-- Test Give to Mon
|
|
local testParty = {
|
|
{ species = 1, hp = 10, maxHp = 20, level = 5 },
|
|
{ species = 4, hp = 19, maxHp = 19, level = 5 },
|
|
}
|
|
local testBag = Bag.new()
|
|
Bag.add(testBag, "ORAN_BERRY", 2)
|
|
Bag.add(testBag, "POTION", 1)
|
|
|
|
local closed = false
|
|
PartyMenu.show(testParty, nil, {
|
|
session = { party = testParty, bag = testBag },
|
|
bag = testBag,
|
|
item = "ORAN_BERRY",
|
|
mode = "give",
|
|
onClose = function() closed = true end,
|
|
})
|
|
assert(PartyMenu.isOpen() == true, "PartyMenu is open")
|
|
assert(PartyMenu.mode == "give", "PartyMenu is in give mode")
|
|
|
|
-- Press A on Mon 1 to give item
|
|
mockInput:press("a")
|
|
PartyMenu.handleInput(mockInput)
|
|
assert(PartyMenu.mode == "message", "Transitions to message mode with give result")
|
|
assert(testParty[1].heldItem ~= nil, "Mon 1 now holds item")
|
|
assert(Bag.get(testBag, "ORAN_BERRY") == 1, "1 berry consumed from bag")
|
|
|
|
-- Press A to dismiss message -> should close and invoke onClose
|
|
mockInput:press("a")
|
|
PartyMenu.handleInput(mockInput)
|
|
assert(PartyMenu.isOpen() == false, "PartyMenu closed after give message dismissed")
|
|
assert(closed == true, "onClose invoked")
|
|
|
|
-- Test Take from Mon via ItemUse
|
|
local takeOk, _, takeMsg = ItemUse.takeFromMon({ party = testParty }, testBag, 1)
|
|
assert(takeOk == true, "takeFromMon succeeds")
|
|
assert(testParty[1].heldItem == nil, "Item taken from Mon 1")
|
|
assert(Bag.get(testBag, "ORAN_BERRY") == 2, "Berry returned to bag")
|
|
|
|
-- Test Use Potion via PartyMenu
|
|
PartyMenu.show(testParty, nil, {
|
|
session = { party = testParty, bag = testBag },
|
|
bag = testBag,
|
|
item = "POTION",
|
|
mode = "use",
|
|
})
|
|
assert(PartyMenu.mode == "use", "PartyMenu is in use mode")
|
|
mockInput:press("a")
|
|
PartyMenu.handleInput(mockInput)
|
|
assert(PartyMenu._hpAnim ~= nil, "HP bar animation started")
|
|
-- Step animation to completion
|
|
PartyMenu.update(1.0)
|
|
assert(PartyMenu._hpAnim == nil, "HP bar animation completed")
|
|
assert(PartyMenu.mode == "message", "Transitions to message mode showing heal")
|
|
assert(testParty[1].hp == 20, "Mon 1 healed to full 20 HP")
|
|
assert(Bag.get(testBag, "POTION") == 0, "Potion consumed")
|
|
|
|
-- Dismiss message (depleted potion -> closes)
|
|
mockInput:press("a")
|
|
PartyMenu.handleInput(mockInput)
|
|
assert(PartyMenu.isOpen() == false, "PartyMenu closed after single potion used")
|
|
print("[ok] PartyMenu item use, give, take, and message lifecycle passed")
|
|
|
|
print("[test] 6. BagMenu USE Potion opens PartyMenu and heals with HP lerp")
|
|
testParty[1].hp = 5
|
|
Bag.add(testBag, "POTION", 1)
|
|
BagMenu.show({ party = testParty, bag = testBag }, { bag = testBag })
|
|
assert(BagMenu.isOpen() == true, "BagMenu is open")
|
|
BagMenu.settle()
|
|
BagMenu.mode = "action"
|
|
BagMenu.actionCursor = 1 -- USE
|
|
mockInput:press("a")
|
|
BagMenu.handleInput(mockInput)
|
|
BagMenu.settle()
|
|
assert(PartyMenu.isOpen() == true, "PartyMenu opened from BagMenu USE")
|
|
assert(PartyMenu.mode == "use", "PartyMenu is in use mode")
|
|
-- Select Mon 1 with A
|
|
mockInput:press("a")
|
|
PartyMenu.handleInput(mockInput)
|
|
assert(PartyMenu._hpAnim ~= nil, "PartyMenu HP animation active")
|
|
PartyMenu.update(1.0)
|
|
assert(PartyMenu.mode == "message", "PartyMenu shows heal message")
|
|
assert(testParty[1].hp == 20, "Mon 1 healed to 20")
|
|
-- Dismiss message
|
|
mockInput:press("a")
|
|
PartyMenu.handleInput(mockInput)
|
|
assert(PartyMenu.isOpen() == false, "PartyMenu closed after potion used")
|
|
assert(BagMenu.mode == "list", "BagMenu returned to list mode")
|
|
print("[ok] BagMenu USE Potion -> PartyMenu -> heal passed")
|
|
|
|
print("[test] 7. Ground item ball pickup via std:1 sets object flag and removes object")
|
|
local Space = require("src.core.game3.scripting.space")
|
|
local Objects = require("src.core.game3.objects")
|
|
local Flags = require("src.core.game3.scripting.flags")
|
|
|
|
Space.store = Flags.newStore()
|
|
Space.active = true
|
|
|
|
local itemBallDef = {
|
|
localId = 5,
|
|
index = 5,
|
|
x = 17,
|
|
y = 54,
|
|
flag = 340,
|
|
graphics = 92,
|
|
graphicsId = 92,
|
|
sprite = "SPRITE_POKE_BALL",
|
|
scriptKey = "test_item_ball",
|
|
}
|
|
Objects.loadMap(nil, "TEST_MAP", { objects = { itemBallDef } })
|
|
local spawned = Objects.find(5)
|
|
assert(spawned ~= nil, "Item ball spawned")
|
|
assert(spawned.visible == true, "Item ball initially visible")
|
|
assert(Space.objectVisible(itemBallDef) == true, "objectVisible true before pickup")
|
|
|
|
local testItemScript = {
|
|
{ op = "setorcopyvar", [1] = 0x8000, [2] = 34 }, -- Great Ball
|
|
{ op = "setorcopyvar", [1] = 0x8001, [2] = 1 },
|
|
{ op = "callstd", [1] = 1, std = 1 },
|
|
{ op = "end" },
|
|
}
|
|
|
|
local Field = require("src.core.game3.field")
|
|
Field._session = { bag = testBag }
|
|
|
|
local Adapters = require("src.core.game3.scripting.adapters")
|
|
local adapters = Adapters.host(nil, nil, nil)
|
|
Space.vm = Vm.new({
|
|
store = Space.store,
|
|
scripts = {
|
|
test_item_ball = testItemScript,
|
|
-- data/scripts/obtain_item.inc:106
|
|
["std:1"] = {
|
|
{ op = "copyvar", [1] = 0x8004, [2] = 0x8000 },
|
|
{ op = "copyvar", [1] = 0x8005, [2] = 0x8001 },
|
|
{ op = "checkitemspace", [1] = 0x8000, [2] = 0x8001 },
|
|
{ op = "copyvar", [1] = 0x8007, [2] = 0x800D },
|
|
{ op = "compare_var_to_value", var = 0x8007, value = 1 },
|
|
{ op = "call_if", cond = 1, target = "EventScript_PickUpItem" },
|
|
{ op = "return" },
|
|
},
|
|
-- data/scripts/obtain_item.inc:122
|
|
EventScript_PickUpItem = {
|
|
{ op = "removeobject", [1] = 0x800F },
|
|
{ op = "additem", [1] = 0x8004, [2] = 0x8005 },
|
|
{ op = "return" },
|
|
},
|
|
},
|
|
adapters = adapters,
|
|
})
|
|
|
|
local prevCount = Bag.get(testBag, 34) or 0
|
|
Space.startScript("test_item_ball", 5, 1)
|
|
|
|
assert(Bag.get(testBag, 34) == prevCount + 1, "Great Ball added to bag")
|
|
assert(Flags.getFlag(Space.store, nil, 340) == true, "Flag 340 set by item pickup")
|
|
assert(spawned.visible == false, "Item ball object hidden")
|
|
assert(Space.objectVisible(itemBallDef) == false, "objectVisible false after pickup")
|
|
print("[ok] Ground item pickup sets flag and removes object")
|
|
|
|
print("[test] all passed successfully!")
|
|
|