mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
516 lines
20 KiB
Lua
516 lines
20 KiB
Lua
#!/usr/bin/env luajit
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
require("tests.fixture_data.game3_items").install()
|
|
|
|
local GameVersion = require("src.core.GameVersion")
|
|
GameVersion.set("firered")
|
|
|
|
local Prize = require("src.ui.game3.prize_corner")
|
|
local Corner = require("src.core.game3.scripting.natives_corner")
|
|
local Bag = require("src.core.game3.bag")
|
|
|
|
local failed = 0
|
|
local function check(cond, msg)
|
|
if cond then
|
|
print("[ok] " .. msg)
|
|
else
|
|
failed = failed + 1
|
|
print("[FAIL] " .. msg)
|
|
end
|
|
end
|
|
|
|
local function eq(a, b, msg)
|
|
check(a == b, string.format("%s (%s == %s)", msg, tostring(a), tostring(b)))
|
|
end
|
|
|
|
local PRET_STRINGS = "../pokefirered/src/strings.c"
|
|
local PRET_MENU = "../pokefirered/src/script_menu.c"
|
|
local PRET_SPECIALS = "../pokefirered/src/field_specials.c"
|
|
local PRET_PRIZE_ROOM = "../pokefirered/data/maps/CeladonCity_GameCorner_PrizeRoom/scripts.inc"
|
|
|
|
local function slurp(path)
|
|
local f = io.open(path, "rb")
|
|
if not f then return nil end
|
|
local d = f:read("*a")
|
|
f:close()
|
|
return d
|
|
end
|
|
|
|
local strings = slurp(PRET_STRINGS)
|
|
local menu = slurp(PRET_MENU)
|
|
local prizeRoom = slurp(PRET_PRIZE_ROOM)
|
|
|
|
-- pokefirered/src/script_menu.c:520
|
|
local LIST_SYMBOL = {
|
|
[Prize.LIST_POKEMON_PRIZES] = "sMultichoiceList_GameCornerPokemonPrizes",
|
|
[Prize.LIST_COIN_PURCHASE] = "sMultichoiceList_GameCornerCoinPurchaseCounter",
|
|
[Prize.LIST_TM_PRIZES] = "sMultichoiceList_GameCornerTMPrizes",
|
|
[Prize.LIST_BATTLE_ITEM_PRIZES] = "sMultichoiceList_GameCornerBattleItemPrizes",
|
|
}
|
|
|
|
local function listSymbols(listId)
|
|
if not menu then return nil end
|
|
local decl = "static const struct MenuAction " .. LIST_SYMBOL[listId] .. "[] = {"
|
|
local i = menu:find(decl, 1, true)
|
|
if not i then return nil end
|
|
local stop = menu:find("\n};", i, true)
|
|
local body = menu:sub(i + #decl, stop)
|
|
-- pokefirered/src/script_menu.c:318
|
|
local lg = body:find("#elif defined(LEAFGREEN)", 1, true)
|
|
local head = lg and body:sub(1, lg - 1) or body
|
|
local tail = lg and body:sub((body:find("#endif", lg, true) or #body)) or ""
|
|
local out = {}
|
|
for sym in (head .. tail):gmatch("{%s*(g[%w_]+)%s*}") do out[#out + 1] = sym end
|
|
return out
|
|
end
|
|
|
|
local function flatten(literal)
|
|
local s = literal:gsub("{[^}]*}", " ")
|
|
s = s:gsub("%s+", " "):gsub("^%s+", ""):gsub("%s+$", "")
|
|
return s
|
|
end
|
|
|
|
local INVENTED_SIX = {
|
|
"WIDGET 1,234 COINS", "GIZMO 999 COINS", "DOODAD 12,345 COINS",
|
|
"GADGET 40 COINS", "THINGUMMY 7 COINS", "LAST ROW",
|
|
}
|
|
local INVENTED_TWO = { "WIDGET 1,234 COINS", "LAST ROW" }
|
|
|
|
local function stringLiteral(sym)
|
|
if not strings then return nil end
|
|
local pat = "const u8 " .. sym .. "%[%] = _%(\"(.-)\"%);"
|
|
return strings:match(pat)
|
|
end
|
|
|
|
print("[test] 1. Every Game Corner prize list is routed to the prize counter")
|
|
do
|
|
eq(Prize.LIST_POKEMON_PRIZES, 14, "MULTICHOICE_GAME_CORNER_POKEMON_PRIZES")
|
|
eq(Prize.LIST_COIN_PURCHASE, 27, "MULTICHOICE_GAME_CORNER_COIN_PURCHASE_COUNTER")
|
|
eq(Prize.LIST_TM_PRIZES, 30, "MULTICHOICE_GAME_CORNER_TMPRIZES")
|
|
eq(Prize.LIST_BATTLE_ITEM_PRIZES, 41, "MULTICHOICE_GAME_CORNER_BATTLE_ITEM_PRIZES")
|
|
for _, id in ipairs({ 14, 27, 30, 41 }) do
|
|
check(Prize.isPrizeList(id), "list " .. id .. " is a prize list")
|
|
end
|
|
check(not Prize.isPrizeList(5), "an ordinary list is left to the generic menu")
|
|
check(not Prize.isPrizeList(nil), "a missing list id is not a prize list")
|
|
end
|
|
|
|
print("[test] 2. The column stops match pret's CLEAR_TO byte for byte")
|
|
if not (strings and menu) then
|
|
print("[skip] no pret checkout at " .. PRET_STRINGS)
|
|
else
|
|
for listId, columns in pairs(Prize.COLUMNS) do
|
|
local syms = listSymbols(listId)
|
|
check(syms ~= nil and #syms > 0, "pret declares " .. LIST_SYMBOL[listId])
|
|
if syms then
|
|
for i, sym in ipairs(syms) do
|
|
local lit = stringLiteral(sym)
|
|
check(lit ~= nil, sym .. " has a string literal")
|
|
if lit then
|
|
local stop = lit:match("{CLEAR_TO 0x(%x+)}")
|
|
eq(columns[i], stop and tonumber(stop, 16) or nil,
|
|
string.format("list %d row %d column stop (%s)", listId, i, sym))
|
|
end
|
|
end
|
|
eq(#syms, #columns + 1, "list " .. listId .. " ends on a row with no price")
|
|
end
|
|
end
|
|
end
|
|
|
|
print("[test] 3. The name and the price split out of the flattened label")
|
|
if not (strings and menu) then
|
|
print("[skip] no pret checkout at " .. PRET_STRINGS)
|
|
else
|
|
for listId, columns in pairs(Prize.COLUMNS) do
|
|
local syms = listSymbols(listId)
|
|
if syms then
|
|
local labels = {}
|
|
for i, sym in ipairs(syms) do labels[i] = flatten(stringLiteral(sym) or "") end
|
|
local rows = Prize.buildRows(listId, labels)
|
|
eq(#rows, #syms, "list " .. listId .. " built every row")
|
|
for i, sym in ipairs(syms) do
|
|
local lit = stringLiteral(sym) or ""
|
|
local priceSrc = lit:match("{CLEAR_TO 0x%x+}(.*)$")
|
|
if priceSrc then
|
|
local expect = flatten(priceSrc)
|
|
eq(rows[i].amount, expect, string.format("list %d row %d price (%s)", listId, i, sym))
|
|
local nameSrc = lit:match("^(.-){CLEAR_TO")
|
|
eq(rows[i].name, flatten(nameSrc or ""), string.format("list %d row %d name (%s)", listId, i, sym))
|
|
eq(rows[i].column, columns[i], string.format("list %d row %d keeps its stop", listId, i))
|
|
else
|
|
eq(rows[i].amount, nil, string.format("list %d row %d has no price (%s)", listId, i, sym))
|
|
eq(rows[i].name, flatten(lit), string.format("list %d row %d is the whole label", listId, i))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
print("[test] 4. The coin purchase counter prints its whole row in the small font")
|
|
if not strings then
|
|
print("[skip] no pret checkout at " .. PRET_STRINGS)
|
|
else
|
|
local lit = stringLiteral("gText_50Coins_1000")
|
|
check(lit ~= nil, "pret declares gText_50Coins_1000")
|
|
if lit then
|
|
check(lit:find("{FONT_SMALL}", 1, true) < lit:find("{CLEAR_TO", 1, true),
|
|
"the small font is selected before the column stop")
|
|
end
|
|
check(Prize.NAME_SMALL[Prize.LIST_COIN_PURCHASE] == true,
|
|
"the coin counter name column uses the small font")
|
|
check(Prize.NAME_SMALL[Prize.LIST_TM_PRIZES] == nil,
|
|
"the TM prize name column uses the normal font")
|
|
end
|
|
|
|
print("[test] 5. Window size and position are pret's")
|
|
if not menu then
|
|
print("[skip] no pret checkout at " .. PRET_MENU)
|
|
else
|
|
local heights = {}
|
|
local body = menu:match("static u8 GetMCWindowHeight%(u8 count%)%s*{(.-)\n}")
|
|
check(body ~= nil, "pret declares GetMCWindowHeight")
|
|
if body then
|
|
for c, h in body:gmatch("case (%d+):%s*return (%d+);") do
|
|
heights[tonumber(c)] = tonumber(h)
|
|
end
|
|
for c, h in pairs(heights) do
|
|
eq(Prize.WINDOW_HEIGHT[c], h, "window height for " .. c .. " rows")
|
|
end
|
|
end
|
|
|
|
local rows = Prize.buildRows(Prize.LIST_TM_PRIZES, INVENTED_SIX)
|
|
local widest = 0
|
|
for _, row in ipairs(rows) do
|
|
local w = Prize.rowWidth(row)
|
|
if w > widest then widest = w end
|
|
end
|
|
-- pokefirered/src/script_menu.c:736
|
|
local expectWidth = math.floor((widest + 9) / 8) + 1
|
|
local g = Prize.layout(Prize.LIST_TM_PRIZES, rows, 11, 0)
|
|
eq(g.width, expectWidth, "the window is as wide as pret makes it")
|
|
eq(g.height, Prize.WINDOW_HEIGHT[6], "six rows give pret's height")
|
|
-- pokefirered/src/script_menu.c:1195
|
|
eq(g.tileX, g.left + 1, "the window is one tile right of the script operand")
|
|
eq(g.tileY, 1, "the window is one tile below the script operand")
|
|
|
|
local wide = Prize.layout(Prize.LIST_TM_PRIZES, rows, 26, 0)
|
|
check(wide.left + wide.width <= 28, "a window that would run off screen is pulled left")
|
|
eq(wide.left, 28 - wide.width, "pret's clamp is 28 tiles")
|
|
end
|
|
|
|
print("[test] 6. Only a list of more than three rows wraps around")
|
|
if not menu then
|
|
print("[skip] no pret checkout at " .. PRET_MENU)
|
|
else
|
|
local at = menu:find("static void CreateMCMenuInputHandlerTask(u8 ignoreBpress, u8 count, u8 windowId, u8 mcId)\n{", 1, true)
|
|
local body = at and menu:sub(at, menu:find("\n}", at, true))
|
|
check(body ~= nil and body:find("if (count > 3)", 1, true) ~= nil
|
|
and body:find("tWrapAround = TRUE", 1, true) ~= nil,
|
|
"pret wraps only above three rows")
|
|
check(Prize.wrapsAround(6), "the six row prize lists wrap")
|
|
check(not Prize.wrapsAround(3), "the three row coin counter does not wrap")
|
|
|
|
Prize.show({ listId = Prize.LIST_COIN_PURCHASE, left = 13, top = 0,
|
|
labels = { "1,111 COINS \194\1652,222", "3,333 COINS \194\1654,444", "LAST ROW" } })
|
|
eq(Prize.cursor, 1, "the coin counter opens on the first row")
|
|
Prize.move(-1)
|
|
eq(Prize.cursor, 1, "up on the first row of a short list does nothing")
|
|
Prize.move(1)
|
|
Prize.move(1)
|
|
eq(Prize.cursor, 3, "the cursor walks down")
|
|
Prize.move(1)
|
|
eq(Prize.cursor, 3, "down on the last row of a short list does nothing")
|
|
Prize.reset()
|
|
|
|
Prize.show({ listId = Prize.LIST_TM_PRIZES, left = 11, top = 0, labels = INVENTED_SIX })
|
|
Prize.move(-1)
|
|
eq(Prize.cursor, 6, "a six row list wraps to the bottom")
|
|
Prize.move(1)
|
|
eq(Prize.cursor, 1, "and back to the top")
|
|
Prize.reset()
|
|
end
|
|
|
|
print("[test] 7. The counter reports pret's selection and pret's cancel value")
|
|
do
|
|
local picked
|
|
Prize.show({ listId = Prize.LIST_POKEMON_PRIZES, left = 11, top = 0,
|
|
labels = INVENTED_SIX,
|
|
onChoose = function(sel) picked = sel end })
|
|
check(Prize.isOpen(), "the counter is open")
|
|
Prize.move(1)
|
|
Prize.move(1)
|
|
Prize.confirm()
|
|
eq(picked, 2, "A reports the zero based row")
|
|
check(not Prize.isOpen(), "the counter closed")
|
|
|
|
picked = nil
|
|
Prize.show({ listId = Prize.LIST_POKEMON_PRIZES, left = 11, top = 0,
|
|
labels = INVENTED_TWO,
|
|
onChoose = function(sel) picked = sel end })
|
|
Prize.cancel()
|
|
eq(picked, Prize.SCR_MENU_CANCEL, "B reports SCR_MENU_CANCEL")
|
|
eq(Prize.SCR_MENU_CANCEL, 127, "SCR_MENU_CANCEL is 127")
|
|
|
|
picked = nil
|
|
Prize.show({ listId = Prize.LIST_POKEMON_PRIZES, left = 11, top = 0,
|
|
labels = INVENTED_TWO, ignoreBPress = true,
|
|
onChoose = function(sel) picked = sel end })
|
|
Prize.cancel()
|
|
eq(picked, nil, "B is ignored when the script asks for it")
|
|
check(Prize.isOpen(), "the counter stays open")
|
|
Prize.reset()
|
|
end
|
|
|
|
print("[test] 8. The prize list blocks the script through the multichoice opcode")
|
|
do
|
|
local Flags = require("src.core.game3.scripting.flags")
|
|
local Vm = require("src.core.game3.scripting.vm")
|
|
local Adapters = require("src.core.game3.scripting.adapters")
|
|
local VAR_RESULT = 0x800D
|
|
|
|
local store = Flags.newStore()
|
|
local vm = Vm.new({
|
|
store = store,
|
|
scripts = {
|
|
t = {
|
|
{ op = "multichoice", [1] = 11, [2] = 0, [3] = Prize.LIST_TM_PRIZES, [4] = 0 },
|
|
{ op = "copyvar", [1] = 0x4002, [2] = VAR_RESULT },
|
|
{ op = "setvar", [1] = 0x4001, [2] = 9 },
|
|
{ op = "end" },
|
|
},
|
|
},
|
|
adapters = Adapters.host(nil, nil, nil),
|
|
})
|
|
vm:start("t")
|
|
check(Prize.isOpen(), "the TM prize list opened the prize counter")
|
|
eq(vm.ctx.status, "waiting", "the script is blocked on the list")
|
|
eq(Flags.getVar(store, vm.ctx, 0x4001), 0, "the script has not run past the list")
|
|
vm:tick()
|
|
check(Prize.isOpen(), "the script stays blocked while the list is open")
|
|
Prize.move(1)
|
|
Prize.confirm()
|
|
vm:tick()
|
|
eq(Flags.getVar(store, vm.ctx, 0x4002), 1, "the chosen row landed in VAR_RESULT")
|
|
eq(Flags.getVar(store, vm.ctx, 0x4001), 9, "the script resumed")
|
|
end
|
|
|
|
print("[test] 9. A purchase debits coins, and a short purse and a full bag refuse")
|
|
do
|
|
local Flags = require("src.core.game3.scripting.flags")
|
|
local Vm = require("src.core.game3.scripting.vm")
|
|
local Adapters = require("src.core.game3.scripting.adapters")
|
|
local Runtime = require("src.core.game3.runtime")
|
|
local ItemsData = require("src.core.game3.items_data")
|
|
local VAR_RESULT = 0x800D
|
|
local VAR_TEMP_1, VAR_TEMP_2, VAR_TEMP_3 = 0x4001, 0x4002, 0x4003
|
|
-- pokefirered/include/constants/items.h:205
|
|
local ITEM_SMOKE_BALL = 194
|
|
local SMOKE_BALL_PRICE = 800
|
|
|
|
-- pokefirered/data/maps/CeladonCity_GameCorner_PrizeRoom/scripts.inc:312
|
|
local prizeScript = {
|
|
TryGivePrize = {
|
|
{ op = "checkcoins", [1] = VAR_RESULT },
|
|
{ op = "compare_var_to_var", [1] = VAR_RESULT, [2] = VAR_TEMP_2 },
|
|
{ op = "goto_if", [1] = 0, [2] = "NotEnoughCoins" },
|
|
{ op = "checkitemspace", [1] = VAR_TEMP_1, [2] = 1 },
|
|
{ op = "compare_var_to_value", [1] = VAR_RESULT, [2] = 0 },
|
|
{ op = "goto_if", [1] = 1, [2] = "BagFull" },
|
|
{ op = "removecoins", [1] = VAR_TEMP_2 },
|
|
{ op = "additem", [1] = VAR_TEMP_1, [2] = 1 },
|
|
{ op = "setvar", [1] = VAR_TEMP_3, [2] = 1 },
|
|
{ op = "end" },
|
|
},
|
|
NotEnoughCoins = { { op = "setvar", [1] = VAR_TEMP_3, [2] = 2 }, { op = "end" } },
|
|
-- pokefirered/data/maps/CeladonCity_GameCorner_PrizeRoom/scripts.inc:324
|
|
BagFull = { { op = "setvar", [1] = VAR_TEMP_3, [2] = 3 }, { op = "end" } },
|
|
}
|
|
|
|
local prevSession = Runtime.session
|
|
local function buy(session, item, price)
|
|
Runtime.session = session
|
|
local store = Flags.newStore()
|
|
store.vars[VAR_TEMP_1] = item
|
|
store.vars[VAR_TEMP_2] = price
|
|
local vm = Vm.new({ store = store, scripts = prizeScript, adapters = Adapters.host(nil, nil, nil) })
|
|
vm:start("TryGivePrize")
|
|
for _ = 1, 12 do vm:tick() end
|
|
return Flags.getVar(store, vm.ctx, VAR_TEMP_3)
|
|
end
|
|
|
|
local rich = { coins = SMOKE_BALL_PRICE, bag = Bag.new() }
|
|
eq(buy(rich, ITEM_SMOKE_BALL, SMOKE_BALL_PRICE), 1, "the purchase runs to the end of the script")
|
|
eq(Bag.Coins.get(rich), 0, "removecoins took exactly the price")
|
|
eq(Bag.get(rich.bag, ITEM_SMOKE_BALL), 1, "the prize landed in the bag")
|
|
|
|
local short = { coins = SMOKE_BALL_PRICE - 1, bag = Bag.new() }
|
|
eq(buy(short, ITEM_SMOKE_BALL, SMOKE_BALL_PRICE), 2, "one coin short takes the NotEnoughCoins branch")
|
|
eq(Bag.Coins.get(short), SMOKE_BALL_PRICE - 1, "a short purse loses no coins")
|
|
eq(Bag.get(short.bag, ITEM_SMOKE_BALL), 0, "a short purse gets no prize")
|
|
|
|
-- pokefirered/src/item.c:195 CheckBagHasSpace fails when every slot of the pocket is taken
|
|
local full = { coins = SMOKE_BALL_PRICE, bag = Bag.new() }
|
|
local id, added = 1, 0
|
|
while added < (ItemsData.CAPACITY.ITEMS or 42) do
|
|
if id ~= ITEM_SMOKE_BALL and ItemsData.pocketOf(id) == "ITEMS" then
|
|
if Bag.add(full.bag, id, 1) then added = added + 1 end
|
|
end
|
|
id = id + 1
|
|
if id > 400 then break end
|
|
end
|
|
eq(added, ItemsData.CAPACITY.ITEMS, "the ITEMS pocket is filled to pret's capacity")
|
|
check(not Bag.canAdd(full.bag, ITEM_SMOKE_BALL, 1), "checkitemspace fails on a full pocket")
|
|
eq(buy(full, ITEM_SMOKE_BALL, SMOKE_BALL_PRICE), 3, "a full bag takes the BagFull branch")
|
|
eq(Bag.Coins.get(full), SMOKE_BALL_PRICE, "a full bag loses no coins")
|
|
eq(Bag.get(full.bag, ITEM_SMOKE_BALL), 0, "a full bag gets no prize")
|
|
Runtime.session = prevSession
|
|
end
|
|
|
|
print("[test] 10. Every prize price in the list matches the price the script takes")
|
|
if not (strings and menu and prizeRoom) then
|
|
print("[skip] no pret checkout at " .. PRET_PRIZE_ROOM)
|
|
else
|
|
local scriptPrices = {}
|
|
for label, price in prizeRoom:gmatch("EventScript_(%w+)::%s*\n%s*%.ifdef FIRERED\n%s*setvar VAR_TEMP_1, [%w_]+\n%s*setvar VAR_TEMP_2, (%d+)") do
|
|
scriptPrices[#scriptPrices + 1] = tonumber(price)
|
|
end
|
|
for label, price in prizeRoom:gmatch("EventScript_(%w+)::%s*\n%s*setvar VAR_TEMP_1, [%w_]+\n%s*setvar VAR_TEMP_2, (%d+)") do
|
|
scriptPrices[#scriptPrices + 1] = tonumber(price)
|
|
end
|
|
local seen = {}
|
|
for _, p in ipairs(scriptPrices) do seen[p] = true end
|
|
check(next(seen) ~= nil, "the prize room script sets prices")
|
|
|
|
for _, listId in ipairs({ Prize.LIST_POKEMON_PRIZES, Prize.LIST_TM_PRIZES, Prize.LIST_BATTLE_ITEM_PRIZES }) do
|
|
local syms = listSymbols(listId)
|
|
if syms then
|
|
local labels = {}
|
|
for i, sym in ipairs(syms) do labels[i] = flatten(stringLiteral(sym) or "") end
|
|
local rows = Prize.buildRows(listId, labels)
|
|
for i, row in ipairs(rows) do
|
|
if row.amount then
|
|
local digits = row.amount:gsub("[^%d]", "")
|
|
check(seen[tonumber(digits)] == true,
|
|
string.format("list %d row %d price %s is a price the script takes", listId, i, digits))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
print("[test] 11. CheckAddCoins guards the coin case against overflow")
|
|
do
|
|
eq(Corner.SPECIAL.CheckAddCoins, 0x15E, "CheckAddCoins is special 0x15E")
|
|
eq(Corner.MAX_COINS, 9999, "MAX_COINS is 9999")
|
|
eq(Corner.checkAddCoins(0, 10), 1, "an empty case takes ten coins")
|
|
eq(Corner.checkAddCoins(9989, 10), 1, "a case that ends exactly full takes them")
|
|
eq(Corner.checkAddCoins(9990, 10), 0, "one coin of overflow is refused")
|
|
eq(Corner.checkAddCoins(9999, 1), 0, "a full case takes nothing")
|
|
eq(Corner.checkAddCoins(9999, 0), 1, "adding nothing is always allowed")
|
|
end
|
|
|
|
print("[test] 12. CheckAddCoins is dispatched, and it reads pret's two vars")
|
|
do
|
|
local Natives = require("src.core.game3.scripting.natives")
|
|
local Flags = require("src.core.game3.scripting.flags")
|
|
local Vm = require("src.core.game3.scripting.vm")
|
|
local Adapters = require("src.core.game3.scripting.adapters")
|
|
local VAR_RESULT = 0x800D
|
|
local VAR_0x8006 = 0x8006
|
|
|
|
check(Natives.MODULES["natives_corner"] ~= nil, "natives_corner is loaded by the dispatcher")
|
|
|
|
local store = Flags.newStore()
|
|
local vm = Vm.new({
|
|
store = store,
|
|
scripts = {
|
|
-- pokefirered/data/scripts/obtain_item.inc:197
|
|
t = {
|
|
{ op = "setvar", [1] = VAR_RESULT, [2] = 9990 },
|
|
{ op = "setvar", [1] = VAR_0x8006, [2] = 10 },
|
|
{ op = "specialvar", [1] = VAR_RESULT, [2] = Corner.SPECIAL.CheckAddCoins },
|
|
{ op = "copyvar", [1] = 0x4003, [2] = VAR_RESULT },
|
|
{ op = "end" },
|
|
},
|
|
u = {
|
|
{ op = "setvar", [1] = VAR_RESULT, [2] = 40 },
|
|
{ op = "setvar", [1] = VAR_0x8006, [2] = 10 },
|
|
{ op = "specialvar", [1] = VAR_RESULT, [2] = Corner.SPECIAL.CheckAddCoins },
|
|
{ op = "copyvar", [1] = 0x4004, [2] = VAR_RESULT },
|
|
{ op = "end" },
|
|
},
|
|
},
|
|
adapters = Adapters.host(nil, nil, nil),
|
|
})
|
|
vm:start("t")
|
|
for _ = 1, 8 do vm:tick() end
|
|
eq(Flags.getVar(store, vm.ctx, 0x4003), 0, "a case that would overflow answers FALSE")
|
|
vm:start("u")
|
|
for _ = 1, 8 do vm:tick() end
|
|
eq(Flags.getVar(store, vm.ctx, 0x4004), 1, "a case with room answers TRUE")
|
|
end
|
|
|
|
print("[test] 13. The special id comes out of pret's def_special order")
|
|
if not slurp("../pokefirered/data/specials.inc") then
|
|
print("[skip] no pret checkout at ../pokefirered/data/specials.inc")
|
|
else
|
|
local src = slurp("../pokefirered/data/specials.inc")
|
|
local index, found = 0, nil
|
|
for line in src:gmatch("[^\n]+") do
|
|
local name = line:match("^%s+def_special%s+([%w_]+)")
|
|
if name then
|
|
if name == "CheckAddCoins" then found = index end
|
|
index = index + 1
|
|
end
|
|
end
|
|
eq(found, Corner.SPECIAL.CheckAddCoins, "CheckAddCoins is at pret's def_special index")
|
|
local body = slurp(PRET_SPECIALS)
|
|
if body then
|
|
local fn = body:match("bool8 CheckAddCoins%(void%)%s*{(.-)\n}")
|
|
check(fn ~= nil and fn:find("gSpecialVar_Result + gSpecialVar_0x8006 > 9999", 1, true) ~= nil,
|
|
"pret compares VAR_RESULT plus VAR_0x8006 against 9999")
|
|
end
|
|
end
|
|
|
|
print("[test] 14. The TM clerk buffers a move name, not a move number")
|
|
do
|
|
local Flags = require("src.core.game3.scripting.flags")
|
|
local Vm = require("src.core.game3.scripting.vm")
|
|
local Adapters = require("src.core.game3.scripting.adapters")
|
|
local Cache = require("tests.game3_cache")
|
|
local root = Cache.mount("pokemon/move_names.lua")
|
|
if not root then
|
|
print("[skip] no extracted cache for move names: " .. tostring(Cache.reason))
|
|
else
|
|
local Pokemon = require("src.core.game3.pokemon")
|
|
Pokemon.install(nil)
|
|
-- pokefirered/data/maps/CeladonCity_GameCorner_PrizeRoom/scripts.inc:267
|
|
local MOVE_ICE_BEAM = 58
|
|
local expect = Pokemon.moveName(MOVE_ICE_BEAM)
|
|
check(expect ~= nil and expect ~= tostring(MOVE_ICE_BEAM), "the cache knows move 58 as " .. tostring(expect))
|
|
|
|
local store = Flags.newStore()
|
|
local vm = Vm.new({
|
|
store = store,
|
|
scripts = {
|
|
t = {
|
|
{ op = "buffermovename", [1] = 1, [2] = MOVE_ICE_BEAM },
|
|
{ op = "end" },
|
|
},
|
|
},
|
|
adapters = Adapters.host(nil, nil, nil),
|
|
})
|
|
vm:start("t")
|
|
local buffered = vm.ctx.stringVars and vm.ctx.stringVars[2]
|
|
eq(buffered, expect, "STR_VAR_2 holds the move name")
|
|
check(buffered ~= tostring(MOVE_ICE_BEAM), "STR_VAR_2 is not the raw move id")
|
|
end
|
|
end
|
|
|
|
if failed > 0 then
|
|
print("[test] FAILED " .. failed)
|
|
os.exit(1)
|
|
end
|
|
print("[test] all passed")
|