Files
gen1recomp/tests/engine/vending_machine_bug1876.lua
T

284 lines
9.8 KiB
Lua

-- engine/events/vending_machine.asm (#1876)
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")
local realFont = package.loaded["src.render.Font"]
local realTextBox = package.loaded["src.render.TextBox"]
local calls = {}
package.loaded["src.render.Font"] = {
BORDER = { tl = 1, tr = 2, bl = 3, br = 4, h = 5, v = 6 },
draw = function(text, x, y) calls[#calls + 1] = { "draw", text, x, y } end,
drawCode = function(code, x, y) calls[#calls + 1] = { "code", code, x, y } end,
drawBox = function(tx, ty, tw, th) calls[#calls + 1] = { "box", tx, ty, tw, th } end,
width = function(text) return #tostring(text) * 8 end,
split = function(text)
local out = {}
for i = 1, #tostring(text) do out[i] = i end
return out
end,
}
package.loaded["src.render.TextBox"] = {
new = function(_, text, onDone, opts)
return { text = text, onDone = onDone, opts = opts, isTextBox = true,
update = function() end }
end,
}
local sounds = {}
package.loaded["src.core.Sound"] = {
play = function(_, name) sounds[#sounds + 1] = name end,
}
local bagFull = false
local bagged = {}
package.loaded["src.inventory.Bag"] = {
add = function(_, id)
if bagFull then return false end
bagged[#bagged + 1] = id
return true
end,
}
package.loaded["src.ui.Menu"] = nil
package.loaded["src.ui.Theme"] = nil
local M = assert(loadfile("data/scripts/story4.lua"))()
local machine = M.CELADON_MART_ROOF.talk.TEXT_CELADONMARTROOF_VENDING_MACHINE1
local function found(kind, pred)
for _, c in ipairs(calls) do
if c[1] == kind and pred(c) then return c end
end
return nil
end
local pressed, done
local function mkGame(cash)
sounds, bagged, done = {}, {}, false
return {
data = {
text = {
_VendingMachineText1 = "A vending machine!\nHere's the menu!",
_VendingMachineText4 = "Oops, not enough\nmoney!",
_VendingMachineText5 = "{RAM:wStringBuffer}\npopped out!",
_VendingMachineText6 = "There's no more\nroom for stuff!",
_VendingMachineText7 = "Not thirsty!",
},
items = {
FRESH_WATER = { name = "FRESH WATER" },
SODA_POP = { name = "SODA POP" },
LEMONADE = { name = "LEMONADE" },
},
},
save = { money = cash, inventory = {} },
input = {
wasPressed = function(_, b) return pressed == b end,
isDown = function() return false end,
},
stack = {
states = {},
push = function(self, s) self.states[#self.states + 1] = s end,
pop = function(self) return table.remove(self.states) end,
top = function(self) return self.states[#self.states] end,
},
}
end
local function open(cash)
local game = mkGame(cash)
machine(game, nil, nil, function() done = true end)
local intro = game.stack:top()
return game, intro
end
do
local game, intro = open(3000)
check(intro.isTextBox, "talking to the sign prints VendingMachineText1")
check(tostring(intro.text):find("vending machine", 1, true) ~= nil,
"with the machine's own greeting")
check(intro.opts.money ~= nil, "DisplayTextBoxID MONEY_BOX rides along")
check(intro.opts.moneyOnShown == true,
"but only after the greeting's prompt press (vending_machine.asm:4-6)")
check(intro.opts.stay ~= nil and intro.opts.stay.prompt,
"the greeting prompts, then stays up under the list")
intro.opts.stay.onShown()
local menu = game.stack:top()
check(menu ~= intro and menu.items ~= nil, "the drink list opens over it")
eq(menu.tx, 0, "TextBoxBorder at hlcoord 0,3")
eq(menu.ty, 3, "row 3")
eq(menu.tw, 14, "14 tiles wide (b=8, c=12)")
eq(menu.th, 10, "10 tiles tall")
eq(#menu.items, 4, "wMaxMenuItem 3: three drinks and CANCEL")
eq(menu.items[4].label, "CANCEL", "CANCEL is the last row")
eq(menu.noWrap, true,
"wMenuWrappingEnabled is never set (home/window.asm:56-83)")
calls = {}
menu:draw()
for i, name in ipairs({ "FRESH WATER", "SODA POP", "LEMONADE", "CANCEL" }) do
local y = (5 + (i - 1) * 2) * 8
check(found("draw", function(c)
return c[2] == name and c[3] == 16 and c[4] == y
end) ~= nil, name .. " sits at (16, " .. y .. ")")
end
for i, price in ipairs({ "¥200", "¥300", "¥350" }) do
local y = (6 + (i - 1) * 2) * 8
check(found("draw", function(c)
return c[2] == price and c[3] == 72 and c[4] == y
end) ~= nil, price .. " sits a row under its drink at (72, " .. y .. ")")
end
end
do
local game, intro = open(3000)
intro.opts.stay.onShown()
local menu = game.stack:top()
menu.items[1].onSelect()
local waiter = game.stack:top()
check(waiter ~= menu and waiter.update ~= nil,
"the delivery rumble runs before the text")
eq(game.save.money, 3000, "money is still untouched during the rumble")
for _ = 1, 120 do
if game.stack:top() ~= waiter then break end
waiter:update(1 / 60)
end
eq(#sounds, 60, "SFX_PUSH_BOULDER restarts 60 times")
eq(sounds[1], "Push_Boulder", "and it is the boulder push, not Cut")
check(bagged[1] == "FRESH_WATER", "the drink lands in the bag")
local result = game.stack:top()
check(result.isTextBox and tostring(result.text):find("popped out") ~= nil,
"then VendingMachineText5 names the drink")
check(tostring(result.text):find("FRESH WATER", 1, true) ~= nil,
"out of wStringBuffer")
eq(game.save.money, 3000, "nothing is subtracted while the text types")
eq(#game.stack.states, 3,
"the greeting and the drink list stay up under the result text")
check(game.stack.states[1] == intro and game.stack.states[2] == menu,
"greeting, then menu, then the result box")
result:update(1 / 60)
eq(game.save.money, 3000, "still untouched before typing ends")
result.done = true
result:update(1 / 60)
eq(game.save.money, 2800,
"SubBCDPredef runs once PrintText returns (vending_machine.asm:67-72)")
result:update(1 / 60)
eq(game.save.money, 2800, "and only once")
check(not done, "the script still waits for the last press")
game.stack:pop()
result.onDone()
eq(#game.stack.states, 0, "the last press closes all three together")
check(done, "dismissing it hands control back to the script")
end
do
local game, intro = open(3000)
intro.opts.stay.onShown()
local menu = game.stack:top()
menu.index = 4
pressed = "a"
menu:update(1 / 60)
pressed = nil
local result = game.stack:top()
check(result.isTextBox and tostring(result.text):find("thirsty") ~= nil,
"CANCEL prints VendingMachineText7")
eq(#game.stack.states, 3, "over the list and the greeting, both still up")
check(game.stack.states[2] == menu, "the list did not close itself")
eq(#bagged, 0, "and nothing bought")
game.stack:pop()
result.onDone()
eq(#game.stack.states, 0, "everything closes on the last press")
game, intro = open(3000)
intro.opts.stay.onShown()
menu = game.stack:top()
pressed = "b"
menu:update(1 / 60)
pressed = nil
check(tostring(game.stack:top().text):find("thirsty") ~= nil,
"B does the same (.notThirsty)")
eq(#game.stack.states, 3, "B leaves the list up under Not thirsty!")
check(game.stack.states[2] == menu, "the same list")
game, intro = open(3000)
intro.opts.stay.onShown()
menu = game.stack:top()
menu.items[4].onSelect()
result = game.stack:top()
game.stack:pop()
game.stack:pop()
game.stack:pop()
local stranger = { isTextBox = true, text = "someone else's box" }
game.stack:push(stranger)
result.onDone()
eq(#game.stack.states, 1, "the last press never pops a state it did not push")
check(game.stack:top() == stranger, "a box pushed by someone else stays up")
check(done, "and the script still hands control back")
end
do
local game, intro = open(100)
intro.opts.stay.onShown()
game.stack:top().items[1].onSelect()
local result = game.stack:top()
check(tostring(result.text):find("enough", 1, true) ~= nil,
"no money prints VendingMachineText4")
eq(game.save.money, 100, "and spends nothing")
eq(#bagged, 0, "and hands over no drink")
eq(#game.stack.states, 3, "with the list still drawn under it")
result.done = true
result:update(1 / 60)
eq(game.save.money, 100, "even once it finishes typing")
bagFull = true
game, intro = open(3000)
intro.opts.stay.onShown()
game.stack:top().items[1].onSelect()
result = game.stack:top()
check(tostring(result.text):find("room for stuff", 1, true) ~= nil,
"a full bag prints VendingMachineText6")
eq(game.save.money, 3000, "with the money untouched (.BagFull)")
eq(#game.stack.states, 3, "and the list still drawn under it")
bagFull = false
end
package.loaded["src.render.Font"] = realFont
package.loaded["src.render.TextBox"] = realTextBox
do
local TextBox = require("src.render.TextBox")
local game = mkGame(3000)
game.save.options = {}
local box = TextBox.new(game, "A vending machine!\nHere's the menu!", nil, {
money = function() return game.save.money end,
moneyOnShown = true,
stay = { prompt = true },
})
check(not box:moneyVisible(), "no money box on the greeting's first frame")
local early = false
for _ = 1, 600 do
if box.done then break end
box:update(1 / 60)
early = early or box:moneyVisible()
end
check(not early, "no money box while the greeting types")
check(box.done, "the greeting finished typing")
box:update(1 / 60)
check(not box:moneyVisible(), "no money box under the prompt arrow")
pressed = "a"
box:update(1 / 60)
pressed = nil
check(box.stayShown, "the prompt press is taken")
check(box:moneyVisible(), "the money box goes up with the press")
local plain = TextBox.new(game, "hi", nil, { money = function() return 1 end })
check(plain:moneyVisible(), "money without moneyOnShown still draws at once")
end
package.loaded["src.core.Sound"] = nil
package.loaded["src.inventory.Bag"] = nil
package.loaded["src.ui.Menu"] = nil
package.loaded["src.ui.Theme"] = nil
require("src.ui.Screens").invalidate()
T.finish()