Files

208 lines
7.1 KiB
Lua

local Copy = require("src.mods.Merge").deepCopy
local Items = require("src.core.game3.items_data")
local Bag = require("src.core.game3.bag")
local Storage = require("src.core.game3.storage")
local Mons = require("src.core.game3.save_mon")
local M = {}
local pockets = { "ITEMS", "KEY_ITEMS", "POKE_BALLS", "TM_CASE", "BERRY_POUCH" }
M.PC_ITEMS_COUNT = 30 -- include/constants/global.h:35
function M.itemId(data, id)
local def = data and data.items and data.items[id]
return tonumber(def and def.itemId) or Items.toNumericId(id) or id
end
function M.key(data, id)
return tostring(M.itemId(data, id))
end
local function equal(a, b)
if a == b then return true end
if type(a) ~= "table" or type(b) ~= "table" then return false end
for k, v in pairs(a) do if not equal(v, b[k]) then return false end end
for k in pairs(b) do if a[k] == nil then return false end end
return true
end
function M.ensureStorage(save)
local legacy = type(save.storage) ~= "table"
local source = legacy and Storage.restore(nil, save.pc, save.pcItems or save.pc_items) or save.storage
local storage = {}
for k, v in pairs(source) do storage[k] = v end
storage.boxes, storage.items = {}, source.items or {}
local boxes = {}
local available = {}
for b = 1, 14 do
local original = source.boxes and source.boxes[b] or { name = "BOX " .. b, wallpaper = ((b - 1) % 16) + 1 }
local box = {}
for k, v in pairs(original) do box[k] = v end
box.mons = {}
for s, mon in pairs(original.mons or {}) do
box.mons[s] = mon
available[#available + 1] = { mon = mon }
end
storage.boxes[b], boxes[b] = box, box.mons
end
for b, box in pairs(save.boxes or {}) do
for s, mon in pairs(type(box) == "table" and box or {}) do
if type(s) == "number" and type(mon) == "table" then
local mirrored = false
for _, entry in ipairs(available) do
if not entry.used and equal(entry.mon, mon) then entry.used = true; mirrored = true; break end
end
if not mirrored then
local targetB, targetS
if boxes[b] and s >= 1 and s <= 30 and s == math.floor(s) and not boxes[b][s] then
targetB, targetS = b, s
else
targetB, targetS = Storage.findOpenSlot(storage)
end
if not targetB then error("Cannot preserve legacy box Pokemon: native storage is full") end
boxes[targetB][targetS] = mon
end
end
end
end
save.storage = storage
if legacy and type(save.pc) == "table" then save.pc.mons, save.pc.items = nil, nil end
save.boxes = boxes
save.currentBox = math.max(1, math.min(14, tonumber(storage.currentBox) or 1))
return boxes
end
local function project(data, slots, out, order)
for _, slot in ipairs(slots or {}) do
if slot.id ~= nil and (tonumber(slot.qty) or 0) > 0 then
local key = M.key(data, slot.id)
if not out[key] and order then order[#order + 1] = key end
out[key] = (out[key] or 0) + slot.qty
end
end
end
function M.project(data, save)
local inv, order, pc = {}, {}, {}
for _, pocket in ipairs(pockets) do project(data, save.bag.pockets[pocket], inv, order) end
project(data, save.storage.items, pc)
save.inventory, save.bagOrder, save.pcItems = inv, order, pc
save.bag.stacks = Copy(inv)
end
function M.hydrate(data, save)
M.ensureStorage(save)
if type(save.bag) ~= "table" then save.bag = Copy(save.inventory or {}) end
if type(save.bag.pockets) ~= "table" then
local source = save.bag
if not source.stacks and not source.items then source = { stacks = source } end
save.bag = Bag.migrate(Copy(source))
end
M.project(data, save)
end
function M.export(save)
local out = Copy(save)
M.ensureStorage(out)
Mons.each(out, Mons.normalize)
out.inventory = Copy(out.bag)
out.boxes, out.pcItems, out.bagOrder = nil, nil, nil
return out
end
local function locate(data, slots, id)
for i, slot in ipairs(slots) do
if M.key(data, slot.id) == M.key(data, id) then return i, slot end
end
end
function M.quantity(data, save, pc, id)
local key = M.key(data, id)
return tonumber((pc and save.pcItems or save.inventory or {})[key]) or 0
end
local function set(data, save, pc, id, qty)
if type(qty) ~= "number" or qty ~= math.floor(qty) or qty < 0 or qty > 999 then return false end
id = M.itemId(data, id)
local slots, cap
if pc then
slots, cap = save.storage.items, M.PC_ITEMS_COUNT
else
for _, p in ipairs(pockets) do
local list = save.bag.pockets[p]
if list and locate(data, list, id) then slots = list; break end
end
local pocket = Items.pocketOf(id)
slots = slots or save.bag.pockets[pocket]
if not slots then slots = {}; save.bag.pockets[pocket] = slots end
cap = Items.CAPACITY[pocket] or 42
end
local lists = pc and { slots } or {}
if not pc then for _, p in ipairs(pockets) do lists[#lists + 1] = save.bag.pockets[p] or {} end end
local found = false
for _, list in ipairs(lists) do
local i = 1
while i <= #list do
local slot = list[i]
if M.key(data, slot.id) == M.key(data, id) then
if not found and qty > 0 then
slot.qty = qty
found = true
i = i + 1
else
found = true
table.remove(list, i)
end
else i = i + 1 end
end
end
if found then
return true
elseif qty > 0 then
if #slots >= cap then return false end
if not pc then
local pocket = Items.pocketOf(id)
local container = pocket == "TM_CASE" and Items.ITEM_TM_CASE or pocket == "BERRY_POUCH" and Items.ITEM_BERRY_POUCH
if container and not locate(data, save.bag.pockets.KEY_ITEMS or {}, container) then
if not set(data, save, false, container, 1) then return false end
end
end
slots[#slots + 1] = { id = id, qty = qty }
end
return true
end
function M.change(data, save, pc, changes)
local staged = { bag = pc and save.bag or Copy(save.bag), storage = save.storage }
if pc then
staged.storage = {}
for k, value in pairs(save.storage) do staged.storage[k] = value end
staged.storage.items = Copy(save.storage.items)
end
for _, change in ipairs(changes) do
if not set(data, staged, pc, change.id, change.qty) then return false end
end
save.bag, save.storage = staged.bag, staged.storage
M.ensureStorage(save)
M.project(data, save)
return true
end
-- src/item_menu.c:2004 Task_TryDoItemDeposit / src/item.c:385 AddPCItem
function M.transfer(data, save, toPc, id, qty)
if type(qty) ~= "number" or qty <= 0 or qty ~= math.floor(qty) then return false end
local fromQty = M.quantity(data, save, not toPc, id)
local toQty = M.quantity(data, save, toPc, id)
if qty > fromQty or toQty + qty > 999 then return false end
local staged = { bag = Copy(save.bag), storage = {} }
for k, value in pairs(save.storage) do staged.storage[k] = value end
staged.storage.items = Copy(save.storage.items)
if not set(data, staged, toPc, id, toQty + qty) then return false end
if not set(data, staged, not toPc, id, fromQty - qty) then return false end
save.bag, save.storage = staged.bag, staged.storage
M.ensureStorage(save)
M.project(data, save)
return true
end
return M