mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
fix(game3): start player item PC with single Potion
This commit is contained in:
@@ -61,7 +61,7 @@ function Bridge.enterFromHost(mod, game, opts)
|
||||
healY = sc.healY or 5,
|
||||
move_overlay = sc.move_overlay or {},
|
||||
options = sc.options or {},
|
||||
storage = require("src.core.game3.storage").restore(sc.storage, sc.pc),
|
||||
storage = require("src.core.game3.storage").restore(sc.storage, sc.pc, sc.pcItems or save.pcItems or save.pc_items),
|
||||
enteredAt = os.time(),
|
||||
}
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ function Schema.fromSaveTable(save)
|
||||
vars = save.vars or {},
|
||||
playtime = save.playTime or save.playtime or { hours = 0, minutes = 0, seconds = 0 },
|
||||
options = save.options,
|
||||
storage = require("src.core.game3.storage").restore(save.storage, save.pc),
|
||||
storage = require("src.core.game3.storage").restore(save.storage, save.pc, save.pcItems or save.pc_items),
|
||||
registeredItem = save.registeredItem,
|
||||
move_overlay = save.move_overlay or {},
|
||||
trainerId = save.trainerId,
|
||||
|
||||
@@ -39,7 +39,7 @@ function Storage.new()
|
||||
local storage = {
|
||||
currentBox = 1,
|
||||
boxes = {},
|
||||
items = {}, -- 50-slot Player PC item storage
|
||||
items = { { id = 13, qty = 1 } }, -- 50-slot Player PC item storage (starts with 1 POTION, pokefirered/src/player_pc.c:100)
|
||||
}
|
||||
for b = 1, Storage.TOTAL_BOXES_COUNT do
|
||||
storage.boxes[b] = {
|
||||
@@ -60,7 +60,7 @@ function Storage.ensure(session)
|
||||
if not session.storage.boxes or #session.storage.boxes < Storage.TOTAL_BOXES_COUNT then
|
||||
local fresh = Storage.new()
|
||||
fresh.currentBox = session.storage.currentBox or 1
|
||||
fresh.items = session.storage.items or {}
|
||||
fresh.items = session.storage.items or fresh.items
|
||||
for b = 1, Storage.TOTAL_BOXES_COUNT do
|
||||
if session.storage.boxes and session.storage.boxes[b] then
|
||||
fresh.boxes[b] = session.storage.boxes[b]
|
||||
@@ -69,7 +69,7 @@ function Storage.ensure(session)
|
||||
session.storage = fresh
|
||||
end
|
||||
if not session.storage.items then
|
||||
session.storage.items = {}
|
||||
session.storage.items = { { id = 13, qty = 1 } }
|
||||
end
|
||||
return session.storage
|
||||
end
|
||||
@@ -436,10 +436,12 @@ function Storage.deserialize(data)
|
||||
local storage = Storage.new()
|
||||
if not data then return storage end
|
||||
storage.currentBox = tonumber(data.currentBox) or 1
|
||||
storage.items = {}
|
||||
for _, item in ipairs(data.items or {}) do
|
||||
if item and item.id and (tonumber(item.qty) or 0) > 0 then
|
||||
storage.items[#storage.items + 1] = { id = item.id, qty = item.qty }
|
||||
if data.items ~= nil then
|
||||
storage.items = {}
|
||||
for _, item in ipairs(data.items) do
|
||||
if item and item.id and (tonumber(item.qty) or 0) > 0 then
|
||||
storage.items[#storage.items + 1] = { id = item.id, qty = item.qty }
|
||||
end
|
||||
end
|
||||
end
|
||||
for b = 1, Storage.TOTAL_BOXES_COUNT do
|
||||
@@ -459,13 +461,17 @@ function Storage.deserialize(data)
|
||||
return storage
|
||||
end
|
||||
|
||||
function Storage.restore(data, legacyPc)
|
||||
function Storage.restore(data, legacyPc, pcItems)
|
||||
local hasData = type(data) == "table"
|
||||
local hasPc = type(legacyPc) == "table"
|
||||
if not hasData and not hasPc then return nil end
|
||||
local hasPcItems = type(pcItems) == "table"
|
||||
if not hasData and not hasPc and not hasPcItems then
|
||||
return Storage.new()
|
||||
end
|
||||
local storage = Storage.deserialize(hasData and data or nil)
|
||||
if hasPc then
|
||||
if not hasData then
|
||||
storage.items = {}
|
||||
for _, it in ipairs(legacyPc.items or {}) do
|
||||
local id = type(it) == "table" and tonumber(it.id or it.itemId)
|
||||
local qty = type(it) == "table" and (tonumber(it.qty or it.quantity) or 0) or 0
|
||||
@@ -481,6 +487,25 @@ function Storage.restore(data, legacyPc)
|
||||
storage.boxes[b].mons[s] = mon
|
||||
end
|
||||
end
|
||||
elseif not hasData and hasPcItems then
|
||||
storage.items = {}
|
||||
for k, v in pairs(pcItems) do
|
||||
local id = nil
|
||||
local qty = 0
|
||||
if type(k) == "number" and type(v) == "table" then
|
||||
id = tonumber(v.id or v.itemId)
|
||||
qty = tonumber(v.qty or v.quantity or v.count) or 0
|
||||
elseif type(k) == "string" and type(v) == "number" then
|
||||
id = ItemsData.toNumericId(k)
|
||||
qty = v
|
||||
elseif type(k) == "number" and type(v) == "number" then
|
||||
id = k
|
||||
qty = v
|
||||
end
|
||||
if id and qty > 0 and #storage.items < Storage.PC_ITEMS_COUNT then
|
||||
storage.items[#storage.items + 1] = { id = id, qty = math.min(Storage.MAX_ITEM_QTY, qty) }
|
||||
end
|
||||
end
|
||||
end
|
||||
return storage
|
||||
end
|
||||
|
||||
@@ -124,6 +124,7 @@ function SummaryMenu.openMenu(party, startIndex, opts)
|
||||
opts = opts or {}
|
||||
SummaryMenu.open = true
|
||||
SummaryMenu._party = party or {}
|
||||
SummaryMenu._cursor = startIndex or 1
|
||||
local session = opts.playerState or opts.session
|
||||
or (package.loaded["src.core.game3.runtime"] and package.loaded["src.core.game3.runtime"].getSession and package.loaded["src.core.game3.runtime"].getSession())
|
||||
SummaryMenu._playerState = session
|
||||
|
||||
@@ -136,7 +136,7 @@ local restored = Schema.fromSaveTable(saved)
|
||||
check(restored.storage and restored.storage.items[1] and restored.storage.items[1].id == 13,
|
||||
"fromSaveTable restores storage")
|
||||
local legacy = Schema.fromSaveTable({ map = "FR_PALLET_TOWN", x = 1, y = 1 })
|
||||
check(legacy.storage == nil, "legacy save without storage gets no seeded POTION")
|
||||
check(legacy.storage and legacy.storage.items[1] and legacy.storage.items[1].id == 13, "legacy save without storage gets seeded POTION")
|
||||
|
||||
check(Storage.withdrawItem(session, 1, 1) == true, "withdrawItem POTION ok")
|
||||
check(Bag.has(session.bag, 13, 1), "bag has POTION")
|
||||
@@ -156,6 +156,7 @@ check(joined(PcMenu.ITEM_STORAGE_ACTIONS) == "WITHDRAW ITEM/DEPOSIT ITEM/CANCEL"
|
||||
local seLog = {}
|
||||
package.loaded["src.core.game3.audio"].playSe = function(id) seLog[#seLog + 1] = id end
|
||||
local s6 = { bag = Bag.new(), storage = Storage.new() }
|
||||
s6.storage.items = {} -- clear to test empty WITHDRAW message
|
||||
local closed6 = 0
|
||||
PcMenu.show({ session = s6, startMode = "player_pc", closeOnExit = true,
|
||||
onClose = function() closed6 = closed6 + 1 end })
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env luajit
|
||||
-- Test: Game 3 Player Item PC starting Potion fidelity (pokefirered player_pc.c:100-101)
|
||||
|
||||
local Storage = require("src.core.game3.storage")
|
||||
local Schema = require("src.core.game3.save_schema_firered")
|
||||
local Bag = require("src.core.game3.bag")
|
||||
local Bridge = require("src.core.game3.bridge")
|
||||
|
||||
local failures = 0
|
||||
local function check(cond, msg)
|
||||
if cond then
|
||||
print("[PASS] " .. msg)
|
||||
else
|
||||
failures = failures + 1
|
||||
print("[FAIL] " .. msg)
|
||||
end
|
||||
end
|
||||
|
||||
print("=== [TEST 1] Storage.new() starts with 1 Potion ===")
|
||||
local s = Storage.new()
|
||||
check(#s.items == 1, "Storage.new has 1 PC item")
|
||||
check(s.items[1].id == 13 and s.items[1].qty == 1, "Storage.new item is POTION x1 (id 13)")
|
||||
|
||||
print("=== [TEST 2] Storage.ensure() seeds 1 Potion for fresh sessions ===")
|
||||
local sess1 = {}
|
||||
Storage.ensure(sess1)
|
||||
check(sess1.storage and #sess1.storage.items == 1, "Storage.ensure populated storage.items")
|
||||
check(sess1.storage.items[1].id == 13 and sess1.storage.items[1].qty == 1, "Storage.ensure item is POTION x1")
|
||||
|
||||
local sess2 = { storage = { currentBox = 1, boxes = {} } }
|
||||
Storage.ensure(sess2)
|
||||
check(sess2.storage and #sess2.storage.items == 1, "Storage.ensure populated missing items list")
|
||||
check(sess2.storage.items[1].id == 13 and sess2.storage.items[1].qty == 1, "Storage.ensure item is POTION x1")
|
||||
|
||||
print("=== [TEST 3] Storage.restore() handles fresh, legacy, and pcItems ===")
|
||||
local rNil = Storage.restore(nil, nil, nil)
|
||||
check(rNil and #rNil.items == 1 and rNil.items[1].id == 13, "Storage.restore(nil, nil, nil) has POTION x1")
|
||||
|
||||
local rMap = Storage.restore(nil, nil, { POTION = 1 })
|
||||
check(rMap and #rMap.items == 1 and rMap.items[1].id == 13 and rMap.items[1].qty == 1, "Storage.restore from host pcItems={POTION=1}")
|
||||
|
||||
local rIdMap = Storage.restore(nil, nil, { [13] = 1 })
|
||||
check(rIdMap and #rIdMap.items == 1 and rIdMap.items[1].id == 13 and rIdMap.items[1].qty == 1, "Storage.restore from id pcItems={[13]=1}")
|
||||
|
||||
print("=== [TEST 4] Schema.newGame() starts with 1 Potion ===")
|
||||
local ng = Schema.newGame({ rngSeed = 100 })
|
||||
check(ng.storage and #ng.storage.items == 1, "Schema.newGame has 1 PC item")
|
||||
check(ng.storage.items[1].id == 13 and ng.storage.items[1].qty == 1, "Schema.newGame PC item is POTION x1")
|
||||
|
||||
print("=== [TEST 5] Schema.fromSaveTable() round-trip and legacy migration ===")
|
||||
local saved = Schema.toSaveTable(ng)
|
||||
local loaded = Schema.fromSaveTable(saved)
|
||||
check(loaded.storage and #loaded.storage.items == 1 and loaded.storage.items[1].id == 13, "fromSaveTable loaded saved storage")
|
||||
|
||||
local legacySave = { map = "FR_PLAYERS_HOUSE_2F", x = 1, y = 2, pcItems = { POTION = 1 } }
|
||||
local legacyLoaded = Schema.fromSaveTable(legacySave)
|
||||
check(legacyLoaded.storage and #legacyLoaded.storage.items == 1 and legacyLoaded.storage.items[1].id == 13, "fromSaveTable migrated legacy pcItems")
|
||||
|
||||
print("=== [TEST 6] Bridge.enterFromHost() starts with 1 Potion ===")
|
||||
local hostSave = {
|
||||
playerName = "RED",
|
||||
party = {},
|
||||
inventory = {},
|
||||
pcItems = { POTION = 1 },
|
||||
}
|
||||
local bridgeSession = Bridge.enterFromHost(nil, { save = hostSave }, { map = "FR_PLAYERS_HOUSE_2F" })
|
||||
check(bridgeSession.storage and #bridgeSession.storage.items == 1, "Bridge session has 1 PC item")
|
||||
check(bridgeSession.storage.items[1].id == 13 and bridgeSession.storage.items[1].qty == 1, "Bridge session item is POTION x1")
|
||||
|
||||
print("=== [TEST 7] Withdrawing Potion leaves PC empty and persists after save ===")
|
||||
check(Storage.withdrawItem(ng, 1, 1) == true, "Withdrew POTION from PC")
|
||||
check(Bag.has(ng.bag, 13, 1), "Bag has POTION")
|
||||
check(#ng.storage.items == 0, "PC is now empty")
|
||||
|
||||
local savedEmpty = Schema.toSaveTable(ng)
|
||||
local loadedEmpty = Schema.fromSaveTable(savedEmpty)
|
||||
check(#loadedEmpty.storage.items == 0, "Empty PC remains empty after save/load")
|
||||
|
||||
if failures == 0 then
|
||||
print("\nALL ITEM PC POTION TESTS PASSED (100%)")
|
||||
os.exit(0)
|
||||
else
|
||||
print(string.format("\n%d test(s) failed", failures))
|
||||
os.exit(1)
|
||||
end
|
||||
@@ -45,6 +45,9 @@ for b = 1, 14 do
|
||||
assert_eq(s.boxes[b].wallpaper, ((b - 1) % 16) + 1, "Box wallpaper initialized")
|
||||
assert_eq(Storage.countBoxMons(s, b), 0, "Box starts empty")
|
||||
end
|
||||
assert_eq(#s.items, 1, "PC starts with 1 item (Potion)")
|
||||
assert_eq(s.items[1].id, 13, "PC starts with Potion (id 13)")
|
||||
assert_eq(s.items[1].qty, 1, "PC starts with 1 Potion")
|
||||
print("[ok] 14 boxes and 50-slot PC initialized successfully")
|
||||
|
||||
print("=== [TEST 2] The PC Heal Exploit ===")
|
||||
@@ -165,6 +168,7 @@ local pcItemSession = {
|
||||
bag = Bag.new(),
|
||||
storage = Storage.new(),
|
||||
}
|
||||
pcItemSession.storage.items = {}
|
||||
-- Give 50 Poké Balls to Bag
|
||||
Bag.add(pcItemSession.bag, 4, 50) -- POKE_BALL = 4
|
||||
assert_eq(Bag.get(pcItemSession.bag, 4), 50, "Bag has 50 Poké Balls")
|
||||
|
||||
Reference in New Issue
Block a user