Files
gen1recomp/tests/game3_bag_test.lua
T
bryanthaboi d920f7acdb clean up
2026-09-22 17:05:12 -04:00

240 lines
8.8 KiB
Lua

#!/usr/bin/env luajit
-- Game3 bag: pret ItemSlot pockets, items pack, checkitem APIs, save migrate.
package.path = "./?.lua;./?/init.lua;" .. package.path
require("tests.game3_cache").requireData("game3_bag_test")
local failed = 0
local function check(cond, msg)
if cond then
print("[ok] " .. msg)
else
failed = failed + 1
print("[FAIL] " .. msg)
end
end
local ItemsData = require("src.core.game3.items_data")
local Bag = require("src.core.game3.bag")
local Schema = require("src.core.game3.save_schema_firered")
local Flags = require("src.core.game3.scripting.flags")
local Ops = require("src.core.game3.scripting.ops_a")
local Vm = require("src.core.game3.scripting.vm")
local Std = require("src.core.game3.scripting.stdscripts")
print("[test] 1. Items pack load")
ItemsData.install(nil)
local potion = ItemsData.info(13)
check(potion ~= nil and potion.name == "POTION", "POTION id 13 from pack/fallback")
check(ItemsData.pocketOf(13) == "ITEMS", "POTION pocket ITEMS")
check(ItemsData.pocketResult(13) == 1, "pocketResult ITEMS=1")
check(ItemsData.pocketResult(4) == 3, "POKE_BALL pocketResult=3")
check(ItemsData.CAPACITY.ITEMS == 42, "ITEMS capacity 42")
check(ItemsData.CAPACITY.KEY_ITEMS == 30, "KEY capacity 30")
check(ItemsData.displayName(364) == "TM CASE" or ItemsData.displayName(364):find("TM"), "TM CASE name")
print("[test] 2. Bag.add / canAdd / has / listPocket")
local bag = Bag.new()
check(Bag.canAdd(bag, 13, 1) == true, "empty bag can add POTION")
local ok = select(1, Bag.add(bag, 13, 5))
check(ok == true, "add 5 POTION")
check(Bag.get(bag, 13) == 5, "get POTION qty 5")
check(Bag.has(bag, 13, 5) == true, "has 5 POTION")
check(Bag.has(bag, 13, 6) == false, "not has 6 POTION")
local rows = Bag.listPocket(bag, "ITEMS")
check(#rows == 1 and rows[1].id == 13 and rows[1].qty == 5, "listPocket ITEMS slot order")
print("[test] 3. Capacity 42 + TM Case auto-grant")
bag = Bag.new()
for i = 1, 42 do
Bag.add(bag, 12 + i, 1) -- 13..54
end
check(#bag.pockets.ITEMS == 42, "filled 42 ITEMS slots")
check(Bag.canAdd(bag, 55, 1) == false, "43rd unique ITEMS item fails")
check(select(1, Bag.add(bag, 13, 1)) == true, "stack into existing POTION still ok")
bag = Bag.new()
ok = select(1, Bag.add(bag, 289, 1)) -- TM01
check(ok == true, "add TM01")
check(Bag.has(bag, ItemsData.ITEM_TM_CASE, 1) == true, "TM CASE auto-granted to KEY_ITEMS")
check(#Bag.listPocket(bag, "TM_CASE") == 1, "TM in TM_CASE pocket")
print("[test] 4. Migrate stacks + schema newGame")
local legacy = { stacks = { POTION = 3, [4] = 2 } }
local mig = Bag.migrate(legacy)
check(Bag.get(mig, 13) >= 3 or Bag.get(mig, "POTION") >= 3, "migrate POTION stacks")
check(mig.pockets and mig.pockets.ITEMS, "migrate has pockets")
local session = Schema.newGame()
check(session.bag and session.bag.pockets and session.bag.pockets.ITEMS, "newGame bag pockets")
local loaded = Schema.fromSaveTable({
bag = { stacks = { POTION = 7 } },
name = "ASH",
})
check(Bag.get(loaded.bag, 13) == 7 or Bag.get(loaded.bag, "POTION") == 7, "fromSaveTable migrates stacks")
print("[test] 5. checkitem / checkitemtype ops")
-- Mock Runtime session for adapters / ops fallbacks
local Runtime = { _session = { bag = Bag.new() } }
function Runtime.getSession() return Runtime._session end
package.loaded["src.core.game3.runtime"] = Runtime
Bag.add(Runtime._session.bag, 13, 2)
local store = Flags.newStore()
local vm = Vm.new({
store = store,
scripts = {
t_check = {
{ op = "checkitem", [1] = 13, [2] = 2 },
{ op = "copyvar", [1] = 0x4000, [2] = 0x800D }, -- persist past end wipe
{ op = "end" },
},
t_type = {
{ op = "checkitemtype", [1] = 13 },
{ op = "copyvar", [1] = 0x4001, [2] = 0x800D },
{ op = "end" },
},
t_space = {
{ op = "checkitemspace", [1] = 13, [2] = 1 },
{ op = "copyvar", [1] = 0x4002, [2] = 0x800D },
{ op = "end" },
},
},
text = Std.TEXT,
stdscripts = Std.SCRIPTS,
})
vm:start("t_check")
for _ = 1, 20 do
if not vm:isRunning() then break end
vm:tick()
end
check(Flags.getVar(store, vm.ctx, 0x4000) == 1, "checkitem POTION x2 → true")
vm:start("t_type")
for _ = 1, 20 do
if not vm:isRunning() then break end
vm:tick()
end
check(Flags.getVar(store, vm.ctx, 0x4001) == 1, "checkitemtype POTION → POCKET_ITEMS(1)")
vm:start("t_space")
for _ = 1, 20 do
if not vm:isRunning() then break end
vm:tick()
end
check(Flags.getVar(store, vm.ctx, 0x4002) == 1, "checkitemspace POTION → true")
print("[test] 6. bufferitemname")
vm.ctx.stringVars = { [1] = "", [2] = "", [3] = "" }
Ops.dispatch(vm, { op = "bufferitemname", dest = 1, src = 13 })
check(vm.ctx.stringVars[2] == "POTION", "bufferitemname STR_VAR_2 = POTION")
print("[test] 7. Pass 2 — give / medicine / TM / escape / register")
local ItemUse = require("src.core.game3.item_use")
local Pokemon = require("src.core.game3.pokemon")
Pokemon.install(nil)
-- medicineKind
check(ItemsData.medicineKind(14) == "status", "ANTIDOTE → status")
check(ItemsData.medicineKind(24) == "revive", "REVIVE → revive")
check(ItemsData.fieldUseKind(13) == "heal", "POTION fieldUseKind heal")
-- give held item
local sess = {
name = "RED",
map = "FR_MT_MOON_1F",
healMap = "FR_PEWTER_CITY_POKEMON_CENTER_1F",
healX = 7,
healY = 4,
party = {
{ species = 1, name = "BULBASAUR", hp = 10, maxHp = 20, moves = { 33 }, pp = { 35 }, status = "PSN" },
},
bag = Bag.new(),
}
Bag.add(sess.bag, 13, 2)
local gOk = select(1, ItemUse.giveToMon(sess, sess.bag, 13, 1))
check(gOk == true, "give POTION to mon")
check(Bag.get(sess.bag, 13) == 1, "give consumed one from bag")
check(tonumber(sess.party[1].item) == 13 or sess.party[1].item == 13, "mon holds POTION")
-- status cure
Bag.add(sess.bag, 14, 1)
local sOk = select(1, ItemUse.useField(sess, sess.bag, 14, 1))
check(sOk == true, "ANTIDOTE clears poison")
check(sess.party[1].status == nil, "status cleared")
-- heal
sess.party[1].hp = 5
Bag.add(sess.bag, 13, 1)
local hOk = select(1, ItemUse.useField(sess, sess.bag, 13, 1))
check(hOk == true, "POTION heals")
check(sess.party[1].hp > 5, "HP increased")
-- TM01 → Focus Punch (264); Bulbasaur learnset bit 0
local move = Pokemon.moveFromTmItem(289)
check(move == 264, "TM01 → move 264 Focus Punch (got " .. tostring(move) .. ")")
local can = Pokemon.canLearnTmItem(1, 289)
-- Bulbasaur may or may not learn TM01; just ensure API returns boolean
check(type(can) == "boolean", "canLearnTmItem returns boolean")
-- Escape rope indoors → heal warp (Warp.request needs mod/game; just can_escape path)
Bag.add(sess.bag, 85, 1)
-- Without Runtime mod/game, useEscapeRope still removes + attempts warp
local Runtime = package.loaded["src.core.game3.runtime"] or {}
Runtime._mod = nil
Runtime._game = nil
package.loaded["src.core.game3.runtime"] = Runtime
local eOk, eWhy = ItemUse.useField(sess, sess.bag, 85, nil)
check(eOk == true or eWhy == "escape", "escape rope attempted on indoor map")
-- outdoor refuse
sess.map = "FR_ROUTE_1"
Bag.add(sess.bag, 85, 1)
local e2 = select(1, ItemUse.useField(sess, sess.bag, 85, nil))
check(e2 == false, "escape rope refused outdoors")
-- register
sess.registeredItem = 360
check(sess.registeredItem == 360, "registered BICYCLE")
print("[test] 8. Berry & TM pocket segregation and 3-pocket Bag UI")
local testBag = Bag.new()
Bag.add(testBag, "POTION", 2)
Bag.add(testBag, "BERRY", 3)
Bag.add(testBag, "ORAN_BERRY", 2)
Bag.add(testBag, "CHESTO_BERRY", 1)
Bag.add(testBag, "TM01", 1)
Bag.add(testBag, "HM01", 1)
local itemRows = Bag.listPocket(testBag, "ITEMS")
local keyRows = Bag.listPocket(testBag, "KEY_ITEMS")
local tmRows = Bag.listPocket(testBag, "TM_CASE")
local berryRows = Bag.listPocket(testBag, "BERRY_POUCH")
check(#itemRows == 1 and itemRows[1].name == "POTION", "ITEMS pocket only contains general items")
check(Bag.has(testBag, ItemsData.ITEM_TM_CASE, 1), "TM CASE key item present in KEY_ITEMS")
check(Bag.has(testBag, ItemsData.ITEM_BERRY_POUCH, 1), "BERRY POUCH key item present in KEY_ITEMS")
check(#tmRows == 2, "TM_CASE pocket contains 2 machines")
check(#berryRows == 2, "BERRY_POUCH pocket contains 2 berry kinds")
check(Bag.get(testBag, 139) == 5, "BERRY + ORAN_BERRY merged to 5 Oran berries")
local BagMenu = require("src.ui.game3.bag_menu")
BagMenu.show(nil, testBag)
BagMenu.settle()
check(BagMenu.currentPocket() == "ITEMS", "BagMenu initial pocket is ITEMS")
BagMenu.handleInput({ wasPressed = function(s, k) return k == "right" end })
check(BagMenu.currentPocket() == "KEY_ITEMS", "BagMenu second pocket is KEY_ITEMS")
BagMenu.handleInput({ wasPressed = function(s, k) return k == "right" end })
check(BagMenu.currentPocket() == "POKE_BALLS", "BagMenu third pocket is POKE_BALLS")
BagMenu.handleInput({ wasPressed = function(s, k) return k == "right" end })
check(BagMenu.currentPocket() == "POKE_BALLS", "BagMenu does not wrap past POKE_BALLS (3 pockets only)")
BagMenu.close()
if failed > 0 then
print(string.format("\n%d FAILED", failed))
os.exit(1)
end
print("\nAll bag tests passed.")
os.exit(0)