mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-07 17:31:35 -04:00
CLOSES #2649, CLOSES #2650, CLOSES #2652, CLOSES #2653, CLOSES #2654, CLOSES #2657, CLOSES #2658, CLOSES #2659, CLOSES #2660, CLOSES #2662, CLOSES #2664, CLOSES #2665, CLOSES #2666, CLOSES #2669, CLOSES #2671, CLOSES #2672, CLOSES #2674, CLOSES #2676, CLOSES #2682, CLOSES #2683, CLOSES #2684, CLOSES #2685, CLOSES #2686, CLOSES #2687, CLOSES #2688, CLOSES #2689
CLOSES #2649, CLOSES #2650, CLOSES #2652, CLOSES #2653, CLOSES #2654, CLOSES #2657, CLOSES #2658, CLOSES #2659, CLOSES #2660, CLOSES #2662, CLOSES #2664, CLOSES #2665, CLOSES #2666, CLOSES #2669, CLOSES #2671, CLOSES #2672, CLOSES #2674, CLOSES #2676, CLOSES #2682, CLOSES #2683, CLOSES #2684, CLOSES #2685, CLOSES #2686, CLOSES #2687, CLOSES #2688, CLOSES #2689
This commit is contained in:
@@ -0,0 +1,315 @@
|
||||
-- engine/items/inventory.asm:59
|
||||
-- engine/events/pokemart.asm:152
|
||||
|
||||
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 function glyphs(text)
|
||||
local n = 0
|
||||
for _ in tostring(text):gmatch("[%z\1-\127\194-\244][\128-\191]*") do
|
||||
n = n + 1
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
local realFont = package.loaded["src.render.Font"]
|
||||
package.loaded["src.render.Font"] = {
|
||||
BORDER = { tl = 1, tr = 2, bl = 3, br = 4, h = 5, v = 6 },
|
||||
draw = function() end,
|
||||
drawCode = function() end,
|
||||
drawBox = function() end,
|
||||
width = function(text) return glyphs(text) * 8 end,
|
||||
split = function(text)
|
||||
local out = {}
|
||||
for s in tostring(text):gmatch("[%z\1-\127\194-\244][\128-\191]*") do
|
||||
out[#out + 1] = s
|
||||
end
|
||||
return out
|
||||
end,
|
||||
spansFitting = function(spans, pixels)
|
||||
return math.min(#spans, math.floor(pixels / 8))
|
||||
end,
|
||||
encode = function(text) return { text } end,
|
||||
advanceOf = function() return 8 end,
|
||||
}
|
||||
|
||||
local RELOAD = {
|
||||
"src.ui.QuantityBox", "src.ui.ShopMenu", "src.ui.ListMenu",
|
||||
"src.ui.ChoiceBox", "src.ui.Menu", "src.ui.Theme", "src.render.TextBox",
|
||||
}
|
||||
for _, m in ipairs(RELOAD) do package.loaded[m] = nil end
|
||||
|
||||
local GameVersion = require("src.core.GameVersion")
|
||||
local Bag = require("src.inventory.Bag")
|
||||
local ShopMenu = require("src.ui.ShopMenu")
|
||||
local TextBox = require("src.render.TextBox")
|
||||
local SaveData = require("src.core.SaveData")
|
||||
|
||||
local BAG_FULL = "You can't carry\nany more items."
|
||||
local BOUGHT = "Here you are!\nThank you!"
|
||||
|
||||
local function boxText(box)
|
||||
local out = {}
|
||||
for _, page in ipairs(box.pages or {}) do
|
||||
for _, line in ipairs(page) do out[#out + 1] = line end
|
||||
end
|
||||
return table.concat(out, " ")
|
||||
end
|
||||
|
||||
local function rowsOf(save, data)
|
||||
local out = {}
|
||||
for _, r in ipairs(Bag.rows(save, data)) do
|
||||
out[#out + 1] = r.id .. "x" .. tostring(r.count)
|
||||
end
|
||||
return table.concat(out, ",")
|
||||
end
|
||||
|
||||
local function newGame(money, inventory, order)
|
||||
local stack = {}
|
||||
local game
|
||||
game = {
|
||||
data = {
|
||||
items = {
|
||||
X_ACCURACY = { name = "X ACCURACY", price = 950, index = 46 },
|
||||
POTION = { name = "POTION", price = 300, index = 20 },
|
||||
},
|
||||
text = {
|
||||
_PokemartItemBagFullText = BAG_FULL,
|
||||
_PokemartBoughtItemText = BOUGHT,
|
||||
},
|
||||
constants = {},
|
||||
field = {},
|
||||
},
|
||||
save = { money = money, inventory = inventory, bagOrder = order },
|
||||
input = {
|
||||
wasPressed = function() return false end,
|
||||
isDown = function() return false end,
|
||||
},
|
||||
}
|
||||
game.stack = {
|
||||
push = function(_, s) stack[#stack + 1] = s end,
|
||||
pop = function() return table.remove(stack) end,
|
||||
top = function() return stack[#stack] end,
|
||||
}
|
||||
return game, stack
|
||||
end
|
||||
|
||||
local function drain(game)
|
||||
for _ = 1, 600 do
|
||||
local top = game.stack:top()
|
||||
if not (top and top.stay and not top.stayShown) then return end
|
||||
top:update(1 / 60)
|
||||
end
|
||||
end
|
||||
|
||||
local function fill(game, n)
|
||||
for i = 1, n do
|
||||
local id = "FILLER_" .. i
|
||||
game.save.inventory[id] = 1
|
||||
game.save.bagOrder[#game.save.bagOrder + 1] = id
|
||||
end
|
||||
end
|
||||
|
||||
local function buy(game, stack, id, qty)
|
||||
local menu = ShopMenu.new(game, { id }, function() end)
|
||||
menu.items[1].onSelect()
|
||||
drain(game)
|
||||
local list = stack[#stack]
|
||||
list.onChoose({ value = id })
|
||||
local qtyBox = stack[#stack]
|
||||
qtyBox.onDone(qty)
|
||||
local confirm = stack[#stack]
|
||||
game.stack:pop()
|
||||
confirm.choice(true)
|
||||
return stack[#stack]
|
||||
end
|
||||
|
||||
GameVersion.set("red")
|
||||
|
||||
do
|
||||
local game = newGame(0, { X_ACCURACY = 2, POTION = 5 }, { "X_ACCURACY", "POTION" })
|
||||
eq(Bag.add(game.save, "X_ACCURACY", 99, game.data), true,
|
||||
"2 + 99 X ACCURACY fits with a free slot")
|
||||
eq(game.save.inventory.X_ACCURACY, 101, "the total is 101")
|
||||
eq(rowsOf(game.save, game.data), "X_ACCURACYx99,POTIONx5,X_ACCURACYx2",
|
||||
"the first slot maxes at 99 and the overflow lands at the end")
|
||||
eq(Bag.slots(game.save, game.data), 3, "three bag slots")
|
||||
|
||||
Bag.remove(game.save, "X_ACCURACY", 2, game.data, 2)
|
||||
eq(rowsOf(game.save, game.data), "X_ACCURACYx99,POTIONx5",
|
||||
"tossing the overflow row removes that slot")
|
||||
|
||||
Bag.add(game.save, "X_ACCURACY", 2, game.data)
|
||||
Bag.remove(game.save, "X_ACCURACY", 99, game.data, 1)
|
||||
eq(rowsOf(game.save, game.data), "POTIONx5,X_ACCURACYx2",
|
||||
"emptying the first slot keeps the later slot where it was")
|
||||
end
|
||||
|
||||
do
|
||||
local game = newGame(0, { X_ACCURACY = 99 }, { "X_ACCURACY" })
|
||||
fill(game, 19)
|
||||
eq(Bag.add(game.save, "X_ACCURACY", 1, game.data), false,
|
||||
"a 20-slot bag refuses the overflow slot")
|
||||
eq(game.save.inventory.X_ACCURACY, 99, "and changes nothing")
|
||||
eq(#game.save.bagOrder, 20, "no slot is added")
|
||||
end
|
||||
|
||||
do
|
||||
local game = newGame(0, { X_ACCURACY = 149 }, { "X_ACCURACY", "X_ACCURACY" })
|
||||
fill(game, 18)
|
||||
eq(Bag.add(game.save, "X_ACCURACY", 10, game.data), false,
|
||||
"a full bag refuses once the maxed first slot overflows")
|
||||
eq(game.save.inventory.X_ACCURACY, 149, "and changes nothing")
|
||||
end
|
||||
|
||||
do
|
||||
local game, stack = newGame(104276, { X_ACCURACY = 2 }, { "X_ACCURACY" })
|
||||
local box = buy(game, stack, "X_ACCURACY", 99)
|
||||
if check(getmetatable(box) == TextBox, "mart: a text box follows the YES") then
|
||||
check(boxText(box):find("Here you are!", 1, true) ~= nil,
|
||||
"mart: the clerk hands the items over")
|
||||
check(boxText(box):find("can't carry", 1, true) == nil,
|
||||
"mart: no bag-full refusal")
|
||||
end
|
||||
eq(game.save.money, 104276 - 94050, "mart: 94050 is paid")
|
||||
eq(rowsOf(game.save, game.data), "X_ACCURACYx99,X_ACCURACYx2",
|
||||
"mart: two X ACCURACY rows")
|
||||
end
|
||||
|
||||
do
|
||||
local game, stack = newGame(3000, { X_ACCURACY = 99 }, { "X_ACCURACY" })
|
||||
fill(game, 19)
|
||||
local box = buy(game, stack, "X_ACCURACY", 1)
|
||||
if check(getmetatable(box) == TextBox, "mart full: a text box follows the YES") then
|
||||
check(boxText(box):find("can't carry", 1, true) ~= nil,
|
||||
"mart full: the bag-full refusal")
|
||||
end
|
||||
eq(game.save.money, 3000, "mart full: no money changes hands")
|
||||
end
|
||||
|
||||
do
|
||||
local game, stack = newGame(0, { X_ACCURACY = 101, POTION = 5 },
|
||||
{ "X_ACCURACY", "POTION", "X_ACCURACY" })
|
||||
local menu = ShopMenu.new(game, {}, function() end)
|
||||
menu.items[2].onSelect()
|
||||
drain(game)
|
||||
local list = stack[#stack]
|
||||
local counts = {}
|
||||
for _, it in ipairs(list.items) do
|
||||
if not it.cancel then counts[#counts + 1] = tostring(it.count) end
|
||||
end
|
||||
eq(table.concat(counts, ","), "99,5,2", "sell list shows each slot's own count")
|
||||
list.index = 3
|
||||
list.onChoose(list.items[3])
|
||||
local qtyBox = stack[#stack]
|
||||
eq(qtyBox.max, 2, "selling from the overflow row caps at that row")
|
||||
qtyBox.onDone(2)
|
||||
local confirm = stack[#stack]
|
||||
game.stack:pop()
|
||||
confirm.choice(true)
|
||||
eq(rowsOf(game.save, game.data), "X_ACCURACYx99,POTIONx5", "the sold row is gone")
|
||||
eq(game.save.money, 950, "paid half price for two")
|
||||
end
|
||||
|
||||
do
|
||||
local game = newGame(0, { X_ACCURACY = 101, POTION = 5 },
|
||||
{ "X_ACCURACY", "POTION", "X_ACCURACY" })
|
||||
local back = assert(SaveData.decode(SaveData.encode(game.save)))
|
||||
SaveData.validate(back, game.data)
|
||||
eq(rowsOf(back, game.data), "X_ACCURACYx99,POTIONx5,X_ACCURACYx2",
|
||||
"split slots survive a save round trip")
|
||||
end
|
||||
|
||||
do
|
||||
local pc, order = { POTION = 99 }, { "POTION" }
|
||||
local data = newGame(0, {}, {}).data
|
||||
eq(Bag.addTo(pc, order, "POTION", 50, data, Bag.storeSlots(pc, data), 50), true,
|
||||
"PC deposit past 99 splits the box slot")
|
||||
eq(table.concat(order, ","), "POTION,POTION", "two box slots")
|
||||
local full, fullOrder = { POTION = 1 }, { "POTION" }
|
||||
for i = 1, 49 do
|
||||
full["FILLER_" .. i] = 1
|
||||
fullOrder[#fullOrder + 1] = "FILLER_" .. i
|
||||
end
|
||||
full.POTION = 99
|
||||
eq(Bag.addTo(full, fullOrder, "POTION", 1, data, Bag.storeSlots(full, data), 50), false,
|
||||
"a 50-slot box refuses the overflow slot")
|
||||
end
|
||||
|
||||
do
|
||||
local game = newGame(0, { X_ACCURACY = 101, POTION = 5 },
|
||||
{ "X_ACCURACY", "POTION", "X_ACCURACY" })
|
||||
Bag.remove(game.save, "X_ACCURACY", 5, game.data, 1)
|
||||
eq(rowsOf(game.save, game.data), "X_ACCURACYx94,POTIONx5,X_ACCURACYx2",
|
||||
"tossing from the first slot leaves 94 and 2")
|
||||
eq(Bag.slots(game.save, game.data), 3, "both X ACCURACY slots stay")
|
||||
local back = assert(SaveData.decode(SaveData.encode(game.save)))
|
||||
SaveData.validate(back, game.data)
|
||||
eq(rowsOf(back, game.data), "X_ACCURACYx94,POTIONx5,X_ACCURACYx2",
|
||||
"a 94/2 split survives a save round trip")
|
||||
Bag.remove(game.save, "X_ACCURACY", 1, game.data)
|
||||
eq(rowsOf(game.save, game.data), "X_ACCURACYx93,POTIONx5,X_ACCURACYx2",
|
||||
"a slotless remove takes from the first slot")
|
||||
Bag.add(game.save, "X_ACCURACY", 10, game.data)
|
||||
eq(rowsOf(game.save, game.data), "X_ACCURACYx99,POTIONx5,X_ACCURACYx6",
|
||||
"adding tops the first slot up and carries the rest to the next")
|
||||
Bag.swap(game.save, 1, 3, game.data)
|
||||
eq(rowsOf(game.save, game.data), "X_ACCURACYx6,POTIONx5,X_ACCURACYx99",
|
||||
"SELECT on two slots of one item moves the 99 to the second pick")
|
||||
Bag.swap(game.save, 1, 2, game.data)
|
||||
eq(rowsOf(game.save, game.data), "POTIONx5,X_ACCURACYx6,X_ACCURACYx99",
|
||||
"swapping different items keeps each slot's count")
|
||||
Bag.swap(game.save, 3, 2, game.data)
|
||||
eq(rowsOf(game.save, game.data), "POTIONx5,X_ACCURACYx99,X_ACCURACYx6",
|
||||
"the 99 follows the second pick when the sum exceeds 99")
|
||||
Bag.remove(game.save, "X_ACCURACY", 90, game.data, 2)
|
||||
Bag.swap(game.save, 2, 3, game.data)
|
||||
eq(rowsOf(game.save, game.data), "POTIONx5,X_ACCURACYx15",
|
||||
"SELECT merges two slots that fit in one")
|
||||
eq(game.save.bagStacks, nil, "a merged stack carries no per-slot list")
|
||||
end
|
||||
|
||||
do
|
||||
local game, stack = newGame(0, { X_ACCURACY = 101, POTION = 5 },
|
||||
{ "X_ACCURACY", "POTION", "X_ACCURACY" })
|
||||
local menu = ShopMenu.new(game, {}, function() end)
|
||||
menu.items[2].onSelect()
|
||||
drain(game)
|
||||
local list = stack[#stack]
|
||||
local function sellRow(row, qty)
|
||||
list.index = row
|
||||
list.onChoose(list.items[row])
|
||||
local qtyBox = stack[#stack]
|
||||
qtyBox.onDone(qty)
|
||||
local confirm = stack[#stack]
|
||||
game.stack:pop()
|
||||
confirm.choice(true)
|
||||
end
|
||||
list.scroll = 1
|
||||
sellRow(3, 1)
|
||||
eq(list.scroll, 1, "selling part of a row keeps the list scroll")
|
||||
eq(list.index, 3, "and the cursor")
|
||||
sellRow(3, 1)
|
||||
eq(rowsOf(game.save, game.data), "X_ACCURACYx99,POTIONx5", "the emptied row is gone")
|
||||
eq(list.scroll, 0, "an emptied row resets the sell list scroll")
|
||||
eq(list.index, 1, "and puts the cursor on the first row")
|
||||
end
|
||||
|
||||
do
|
||||
GameVersion.set("gold")
|
||||
local game = newGame(0, { POTION = 99 }, { "POTION" })
|
||||
game.data.items.POTION.pocket = "ITEM"
|
||||
eq(Bag.add(game.save, "POTION", 1, game.data), false,
|
||||
"Gen 2 pockets keep one row per id")
|
||||
eq(#game.save.bagOrder, 1, "no duplicate row in Gen 2")
|
||||
GameVersion.set("red")
|
||||
end
|
||||
|
||||
package.loaded["src.render.Font"] = realFont
|
||||
for _, m in ipairs(RELOAD) do package.loaded[m] = nil end
|
||||
require("src.ui.Screens").invalidate()
|
||||
|
||||
T.finish()
|
||||
Reference in New Issue
Block a user