Merge branch 'my-uncles-neighbor' into dev

# Conflicts:
#	src/import/RomImporter.lua
This commit is contained in:
bryanthaboi
2026-08-20 08:28:19 -04:00
68 changed files with 18617 additions and 466 deletions
@@ -41,12 +41,21 @@ end
-- Mock isReady
RomImporter.isReady = function(v)
return v == "red" or v == "gold" or v == "blue" or v == "yellow"
or v == "silver"
end
local ok = RomImporter.syncAndroidShortcuts("gold")
check(ok == true, "syncAndroidShortcuts returns true on Android")
check(#capturedShortcuts == 4, "syncAndroidShortcuts caps at 4 items")
check(capturedShortcuts[1] == "gold", "activeVersion 'gold' is placed first")
check(capturedShortcuts[2] == "red" and capturedShortcuts[3] == "blue"
and capturedShortcuts[4] == "yellow",
"the rest follow GameVersion.ORDER until the cap")
capturedShortcuts = nil
RomImporter.syncAndroidShortcuts("silver")
check(#capturedShortcuts == 4, "a fifth ready game does not widen the payload")
check(capturedShortcuts[1] == "silver", "activeVersion 'silver' is placed first")
-- Test with subset of ready games (e.g. only Red and Gold)
RomImporter.isReady = function(v)
@@ -78,4 +87,4 @@ end
love.system.getLaunchGame = savedGetLaunchGame
love.system.updateShortcuts = nil
print("8/8 checks passed (android_shortcuts_payload_test)")
T.finish("android_shortcuts_payload_test")
+1
View File
@@ -23,6 +23,7 @@ T.eq(GameVersion.generation("red"), 1, "Red is Gen 1")
T.eq(GameVersion.generation("blue"), 1, "Blue is Gen 1")
T.eq(GameVersion.generation("yellow"), 1, "Yellow is Gen 1")
T.eq(GameVersion.generation("gold"), 2, "Gold is Gen 2")
T.eq(GameVersion.generation("silver"), 2, "Silver is Gen 2")
-- ------- 2. manifest: gen2compat is opt-in and defaults off
+45 -34
View File
@@ -39,45 +39,54 @@ end
local edited = 0
local hooks = { editTouchControls = function() edited = edited + 1 end }
local gold = LauncherSettings.open(hooks, "gold")
check(has(gold, "TOUCH PAD"), "Gold's gear offers TOUCH PAD")
check(has(gold, "VIBRATION"), "and VIBRATION")
check(has(gold, "TOUCH CONTROLS"), "and the layout editor")
check(has(gold, "VOID FILL"), "and VOID FILL")
for _, version in ipairs({ "gold", "silver" }) do
local model = LauncherSettings.open(hooks, version)
check(has(model, "TOUCH PAD"), version .. " gear offers TOUCH PAD")
check(has(model, "VIBRATION"), version .. " and VIBRATION")
check(has(model, "TOUCH CONTROLS"), version .. " and the layout editor")
check(has(model, "VOID FILL"), version .. " and VOID FILL")
local voidFill = findRow(gold, "VOID FILL")
eq(voidFill.value(), "FADE ", "VOID FILL defaults to FADE")
voidFill.step(1)
eq(gold.opts.gold.voidFill, "water", "right stores water in the gold block")
eq(voidFill.value(), "WATER", "and the row reads WATER")
voidFill.step(-1)
eq(gold.opts.gold.voidFill, "fade", "left restores fade")
local voidFill = findRow(model, "VOID FILL")
eq(voidFill.value(), "FADE ", version .. " VOID FILL defaults to FADE")
voidFill.step(1)
eq(model.opts.gold.voidFill, "water",
version .. " right stores water in the gold block")
eq(voidFill.value(), "WATER", version .. " and the row reads WATER")
voidFill.step(-1)
eq(model.opts.gold.voidFill, "fade", version .. " left restores fade")
-- Every write has to land in the gold block: the flat keys beside it are
-- Red's, and Gold's boot never reads them (src/core/gen2/Save.lua:299).
-- loadOptions seeds the flat Gen 1 defaults, so the check is that the Gold
-- rows leave them exactly as they found them.
local flatPad = gold.opts.touchControls
local flatBuzz = gold.opts.haptics
-- Every write has to land in the gen2 block: the flat keys beside it are
-- Red's, and no Gen 2 boot reads them (src/core/gen2/Save.lua:299).
-- loadOptions seeds the flat Gen 1 defaults, so the check is that the Gen 2
-- rows leave them exactly as they found them.
local flatPad = model.opts.touchControls
local flatBuzz = model.opts.haptics
local pad = findRow(gold, "TOUCH PAD")
local before = pad.value()
pad.step(1)
check(pad.value() ~= before, "stepping TOUCH PAD flips it")
check(type(gold.opts.gold) == "table", "into the gold block")
eq(gold.opts.gold.touchControls.enabled, false, "which now carries enabled")
eq(gold.opts.touchControls, flatPad, "leaving the flat Gen 1 key alone")
local pad = findRow(model, "TOUCH PAD")
local before = pad.value()
pad.step(1)
check(pad.value() ~= before, version .. " stepping TOUCH PAD flips it")
check(type(model.opts.gold) == "table", version .. " into the gold block")
eq(model.opts.gold.touchControls.enabled, false,
version .. " which now carries enabled")
eq(model.opts.touchControls, flatPad,
version .. " leaving the flat Gen 1 key alone")
eq(model.opts.silver, nil,
version .. " and inventing no second Gen 2 block beside it")
local buzz = findRow(gold, "VIBRATION")
local buzzBefore = buzz.value()
buzz.step(1)
check(buzz.value() ~= buzzBefore, "stepping VIBRATION moves the level")
eq(gold.opts.gold.haptics, TouchControls.normalizeHaptics(gold.opts.gold.haptics),
"VIBRATION stores a level the shared module knows")
eq(gold.opts.haptics, flatBuzz, "also without touching Red's")
local buzz = findRow(model, "VIBRATION")
local buzzBefore = buzz.value()
buzz.step(1)
check(buzz.value() ~= buzzBefore, version .. " stepping VIBRATION moves the level")
eq(model.opts.gold.haptics,
TouchControls.normalizeHaptics(model.opts.gold.haptics),
version .. " VIBRATION stores a level the shared module knows")
eq(model.opts.haptics, flatBuzz, version .. " also without touching Red's")
findRow(gold, "TOUCH CONTROLS").action()
eq(edited, 1, "the editor row reaches the host hook")
edited = 0
findRow(model, "TOUCH CONTROLS").action()
eq(edited, 1, version .. " the editor row reaches the host hook")
end
-- The Gen 1 gear is untouched by the extraction: same three rows, still on
-- the flat table.
@@ -94,6 +103,8 @@ eq(findRow(red, "TOUCH PAD").value() ~= nil, true, "and reading it back")
-- rather than dead, on both sides.
eq(has(LauncherSettings.open(nil, "gold"), "TOUCH CONTROLS"), false,
"no hook, no editor row on Gold")
eq(has(LauncherSettings.open(nil, "silver"), "TOUCH CONTROLS"), false,
"nor on Silver")
eq(has(LauncherSettings.open(nil, "red"), "TOUCH CONTROLS"), false,
"nor on Red")
-- The Edit row hands the screen to the host, and the host has to know WHICH
+41 -38
View File
@@ -155,28 +155,34 @@ modImp.mods = mods
modImp._ensureMods = function() return mods end
LauncherView.draw(modImp)
LauncherView.draw(modImp)
check((modImp._modScrollMax or 0) > 0,
"60 mods overflow the list viewport inside the panel")
local list = modImp._modListRect
check(list.x + list.w
<= modImp._tabRegionRect.x + modImp._tabRegionRect.w - Kit.scrollBarW(),
"the rows stop short of the region's scrollbar gutter")
pointer(list.x + 10, list.y + 10)
local reg = modImp._tabRegionRect
check((modImp._tabScrollMax.mods or 0) > reg.h,
"60 installed mods are ONE continuous list: the region's travel spans "
.. "the whole list, not one page of it")
eq(modImp._pages.mods, nil, "and no page state is ever minted for it")
pointer(reg.x + 10, reg.y + 40)
modImp._wheelY = -1
LauncherView.draw(modImp)
check((modImp.modScroll or 0) > 0, "a notch over the mod list scrolls the list")
eq(modImp._tabScroll.mods or 0, 0, "not the panel region around it")
eq(modImp._pageScroll, 0, "and not the page behind that")
check((modImp._tabScroll.mods or 0) > 0,
"a notch over the region scrolls the list like any other tab")
eq(modImp._pageScroll, 0, "without reaching the page behind it")
modImp._tabScroll.mods = modImp._tabScrollMax.mods
LauncherView.draw(modImp)
pointer(reg.x + 10, reg.y + reg.h * 0.5)
modImp._wheelY = -1
LauncherView.draw(modImp)
eq(modImp._pages.mods, nil,
"bottoming the list out never auto-advances any pager")
eq(modImp._tabScroll.mods, modImp._tabScrollMax.mods,
"the list just rests at its end")
modImp._modActions = "mod1"
local shielded = modImp.modScroll
local shieldedPage = modImp._pageScroll
pointer(list.x + 10, list.y + 10)
local shieldedAt = modImp._tabScroll.mods
modImp._wheelY = -1
LauncherView.draw(modImp)
eq(modImp.modScroll, shielded, "a shielded mod list ignores the notch")
eq(modImp._tabScroll.mods or 0, 0, "and so does the region under the scrim")
eq(modImp._pageScroll, shieldedPage, "and the page behind that")
eq(modImp._tabScroll.mods, shieldedAt,
"a shielded mod list ignores the notch under the scrim")
modImp._modActions = nil
modImp._wheelY = 0
LauncherView.draw(modImp)
@@ -225,32 +231,27 @@ dragMods.mods = mods
dragMods._ensureMods = function() return mods end
LauncherView.draw(dragMods)
LauncherView.draw(dragMods)
local dlist = dragMods._modListRect
local dListMax = dragMods._modScrollMax
local dreg = dragMods._tabRegionRect
local dRegionMax = dragMods._tabScrollMax.mods
check(dListMax > 0 and dRegionMax > 0,
"the mods tab has both an inner list and a region to scroll")
LauncherView.touchpressed(dragMods, 7, dlist.x + 20, dlist.y + 30)
LauncherView.touchmoved(dragMods, 7, dlist.x + 20, dlist.y + 30 - 60)
eq(dragMods.modScroll, math.min(60, dListMax),
"the first pixels of the drag move the list")
eq(dragMods._tabScroll.mods or 0, 0, "and nothing else")
LauncherView.touchmoved(dragMods, 7, dlist.x + 20,
dlist.y + 30 - 60 - dListMax - dRegionMax * 2)
eq(dragMods.modScroll, dListMax, "carrying on saturates the list")
eq(dragMods._tabScroll.mods, dRegionMax,
"then the same gesture walks the region to its bottom")
check(dRegionMax > 0, "the mods region scrolls its overscan like any tab")
LauncherView.touchpressed(dragMods, 7, dreg.x + 20, dreg.y + 30)
LauncherView.touchmoved(dragMods, 7, dreg.x + 20, dreg.y + 30 - 60)
eq(dragMods._tabScroll.mods, math.min(60, dRegionMax),
"a drag moves the region by the finger's travel")
LauncherView.touchmoved(dragMods, 7, dreg.x + 20,
dreg.y + 30 - 60 - dRegionMax * 2)
eq(dragMods._tabScroll.mods, dRegionMax, "carrying on saturates the region")
check((dragMods._pageScroll or 0) > 0, "and only then reaches the page")
LauncherView.touchreleased(dragMods, 7, dlist.x + 20, dlist.y - 900)
LauncherView.touchreleased(dragMods, 7, dreg.x + 20, dreg.y - 900)
dragMods._skins = { { id = "s1", source = "user", controls = 8, pages = 1 } }
for i = 2, 12 do
dragMods._skins[i] = { id = "s" .. i, source = "user", controls = 8, pages = 1 }
end
dragMods._ensureSkins = function() return dragMods._skins end
dragMods.modScroll = 0
local heldModScroll = dragMods.modScroll
local overList = dlist.y + 30
local heldModsPage = dragMods._pages.mods or 1
local heldModsAt = dragMods._tabScroll.mods
local overList = dreg.y + 60
dragMods:_switchTab("skins")
LauncherView.draw(dragMods)
LauncherView.draw(dragMods)
@@ -260,8 +261,10 @@ LauncherView.touchpressed(dragMods, 9, sreg.x + 20, overList)
LauncherView.touchmoved(dragMods, 9, sreg.x + 20, overList - 200)
check((dragMods._tabScroll.skins or 0) > 0,
"a drag on the skins tab scrolls the skins tab")
eq(dragMods.modScroll, heldModScroll,
"and leaves the mod list where the player parked it")
eq(dragMods._pages.mods or 1, heldModsPage,
"and leaves the mod list on the page the player parked it")
eq(dragMods._tabScroll.mods, heldModsAt,
"with its region offset held for the return trip")
LauncherView.touchreleased(dragMods, 9, sreg.x + 20, sreg.y - 400)
love.graphics.polygon = love.graphics.polygon or function() end
@@ -308,8 +311,8 @@ local view = read("src/import/LauncherView.lua")
check(view:find("Kit.scrollBegin(", 1, true) ~= nil,
"the panel dispatch opens a scroll region")
check(view:find("Kit.scrollEnd(", 1, true) ~= nil, "and closes it")
check(view:find("modListWantsWheel", 1, true) ~= nil,
"the nested mod list is asked before the region takes a notch")
check(view:find("modListWantsWheel", 1, true) == nil,
"no nested list steals the wheel from the region any more")
check(view:find("start.region", 1, true) ~= nil,
"a touch drag that began in the region scrolls the region")
check(view:find("Kit.scrollGutter(", 1, true) ~= nil,
+12 -8
View File
@@ -32,11 +32,13 @@ do
"a version id names exactly that game")
eq(table.concat(ModTargets.expand("GEN1"), ","), "red,blue,yellow",
"gen1 is every Gen 1 game, case-insensitive")
eq(table.concat(ModTargets.expand("gen2"), ","), "gold",
eq(table.concat(ModTargets.expand("gen2"), ","), "gold,silver",
"gen2 is every Gen 2 game")
eq(table.concat(ModTargets.expand("silver"), ","), "silver",
"and each of them names itself")
eq(table.concat(ModTargets.expand("all"), ","),
table.concat(GameVersion.ORDER, ","), "all is the launcher order itself")
eq(ModTargets.expand("silver"), nil, "a game this engine has no cache for")
eq(ModTargets.expand("crystal"), nil, "a game this engine has no cache for")
eq(ModTargets.expand("gen9"), nil, "a generation with no games is unknown")
eq(ModTargets.expand(7), nil, "a non-string token is not a game")
end
@@ -56,7 +58,7 @@ end
do
eq(list(mf({})), "red,blue,yellow",
"a manifest with no games key is Gen 1, which is what it was tested as")
eq(list(mf({ gen2compat = true })), "red,blue,yellow,gold",
eq(list(mf({ gen2compat = true })), "red,blue,yellow,gold,silver",
"gen2compat keeps Gen 1 and adds Gen 2")
eq(mf({}).gen2compat, false, "and the derived flag agrees")
eq(mf({ gen2compat = true }).gen2compat, true, "both ways")
@@ -67,14 +69,14 @@ end
do
local gen2 = mf({ games = { "gen2" } })
eq(list(gen2), "gold", "games can name Gen 2 alone")
eq(list(gen2), "gold,silver", "games can name Gen 2 alone")
eq(gen2.gen2compat, true, "which IS the gen2compat claim the gate reads")
local both = mf({ games = { "gen1", "gen2" } })
eq(list(both), "red,blue,yellow,gold", "or both generations")
eq(list(both), "red,blue,yellow,gold,silver", "or both generations")
local one = mf({ games = { "blue" } })
eq(list(one), "blue", "or one single game")
eq(one.gen2compat, false, "a Gen 1 game is not a Gen 2 claim")
eq(list(mf({ games = { "red" }, gen2compat = true })), "red,gold",
eq(list(mf({ games = { "red" }, gen2compat = true })), "red,gold,silver",
"an old gen2compat beside a new games list still adds its game")
end
@@ -224,10 +226,12 @@ do
local bad = ModProfile.decode(require("src.core.SaveSerializer").encode({
format = "g1rmodlist", formatVersion = 1,
profile = { name = "P", enabledByVersion = {
gold = { a = true }, silver = { a = true }, red = "nope" } },
gold = { a = true }, silver = { a = true }, crystal = { a = true },
red = "nope" } },
}))
eq(bad.enabledByVersion.gold.a, true, "a shared file's known game is kept")
eq(bad.enabledByVersion.silver, nil, "an unknown game is dropped on read")
eq(bad.enabledByVersion.silver.a, true, "every one of them, not just the first")
eq(bad.enabledByVersion.crystal, nil, "an unknown game is dropped on read")
eq(bad.enabledByVersion.red, nil, "and so is a bucket that is not a table")
end
@@ -59,7 +59,7 @@ package.loaded["src.import.RomImporter"] = nil
RomImporter = require("src.import.RomImporter")
local function clearSavesInbox()
for _, ver in ipairs({ "red", "blue", "yellow", "gold" }) do
for _, ver in ipairs({ "red", "blue", "yellow", "gold", "silver" }) do
local dir = "imports/saves/" .. ver
for _, name in ipairs(love.filesystem.getDirectoryItems(dir) or {}) do
love.filesystem.remove(dir .. "/" .. name)
@@ -137,6 +137,8 @@ check(createdDirs["imports/saves/yellow"] == true,
"RES-01: ensureSavesInboxDir creates imports/saves/yellow/")
check(createdDirs["imports/saves/gold"] == true,
"RES-01: ensureSavesInboxDir creates imports/saves/gold/")
check(createdDirs["imports/saves/silver"] == true,
"RES-01: ensureSavesInboxDir creates imports/saves/silver/")
-- NXSAV-02: notice/hint includes save dir + per-game imports/saves/<version>/ MTP path
ri = freshImporter()
+4 -2
View File
@@ -81,7 +81,8 @@ local function importer(ready)
}, RomImporter)
end
local allReady = importer({ red = true, blue = true, yellow = true, gold = true })
local allReady = importer({ red = true, blue = true, yellow = true, gold = true,
silver = true })
allReady:_queueBaseRomScan()
eq(allReady.baseRomScan.state, "done", "ready launcher skips discovery")
eq(listings, 0, "ready launcher does not enumerate baseroms")
@@ -123,7 +124,8 @@ eq(picks, 0, "missing detected ROM does not open the picker unexpectedly")
missing:choose("red")
eq(picks, 1, "the next import attempt falls back to the native picker")
local rescanned = importer({ red = true, blue = true, yellow = true, gold = true })
local rescanned = importer({ red = true, blue = true, yellow = true, gold = true,
silver = true })
rescanned.baseRoms.red = { path = "baseroms/z-red.gb", name = "z-red.gb" }
rescanned:reimport("red")
check(rescanned.baseRoms.red == nil, "re-import clears the detected ROM")
+6
View File
@@ -31,10 +31,14 @@ require("src.core.Logger").warn = function() end
local Core = require("src.core.gen2.HallOfFame")
local Credits = require("src.ui.gen2.Credits")
local GameVersion = require("src.core.GameVersion")
local HallOfFame = require("src.ui.gen2.HallOfFame")
local Save = require("src.core.gen2.Save")
local Screens = require("src.ui.Screens")
local priorVersion = GameVersion.get()
GameVersion.set("gold")
-- ---- fixtures -------------------------------------------------------------
local POKEMON = {
@@ -711,4 +715,6 @@ else
eq(creditsData.palettes[1][1][1], 255, "and set 1 colour 0 is white")
end
GameVersion.set(priorVersion)
S.finish()
+2
View File
@@ -76,6 +76,8 @@ local function makeWorld()
local game = {
data = { audio = {}, screens = registry },
save = {
version = "gold",
generation = 2,
player = { name = "GOLD" },
party = { { species = "TYPHLOSION", level = 50, hp = 120,
otId = 33333 } },
+55 -1
View File
@@ -28,6 +28,9 @@ love.filesystem = {
local GameVersion = require("src.core.GameVersion")
local Save = require("src.core.gen2.Save")
local priorVersion = GameVersion.get()
GameVersion.set("gold")
local failures, checks = 0, 0
local function check(name, got, want)
checks = checks + 1
@@ -48,6 +51,11 @@ check("main file", main, "save_gold.lua")
check("backup file", backup, "save_gold.lua.bak")
check("staged file", tmp, "save_gold.lua.tmp")
check("gold suffix", GameVersion.saveSuffix("gold"), "_gold")
local sMain, sBackup, sTmp = Save.filenames("silver")
check("silver main file", sMain, "save_silver.lua")
check("silver backup file", sBackup, "save_silver.lua.bak")
check("silver staged file", sTmp, "save_silver.lua.tmp")
check("silver suffix", GameVersion.saveSuffix("silver"), "_silver")
-- Red keeps its historical un-suffixed name, so the two can never collide.
check("red is unsuffixed", GameVersion.saveSuffix("red"), "")
@@ -104,7 +112,13 @@ check("defaults are copied", Save.defaultOptions().textSpeed, "MID")
-- A sparse or hand-edited save must come back indexable rather than crashing
-- the first screen that reads it.
local sparse = Save.normalize({ player = {} })
check("normalized version", sparse.version, "gold")
check("normalized version", sparse.version, GameVersion.get())
check("a Gen 2 version already on the table is kept",
Save.normalize({ version = "silver", player = {} }).version, "silver")
check("and a Gen 1 one is replaced by the running edition",
Save.normalize({ version = "red", player = {} }).version, "gold")
check("as is a version this engine does not have",
Save.normalize({ version = "crystal", player = {} }).version, "gold")
check("party exists", type(sparse.party), "table")
check("inventory exists", type(sparse.inventory), "table")
check("pokedex seen exists", type(sparse.pokedex.seen), "table")
@@ -478,6 +492,46 @@ do
files = {}
end
-- ------- the same save, run as Silver
--
-- The blank name is the edition's own first PlayerNameArray row
-- (data/player_names.asm:12-23).
do
GameVersion.set("silver")
files = {}
local born = Save.newGame({})
check("a silver boot stamps silver", born.version, "silver")
check("still generation 2", born.generation, 2)
check("with the edition's own preset name", born.player.name, "SILVER")
check("gold's preset is unchanged", Save.defaultPlayerName("gold"), "GOLD")
check("and silver's is its own", Save.defaultPlayerName("silver"), "SILVER")
check("the running edition answers with no argument",
Save.defaultPlayerName(), "SILVER")
check("a version-less save takes the running edition",
Save.normalize({ player = {} }).version, "silver")
check("and a gold save keeps gold on a silver boot",
Save.normalize({ version = "gold", player = {} }).version, "gold")
check("no silver save yet", Save.exists("silver"), false)
check("silver save wrote", Save.save(born), true)
check("silver save exists now", Save.exists("silver"), true)
check("gold is untouched by it", Save.exists("gold"), false)
local main = Save.filenames("silver")
check("the silver file is silver's own", main, "saves/silver/slot1.lua")
check("and it is the file on disk", files[main] ~= nil, true)
local back = Save.load("silver")
check("silver round-trips", back and back.player.name, "SILVER")
check("keeping its stamp", back.version, "silver")
files = {}
GameVersion.set("gold")
end
GameVersion.set(priorVersion)
print(("gen2 save: %d checks, %d failures"):format(checks, failures))
-- Raise rather than os.exit: tests/run_tests.lua dofiles this file, so an
-- exit here takes the whole tier down with it and silently skips every
+35 -21
View File
@@ -71,13 +71,17 @@ local data = {
maps = {},
}
local function newState()
local function newState(version)
version = version or "gold"
local prior = GameVersion.get()
GameVersion.set(version)
local S = State.new()
S.data = data
S.cat = Catalog.build(data)
S.save = Save2.newGame()
S.version = "gold"
S.version = version
Gen.ensureBoxes(S.save)
GameVersion.set(prior)
return S
end
@@ -85,10 +89,19 @@ do
GameVersion.set("gold")
eq(Gen.of({ generation = 2 }), 2, "Gen.of generation field")
eq(Gen.of({ version = "gold" }), 2, "Gen.of version gold")
eq(Gen.of({ version = "silver" }), 2, "Gen.of version silver")
eq(Gen.of(SaveData.newGame()), 1, "Gen.of gen1 newGame")
eq(Gen.of(Save2.newGame()), 2, "Gen.of gold newGame")
end
do
GameVersion.set("silver")
eq(Save2.newGame().version, "silver", "a silver boot stamps silver")
eq(Save2.newGame().player.name, "SILVER", "with silver's preset name")
eq(Gen.of(Save2.newGame()), 2, "Gen.of silver newGame")
GameVersion.set("gold")
end
do
local S = newState()
check(Ops.speciesUsable(S, "CYNDAQUIL"), "spa/spd species is usable")
@@ -103,36 +116,37 @@ do
check(not Ops.speciesUsable(S1, "BROKEN"), "partial record is not usable")
end
do
local S = newState()
for _, version in ipairs({ "gold", "silver" }) do
local S = newState(version)
Ops.partyAdd(S)
eq(#S.save.party, 1, "partyAdd on gold")
eq(#S.save.party, 1, "partyAdd on " .. version)
local mon = S.save.party[1]
eq(mon.species, "CYNDAQUIL", "first catalog species")
check(mon.experience ~= nil, "gold mon has experience")
check(mon.exp == nil or mon.experience ~= nil, "does not rely on gen1 exp")
eq(mon.species, "CYNDAQUIL", version .. " first catalog species")
check(mon.experience ~= nil, version .. " mon has experience")
check(mon.exp == nil or mon.experience ~= nil,
version .. " does not rely on gen1 exp")
check(mon.stats.specialAttack and mon.stats.specialDefense,
"gold stats have spa/spd")
check(mon.happiness ~= nil, "gold mon has happiness")
eq(mon.ot, S.save.player.name, "stampOT copies player name")
version .. " stats have spa/spd")
check(mon.happiness ~= nil, version .. " mon has happiness")
eq(mon.ot, S.save.player.name, version .. " stampOT copies player name")
Ops.setLevel(S, mon, 20)
eq(mon.level, 20, "setLevel 20")
check(mon.experience > 0, "experience resynced")
eq(mon.level, 20, version .. " setLevel 20")
check(mon.experience > 0, version .. " experience resynced")
Ops.setHappiness(S, mon, 200)
eq(mon.happiness, 200, "happiness 200")
eq(mon.happiness, 200, version .. " happiness 200")
Ops.setPokerus(S, mon, 15)
eq(mon.pokerus, 15, "pokerus byte")
eq(mon.pokerus, 15, version .. " pokerus byte")
Ops.setHeldItem(S, mon, "POTION")
eq(mon.item, "POTION", "held item")
eq(mon.item, "POTION", version .. " held item")
eq(mon.name, "CYNDAQUIL", "new mon copies species display name")
eq(mon.name, "CYNDAQUIL", version .. " new mon copies species display name")
Ops.setSpecies(S, mon, "TOTODILE")
eq(mon.species, "TOTODILE", "setSpecies id")
eq(mon.name, "TOTODILE", "setSpecies rewrites the Gold display name")
check(mon.nickname == nil, "setSpecies does not invent a nickname")
eq(mon.types[1], "WATER", "setSpecies rewrites copied types")
eq(mon.species, "TOTODILE", version .. " setSpecies id")
eq(mon.name, "TOTODILE", version .. " setSpecies rewrites the display name")
check(mon.nickname == nil, version .. " setSpecies does not invent a nickname")
eq(mon.types[1], "WATER", version .. " setSpecies rewrites copied types")
end
do
+10
View File
@@ -149,12 +149,22 @@ local ok, err = pcall(function()
local Ops = require("Ops")
check(not ModTargets.supports({}, "gold"),
"legacy gen1-only fixture does not support gold")
check(not ModTargets.supports({}, "silver"),
"nor silver")
check(not ModTargets.supports({ games = { "red" } }, "gold"),
"explicit gen1 games list does not support gold")
check(not ModTargets.supports({ games = { "red" } }, "silver"),
"nor silver")
check(ModTargets.supports({ games = { "gold" } }, "gold"),
"gold-targeted manifest supports gold")
check(not ModTargets.supports({ games = { "gold" } }, "silver"),
"and names one edition, not the generation")
check(ModTargets.supports({ games = { "gold", "silver" } }, "silver"),
"a whole-Gen-2 games list supports silver")
check(ModTargets.supports({ gen2compat = true }, "gold"),
"gen2compat legacy still supports gold")
check(ModTargets.supports({ gen2compat = true }, "silver"),
"and silver with it")
local goldS = {
data = {