mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-28 07:36:30 -04:00
186 lines
5.3 KiB
Lua
186 lines
5.3 KiB
Lua
local Gen = require("Gen")
|
|
|
|
local M = {}
|
|
|
|
M.MODES = {
|
|
{ id = "livingdex", label = "Living Dex order", icon = "book-open", done = "in Living Dex order" },
|
|
{ id = "species", label = "Species, then level", icon = "list-filter", done = "by species and level" },
|
|
{ id = "level", label = "Level, high to low", icon = "arrow-up-down", done = "by level" },
|
|
{ id = "compact", label = "Remove gaps", icon = "chevrons-up", done = "without gaps" },
|
|
}
|
|
|
|
local DONE = {}
|
|
for _, mode in ipairs(M.MODES) do DONE[mode.id] = mode.done end
|
|
|
|
local function isEgg(mon)
|
|
return mon.isEgg == true or mon.egg == true
|
|
end
|
|
|
|
function M.dexNumber(S, mon)
|
|
local species = mon.species or mon.speciesId
|
|
if mon.speciesNumbering == "national" then
|
|
return tonumber(species)
|
|
end
|
|
local pokemon = S.data and S.data.pokemon
|
|
local def = pokemon and species ~= nil and pokemon[species]
|
|
local dex = def and tonumber(def.dex or def.national)
|
|
if dex then return dex end
|
|
if Gen.ofState(S) == 3 then
|
|
local ok, Pokemon = pcall(require, "src.core.game3.pokemon")
|
|
if ok and Pokemon and Pokemon.national then
|
|
local okN, n = pcall(Pokemon.national, species)
|
|
if okN and tonumber(n) then return tonumber(n) end
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function collect(S, boxes, count, capacity)
|
|
local entries = {}
|
|
for b = 1, count do
|
|
local box = boxes[b] or {}
|
|
for slot = 1, math.max(capacity, #box) do
|
|
local mon = box[slot]
|
|
if type(mon) == "table" then
|
|
local egg = isEgg(mon)
|
|
entries[#entries + 1] = {
|
|
mon = mon,
|
|
order = #entries + 1,
|
|
egg = egg,
|
|
dex = not egg and M.dexNumber(S, mon) or nil,
|
|
level = tonumber(mon.level) or 0,
|
|
}
|
|
end
|
|
end
|
|
end
|
|
return entries
|
|
end
|
|
|
|
local function group(e)
|
|
if e.egg then return 3 end
|
|
if not e.dex then return 2 end
|
|
return 1
|
|
end
|
|
|
|
local COMPARE = {
|
|
species = function(a, b)
|
|
if a.dex ~= b.dex then return a.dex < b.dex end
|
|
if a.level ~= b.level then return a.level > b.level end
|
|
end,
|
|
level = function(a, b)
|
|
if a.level ~= b.level then return a.level > b.level end
|
|
if a.dex ~= b.dex then return a.dex < b.dex end
|
|
end,
|
|
}
|
|
|
|
local function sortEntries(list, mode)
|
|
local cmp = COMPARE[mode]
|
|
table.sort(list, function(a, b)
|
|
local ga, gb = group(a), group(b)
|
|
if ga ~= gb then return ga < gb end
|
|
if ga == 1 and cmp then
|
|
local r = cmp(a, b)
|
|
if r ~= nil then return r end
|
|
end
|
|
return a.order < b.order
|
|
end)
|
|
end
|
|
|
|
function M.plan(S, mode, boxes, count, capacity)
|
|
local entries = collect(S, boxes, count, capacity)
|
|
local total = count * capacity
|
|
if #entries > total then return nil, "Not enough box space" end
|
|
local slots = {}
|
|
if mode == "compact" then
|
|
for i, e in ipairs(entries) do slots[i] = e.mon end
|
|
return slots, #entries
|
|
end
|
|
if mode ~= "livingdex" then
|
|
sortEntries(entries, mode)
|
|
for i, e in ipairs(entries) do slots[i] = e.mon end
|
|
return slots, #entries
|
|
end
|
|
local firsts, rest, seen = {}, {}, {}
|
|
for _, e in ipairs(entries) do
|
|
if e.dex and not seen[e.dex] then
|
|
seen[e.dex] = true
|
|
firsts[#firsts + 1] = e
|
|
else
|
|
rest[#rest + 1] = e
|
|
end
|
|
end
|
|
sortEntries(firsts, "species")
|
|
sortEntries(rest, "species")
|
|
if Gen.ofState(S) ~= 3 then
|
|
local i = 0
|
|
for _, e in ipairs(firsts) do i = i + 1; slots[i] = e.mon end
|
|
for _, e in ipairs(rest) do i = i + 1; slots[i] = e.mon end
|
|
return slots, i
|
|
end
|
|
local spill, top = {}, 0
|
|
for _, e in ipairs(firsts) do
|
|
if e.dex >= 1 and e.dex <= total and e.dex == math.floor(e.dex) then
|
|
slots[e.dex] = e.mon
|
|
top = math.max(top, e.dex)
|
|
else
|
|
spill[#spill + 1] = e
|
|
end
|
|
end
|
|
for _, e in ipairs(rest) do spill[#spill + 1] = e end
|
|
local cursor = top
|
|
for _, e in ipairs(spill) do
|
|
local placed = false
|
|
for step = 1, total do
|
|
local at = (cursor + step - 1) % total + 1
|
|
if slots[at] == nil then
|
|
slots[at] = e.mon
|
|
cursor = at
|
|
placed = true
|
|
break
|
|
end
|
|
end
|
|
if not placed then return nil, "Not enough box space" end
|
|
end
|
|
return slots, #entries
|
|
end
|
|
|
|
local function locate(boxes, count, capacity, mon)
|
|
for b = 1, count do
|
|
for slot = 1, capacity do
|
|
if boxes[b][slot] == mon then return b, slot end
|
|
end
|
|
end
|
|
end
|
|
|
|
function M.apply(S, Ops, mode)
|
|
if not DONE[mode] then return Ops.say(S, "Unknown box sort") end
|
|
local boxes = Ops.boxes(S)
|
|
local count, capacity = Ops.boxCount(S), Ops.boxCapacity(S)
|
|
local slots, n = M.plan(S, mode, boxes, count, capacity)
|
|
if not slots then return Ops.say(S, n) end
|
|
if n == 0 then return Ops.say(S, "The boxes are empty", "info") end
|
|
local changed = false
|
|
for b = 1, count do
|
|
local box = boxes[b]
|
|
for slot = 1, capacity do
|
|
if box[slot] ~= slots[(b - 1) * capacity + slot] then changed = true end
|
|
end
|
|
end
|
|
if not changed then
|
|
return Ops.say(S, "Boxes are already sorted " .. DONE[mode], "info")
|
|
end
|
|
for b = 1, count do
|
|
local box = boxes[b]
|
|
for slot = math.max(capacity, #box), 1, -1 do box[slot] = nil end
|
|
for slot = 1, capacity do
|
|
box[slot] = slots[(b - 1) * capacity + slot]
|
|
end
|
|
end
|
|
local b, slot = nil, nil
|
|
if S.editingMon then b, slot = locate(boxes, count, capacity, S.editingMon) end
|
|
S.selectedBoxSlot = b == S.selectedBox and slot or 1
|
|
return Ops.mark(S, ("Sorted %d Pokemon %s"):format(n, DONE[mode]))
|
|
end
|
|
|
|
return M
|