mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-25 06:55:29 +02:00
1905261c5b
CLOSES #1483, CLOSES #1610, CLOSES #1615, CLOSES #1646, CLOSES #1649, CLOSES #1651, CLOSES #1653, CLOSES #1656, CLOSES #1683, CLOSES #1685, CLOSES #1686, CLOSES #1687, CLOSES #1688, CLOSES #1689, CLOSES #1690, CLOSES #1693, CLOSES #1694, CLOSES #1695, CLOSES #1696, CLOSES #1702, CLOSES #1704, CLOSES #1705, CLOSES #1706, CLOSES #1707, CLOSES #1708, CLOSES #1710, CLOSES #1711, CLOSES #1712, CLOSES #1713, CLOSES #1716, CLOSES #1717, CLOSES #1718, CLOSES #1719, CLOSES #1720, CLOSES #1721, CLOSES #1725, CLOSES #1732, CLOSES #1745, CLOSES #1748, CLOSES #1749, CLOSES #1751, CLOSES #1754
245 lines
8.6 KiB
Lua
245 lines
8.6 KiB
Lua
-- The BICYCLE is used straight off the item list, with no USE/TOSS box
|
|
-- (#1705).
|
|
--
|
|
-- StartMenu_Item's .choseItem reads wCurItem and, before it ever loads
|
|
-- USE_TOSS_MENU_TEMPLATE into wTextBoxID, does `cp BICYCLE / jp z,
|
|
-- .useOrTossItem` (engine/menus/start_sub_menus.asm:340-342). So the bike
|
|
-- mounts, dismounts or refuses on one A press, and -- having no TOSS row to
|
|
-- reach -- can never be thrown away from the bag at all. Every other item,
|
|
-- key items included, still gets the option box.
|
|
--
|
|
-- The port pushed the USE/TOSS Menu for every field item, so the bike took
|
|
-- two presses and offered a TOSS the cart does not.
|
|
-- luajit tests/engine/bag_bicycle_no_options_bug1705.lua
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.harness")
|
|
local check, eq = T.check, T.eq
|
|
love = love or require("tests.love_stub")
|
|
|
|
package.loaded["src.core.Sound"] = { play = function() end, playCry = function() end }
|
|
local music = {}
|
|
package.loaded["src.core.Music"] = {
|
|
playMap = function(_, mapId, biking) music[#music + 1] = { mapId, biking } end,
|
|
}
|
|
package.loaded["src.render.TextBox"] = {
|
|
new = function(_, text, done) return { textBox = true, text = text, done = done } end,
|
|
soundOpts = function() return nil end,
|
|
}
|
|
package.loaded["src.ui.BagMenu"] = nil
|
|
local BagMenu = require("src.ui.BagMenu")
|
|
local Menu = require("src.ui.Menu")
|
|
require("src.ui.Screens").invalidate()
|
|
|
|
local Fixtures = require("tests.modkit.fixtures")
|
|
local Bag = require("src.inventory.Bag")
|
|
|
|
local Data = Fixtures.fresh()
|
|
-- the fixture item table carries neither, and BagMenu reads only
|
|
-- name/keyItem/machine off a def; the branches under test key on the id
|
|
Data.items.BICYCLE = { id = "BICYCLE", index = 6, name = "BICYCLE", price = 0,
|
|
keyItem = true }
|
|
Data.items.TOWN_MAP = { id = "TOWN_MAP", index = 5, name = "TOWN MAP",
|
|
price = 0, keyItem = true }
|
|
Data.items.FIX_POTION = Data.items.FIX_POTION
|
|
or { id = "FIX_POTION", index = 20, name = "FIX POTION", price = 300 }
|
|
|
|
local BIKE_MAP = { id = "FIX_TOWN", def = { tileset = "OVERWORLD" } }
|
|
|
|
local function freshGame(extras)
|
|
local game = {
|
|
data = Data,
|
|
save = {
|
|
party = {},
|
|
player = { name = "RED", id = 1 },
|
|
inventory = {},
|
|
options = {},
|
|
flags = {},
|
|
money = 0,
|
|
},
|
|
}
|
|
game.stack = {
|
|
states = {},
|
|
push = function(self, s) table.insert(self.states, s) end,
|
|
pop = function(self) return table.remove(self.states) end,
|
|
top = function(self) return self.states[#self.states] end,
|
|
}
|
|
game.input = { pressed = nil }
|
|
function game.input:wasPressed(b) return self.pressed == b end
|
|
function game.input:isDown() return false end
|
|
-- IsBikeRidingAllowed reads the tileset off the loaded map, and the
|
|
-- mount/dismount lines read the player's name off the save
|
|
game.overworld = { map = BIKE_MAP, player = { surfing = false } }
|
|
Bag.add(game.save, "FIX_POTION", 3)
|
|
Bag.add(game.save, "BICYCLE", 1)
|
|
Bag.add(game.save, "TOWN_MAP", 1)
|
|
for k, v in pairs(extras or {}) do game.save[k] = v end
|
|
return game
|
|
end
|
|
|
|
local function rowFor(list, id)
|
|
for i, r in ipairs(list.items) do
|
|
if r.value == id then return i end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function isMenu(s) return getmetatable(s) == Menu end
|
|
local function isBox(s) return type(s) == "table" and s.textBox == true end
|
|
|
|
local function inStack(game, pred)
|
|
for _, s in ipairs(game.stack.states) do
|
|
if pred(s) then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Open the bag, put the cursor on `id` and press A once. Returns the list
|
|
-- and every state the press left on the stack above it, so a USE/TOSS box
|
|
-- that appears and is then replaced is still caught.
|
|
local function chooseOnce(game, id)
|
|
local list = BagMenu.new(game, {})
|
|
game.stack:push(list)
|
|
local row = rowFor(list, id)
|
|
if not row then return nil, nil, "no " .. id .. " row in the bag" end
|
|
list.index = row
|
|
local seen = {}
|
|
local realPush = game.stack.push
|
|
game.stack.push = function(self, s)
|
|
seen[#seen + 1] = s
|
|
return realPush(self, s)
|
|
end
|
|
game.input.pressed = "a"
|
|
list:update(1 / 60)
|
|
game.input.pressed = nil
|
|
game.stack.push = realPush
|
|
return list, seen
|
|
end
|
|
|
|
local function sawMenu(seen)
|
|
for _, s in ipairs(seen or {}) do
|
|
if isMenu(s) then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function boxText(game)
|
|
local top = game.stack:top()
|
|
return isBox(top) and top.text or nil
|
|
end
|
|
|
|
-- on foot, somewhere cycling is allowed: one A press mounts
|
|
do
|
|
music = {}
|
|
local game = freshGame()
|
|
local list, seen, why = chooseOnce(game, "BICYCLE")
|
|
if check(list ~= nil, "the bag opened on the BICYCLE row: " .. tostring(why)) then
|
|
check(not sawMenu(seen),
|
|
"no USE/TOSS Menu was pushed, not even for a frame (:340-342)")
|
|
eq(game.save.onBike, true, "the single press mounted the bike")
|
|
local said = boxText(game)
|
|
if check(said ~= nil, "and printed a line") then
|
|
check(said:find("got on", 1, true) ~= nil,
|
|
"which is the mount text: " .. tostring(said))
|
|
end
|
|
eq(game.save.inventory.BICYCLE, 1, "the bike is still in the bag")
|
|
eq(#music, 1, "the bike theme was cued once")
|
|
check(music[1] and music[1][2] == true, "as the riding track")
|
|
end
|
|
end
|
|
|
|
-- already riding: the same single press dismounts
|
|
do
|
|
music = {}
|
|
local game = freshGame({ onBike = true })
|
|
local list, seen = chooseOnce(game, "BICYCLE")
|
|
if check(list ~= nil, "the bag opened while riding") then
|
|
check(not sawMenu(seen), "still no option box on the way off the bike")
|
|
eq(game.save.onBike, false, "one press dismounted")
|
|
local said = boxText(game)
|
|
if check(said ~= nil, "and printed a line") then
|
|
check(said:find("got off", 1, true) ~= nil,
|
|
"which is the dismount text: " .. tostring(said))
|
|
end
|
|
check(music[1] and music[1][2] == false, "and the map theme came back")
|
|
end
|
|
end
|
|
|
|
-- Cycling Road: the refusal is reached on the same single press, and the
|
|
-- item list stays open behind it (`jp ItemMenuLoop`, #513)
|
|
do
|
|
local game = freshGame({ onBike = true, forcedBike = true })
|
|
local list, seen = chooseOnce(game, "BICYCLE")
|
|
if check(list ~= nil, "the bag opened on the Cycling Road") then
|
|
check(not sawMenu(seen), "the refusal is not behind an option box either")
|
|
eq(game.save.onBike, true, "and the player is still riding")
|
|
local said = boxText(game)
|
|
if check(said ~= nil, "the refusal printed") then
|
|
check(said:find("get off", 1, true) ~= nil,
|
|
"with _CannotGetOffHereText: " .. tostring(said))
|
|
end
|
|
check(inStack(game, function(s) return s == list end),
|
|
"the bag list is still on the stack under it (#513)")
|
|
end
|
|
end
|
|
|
|
-- somewhere cycling is not allowed: still one press, still no box
|
|
do
|
|
local game = freshGame()
|
|
game.overworld.map = { id = "FIX_INDOORS", def = { tileset = "FIX_HOUSE" } }
|
|
local list, seen = chooseOnce(game, "BICYCLE")
|
|
if check(list ~= nil, "the bag opened indoors") then
|
|
check(not sawMenu(seen), "the no-cycling refusal skips the box too")
|
|
eq(game.save.onBike, nil, "and nothing was mounted")
|
|
local said = boxText(game)
|
|
check(said ~= nil and said:find("cycling", 1, true) ~= nil,
|
|
"NoCyclingAllowedHere printed: " .. tostring(said))
|
|
end
|
|
end
|
|
|
|
-- the control cases: everything else still gets USE/TOSS, key items included
|
|
do
|
|
local game = freshGame()
|
|
local list, seen = chooseOnce(game, "FIX_POTION")
|
|
if check(list ~= nil, "the bag opened on a plain item") then
|
|
check(sawMenu(seen), "an ordinary item still opens the option box")
|
|
check(isMenu(game.stack:top()), "and it is what the A press left on top")
|
|
end
|
|
end
|
|
|
|
do
|
|
local game = freshGame()
|
|
local list, seen = chooseOnce(game, "TOWN_MAP")
|
|
if check(list ~= nil, "the bag opened on the TOWN MAP") then
|
|
check(sawMenu(seen),
|
|
"another key item still gets the box: pokered special-cases the "
|
|
.. "BICYCLE by id, not key items as a class")
|
|
end
|
|
end
|
|
|
|
-- the box the bike no longer opens is the only route to TOSS, so the bike
|
|
-- cannot be thrown away from the bag at all
|
|
do
|
|
local game = freshGame()
|
|
local list, seen = chooseOnce(game, "BICYCLE")
|
|
if check(list ~= nil, "the bag opened on the BICYCLE row") then
|
|
local rows = {}
|
|
for _, s in ipairs(seen or {}) do
|
|
for _, it in ipairs((isMenu(s) and s.items) or {}) do
|
|
rows[#rows + 1] = tostring(it.label)
|
|
end
|
|
end
|
|
eq(#rows, 0, "the press offered no menu rows at all, TOSS included")
|
|
eq(game.save.inventory.BICYCLE, 1, "and the bike survives the press")
|
|
end
|
|
end
|
|
|
|
package.loaded["src.core.Sound"] = nil
|
|
package.loaded["src.core.Music"] = nil
|
|
package.loaded["src.render.TextBox"] = nil
|
|
package.loaded["src.ui.BagMenu"] = nil
|
|
require("src.ui.Screens").invalidate()
|
|
|
|
T.finish()
|