From 7b208043b913de0892a95bbfb471655f23ecc0dc Mon Sep 17 00:00:00 2001 From: johnjohto Date: Thu, 30 Jul 2026 10:17:30 -0400 Subject: [PATCH] Fix Yellow NPC trades --- src/core/Data.lua | 23 +++++++++++++ tests/parity_yellow_trades.lua | 59 ++++++++++++++++++++++++++++++++++ tests/run_tests.lua | 1 + tools/make_yellow_manifest.py | 1 + tools/rom_manifest_yellow.json | 54 +++++++++++++++---------------- 5 files changed, 111 insertions(+), 27 deletions(-) create mode 100644 tests/parity_yellow_trades.lua diff --git a/src/core/Data.lua b/src/core/Data.lua index 8ca5a734..a68c050d 100644 --- a/src/core/Data.lua +++ b/src/core/Data.lua @@ -38,6 +38,22 @@ local CONSTANT_DEFAULTS = { }, } +-- data/events/trades.asm in Pokemon Yellow has its own TradeMons table. +-- Old Yellow imports were built from the Red manifest, so correct that +-- table after loading as well as in the fixed import manifest below. +local YELLOW_TRADES = { + { give = "LICKITUNG", get = "DUGTRIO", dialogset = 1, nickname = "GURIO" }, + { give = "CLEFAIRY", get = "MR_MIME", dialogset = 1, nickname = "MILES" }, + { give = "BUTTERFREE", get = "BEEDRILL", dialogset = 3, nickname = "STINGER" }, + { give = "KANGASKHAN", get = "MUK", dialogset = 1, nickname = "STICKY" }, + { give = "MEW", get = "MEW", dialogset = 3, nickname = "BART" }, + { give = "TANGELA", get = "PARASECT", dialogset = 1, nickname = "SPIKE" }, + { give = "PIDGEOT", get = "PIDGEOT", dialogset = 2, nickname = "MARTY" }, + { give = "GOLDUCK", get = "RHYDON", dialogset = 2, nickname = "BUFFY" }, + { give = "GROWLITHE", get = "DEWGONG", dialogset = 3, nickname = "CEZANNE" }, + { give = "CUBONE", get = "MACHOKE", dialogset = 3, nickname = "RICKY" }, +} + -- field.boot is the total-conversion override point for the new game; the -- values match what SaveData.newGame and the Oak speech used to inline. local BOOT_DEFAULTS = { @@ -58,6 +74,12 @@ local function copy(value) return out end +function Data:applyVersionedFieldData() + if require("src.core.GameVersion").isYellow() then + self.field.trades = copy(YELLOW_TRADES) + end +end + -- Fills only what the cache is missing, so an importer that learns to -- stamp one of these keys silently takes over from the engine. function Data:seedDefaults() @@ -77,6 +99,7 @@ function Data:seedDefaults() if constants.dexDigits == nil then constants.dexDigits = math.max(3, #tostring(constants.dexSize)) end + self:applyVersionedFieldData() local boot = self.field.boot if boot == nil then boot = {} diff --git a/tests/parity_yellow_trades.lua b/tests/parity_yellow_trades.lua new file mode 100644 index 00000000..95e34bda --- /dev/null +++ b/tests/parity_yellow_trades.lua @@ -0,0 +1,59 @@ +-- Pokemon Yellow has a different TradeMons table from Red and Blue. +-- The import manifest must agree, and Data:seedDefaults repairs Yellow +-- caches made before that manifest carried the Yellow table (#453). + +package.path = "./?.lua;./?/init.lua;" .. package.path +if not _G.love then _G.love = require("tests.love_stub") end + +local Data = require("src.core.Data") +if not (Data.field and Data.field.trades) then Data:load() end +local GameVersion = require("src.core.GameVersion") +local S = require("tests.harness").suite("parity Yellow trades") +local check, eq = S.check, S.eq + +local oldVersion = GameVersion.get() +local oldTrades = {} +for i, trade in ipairs(Data.field.trades) do + oldTrades[i] = {} + for key, value in pairs(trade) do oldTrades[i][key] = value end +end + +GameVersion.set("yellow") +Data:applyVersionedFieldData() + +local expected = { + { "LICKITUNG", "DUGTRIO", "GURIO" }, + { "CLEFAIRY", "MR_MIME", "MILES" }, + { "BUTTERFREE", "BEEDRILL", "STINGER" }, + { "KANGASKHAN", "MUK", "STICKY" }, + { "MEW", "MEW", "BART" }, + { "TANGELA", "PARASECT", "SPIKE" }, + { "PIDGEOT", "PIDGEOT", "MARTY" }, + { "GOLDUCK", "RHYDON", "BUFFY" }, + { "GROWLITHE", "DEWGONG", "CEZANNE" }, + { "CUBONE", "MACHOKE", "RICKY" }, +} + +local manifestFile = assert(io.open("tools/rom_manifest_yellow.json", "r")) +local manifest = manifestFile:read("*a") +manifestFile:close() + +eq(#Data.field.trades, #expected, "Yellow has ten in-game trade rows") +for i, want in ipairs(expected) do + local got = Data.field.trades[i] + check(got.give == want[1] and got.get == want[2] and got.nickname == want[3], + ("Yellow trade %d is %s for %s (%s)"):format(i, want[1], want[2], want[3])) + local row = ('"get": "%s",%%s+"give": "%s",%%s+"nickname": "%s"') + :format(want[2], want[1], want[3]) + check(manifest:find(row) ~= nil, + ("Yellow manifest carries trade %d"):format(i)) +end + +local ricky = Data.field.trades[10] +check(ricky.give == "CUBONE" and ricky.get == "MACHOKE", + "the Underground Path trade is Cubone for Machoke") + +Data.field.trades = oldTrades +GameVersion.set(oldVersion) + +return S:finish() diff --git a/tests/run_tests.lua b/tests/run_tests.lua index 274789d1..0bf52a1c 100644 --- a/tests/run_tests.lua +++ b/tests/run_tests.lua @@ -3277,6 +3277,7 @@ runSuites(orderedGlob("tests/parity_*.lua", { "tests/parity_flavor.lua", "tests/parity_trainer_sight.lua", "tests/parity_static.lua", "tests/parity_trashcans.lua", "tests/parity_hof.lua", "tests/parity_trade_gift.lua", + "tests/parity_yellow_trades.lua", "tests/parity_intro.lua", "tests/parity_tilt.lua", "tests/parity_gbcfx.lua", })) diff --git a/tools/make_yellow_manifest.py b/tools/make_yellow_manifest.py index f1527e2b..aca456e9 100755 --- a/tools/make_yellow_manifest.py +++ b/tools/make_yellow_manifest.py @@ -382,6 +382,7 @@ def derive(red, pokeyellow, symbols_path): except SystemExit as exc: print(f"warning: parse_credits failed ({exc}); keeping Red credits") # TODO: hand-author a Yellow credits banner if pret layout drifts. + yellow["field"]["trades"] = field.parse_trades(pokeyellow) finally: util.ASM_DEFINES = saved diff --git a/tools/rom_manifest_yellow.json b/tools/rom_manifest_yellow.json index 9d565854..00c35a7a 100644 --- a/tools/rom_manifest_yellow.json +++ b/tools/rom_manifest_yellow.json @@ -10552,63 +10552,63 @@ "trades": [ { "dialogset": 1, - "get": "NIDORINA", - "give": "NIDORINO", - "nickname": "TERRY" + "get": "DUGTRIO", + "give": "LICKITUNG", + "nickname": "GURIO" }, { "dialogset": 1, "get": "MR_MIME", - "give": "ABRA", - "nickname": "MARCEL" + "give": "CLEFAIRY", + "nickname": "MILES" }, { "dialogset": 3, "get": "BEEDRILL", "give": "BUTTERFREE", - "nickname": "CHIKUCHIKU" + "nickname": "STINGER" }, { "dialogset": 1, - "get": "SEEL", - "give": "PONYTA", - "nickname": "SAILOR" + "get": "MUK", + "give": "KANGASKHAN", + "nickname": "STICKY" }, { "dialogset": 3, - "get": "FARFETCHD", - "give": "SPEAROW", - "nickname": "DUX" + "get": "MEW", + "give": "MEW", + "nickname": "BART" }, { "dialogset": 1, - "get": "LICKITUNG", - "give": "SLOWBRO", - "nickname": "MARC" + "get": "PARASECT", + "give": "TANGELA", + "nickname": "SPIKE" }, { "dialogset": 2, - "get": "JYNX", - "give": "POLIWHIRL", - "nickname": "LOLA" + "get": "PIDGEOT", + "give": "PIDGEOT", + "nickname": "MARTY" }, { "dialogset": 2, - "get": "ELECTRODE", - "give": "RAICHU", - "nickname": "DORIS" + "get": "RHYDON", + "give": "GOLDUCK", + "nickname": "BUFFY" }, { "dialogset": 3, - "get": "TANGELA", - "give": "VENONAT", - "nickname": "CRINKLES" + "get": "DEWGONG", + "give": "GROWLITHE", + "nickname": "CEZANNE" }, { "dialogset": 3, - "get": "NIDORAN_F", - "give": "NIDORAN_M", - "nickname": "SPOT" + "get": "MACHOKE", + "give": "CUBONE", + "nickname": "RICKY" } ], "warpCarpets": {