mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-19 12:15:31 +02:00
G2 support
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
-- The Viridian Trainer House: the one battle a day against CAL in the
|
||||
-- basement's TRAINING HALL (maps/TrainerHouseB1F.asm).
|
||||
--
|
||||
-- The conversation itself is script bytecode and stays in the cache: the
|
||||
-- coord_event on the doorway cell runs TrainerHouseReceptionistScript, which
|
||||
-- checks ENGINE_FOUGHT_IN_TRAINER_HALL_TODAY, asks `special TrainerHouse`
|
||||
-- whose opponent it is, walks the player into the room and starts the battle.
|
||||
-- What that script needs from the port is the three compiled routines behind
|
||||
-- it, and all three are about the SAME question: whether a Mystery Gift trade
|
||||
-- has left a custom trainer in SRAM.
|
||||
--
|
||||
-- TrainerHouse engine/events/specials.asm -- reads
|
||||
-- sMysteryGiftTrainerHouseFlag into wScriptVar, so
|
||||
-- the script picks CAL2 (the visitor) or CAL3 (the
|
||||
-- house's own trainer).
|
||||
-- ReadTrainerParty engine/battle/read_trainer_party.asm -- CAL2 is
|
||||
-- the ONLY trainer in the game whose party does not
|
||||
-- come from data/trainers/parties.asm. Its `.cal2`
|
||||
-- arm reads sMysteryGiftTrainer as a
|
||||
-- TRAINERTYPE_MOVES party instead.
|
||||
-- GetTrainerName same file -- and CAL is the only class whose name
|
||||
-- is not read from the parties table either: with
|
||||
-- the flag set it is copied out of
|
||||
-- sMysteryGiftPartnerName.
|
||||
--
|
||||
-- MYSTERY GIFT IS OUT OF SCOPE (it is one of the six peripheral stubs in
|
||||
-- src/script/gen2/Specials.lua: the trade rides the Game Boy's infrared port
|
||||
-- into a second cartridge, and there is no second cartridge here). So the
|
||||
-- flag is permanently clear, which is exactly the state of a cartridge that
|
||||
-- has never been linked, and every one of the three routines above takes its
|
||||
-- own no-custom-data arm. That fallback is what this module ports:
|
||||
--
|
||||
-- * the script asks once before the walk-in and once at the battle, gets
|
||||
-- FALSE both times, and fights CAL3 -- MEGANIUM, TYPHLOSION and FERALIGATR
|
||||
-- at level 50, the strongest of the three CAL rows;
|
||||
-- * a CAL2 lookup that reaches here anyway is answered with CAL3 rather than
|
||||
-- with parties.asm's CAL (2) row, because that row is DEAD DATA on the
|
||||
-- cart: `ReadTrainerParty` branches to SRAM before it ever indexes the
|
||||
-- table, so handing back BAYLEEF/QUILAVA/CROCONAW at level 30 would be a
|
||||
-- team no cartridge ever fields;
|
||||
-- * the name is the parties table's own "CAL".
|
||||
--
|
||||
-- The once-a-day gate is not here: ENGINE_FOUGHT_IN_TRAINER_HALL_TODAY is a
|
||||
-- wDailyFlags1 bit like Kurt's, so the script's own setflag is the whole of
|
||||
-- the write and src/core/gen2/Apricorns.lua's daily reset is the whole of the
|
||||
-- clear. The id below is for readers and for the test that pins the pair.
|
||||
|
||||
local Trainers = require("src.world.gen2.Trainers")
|
||||
|
||||
local TrainerHouse = {}
|
||||
|
||||
-- constants/trainer_constants.asm: the CAL class and its three members. CAL1
|
||||
-- is the Route 27 battle, CAL2 the Mystery Gift visitor, CAL3 the house's own.
|
||||
TrainerHouse.CAL = 12
|
||||
TrainerHouse.CAL1, TrainerHouse.CAL2, TrainerHouse.CAL3 = 1, 2, 3
|
||||
|
||||
-- constants/engine_flags.asm index 86, wDailyFlags1 bit
|
||||
-- DAILYFLAGS1_FOUGHT_IN_TRAINER_HALL_TODAY. Cleared by
|
||||
-- Apricorns.dailyReset, which wipes both daily bytes whole.
|
||||
TrainerHouse.ENGINE_FOUGHT_IN_TRAINER_HALL_TODAY = 86
|
||||
|
||||
-- sMysteryGiftTrainerHouseFlag (ram/sram.asm), the byte a completed Mystery
|
||||
-- Gift trade leaves behind. STUB, and a deliberate one: nothing in this port
|
||||
-- can set it, because nothing in this port can run the infrared trade that
|
||||
-- writes it (engine/link/mystery_gift.asm). Kept as a function rather than as
|
||||
-- a constant `false` so the day Mystery Gift lands there is one place to teach
|
||||
-- about save.mysteryGift, and so the two readers below cannot drift apart.
|
||||
function TrainerHouse.hasCustomTrainer(save)
|
||||
local gift = type(save) == "table" and save.mysteryGift or nil
|
||||
return (gift and gift.trainerHouse) and true or false
|
||||
end
|
||||
|
||||
-- ReadTrainerParty's `cp CAL / cp CAL2` pair, as the question a caller with a
|
||||
-- class and a member can ask: which member should actually be loaded. Only
|
||||
-- CAL2 is ever redirected, and only when there is no custom trainer to redirect
|
||||
-- it to -- with one in SRAM the cart reads the party out of SRAM and this
|
||||
-- would have nothing to say about it either.
|
||||
function TrainerHouse.resolveMember(save, class, member)
|
||||
if class == TrainerHouse.CAL and member == TrainerHouse.CAL2
|
||||
and not TrainerHouse.hasCustomTrainer(save) then
|
||||
return TrainerHouse.CAL3
|
||||
end
|
||||
return member
|
||||
end
|
||||
|
||||
-- GetTrainerName's CAL arm. Returns the name the SRAM copy would have
|
||||
-- supplied, or nil for "fall through to the parties table", which is what
|
||||
-- `.not_cal2` does. nil rather than "CAL" on purpose: the caller already has
|
||||
-- the table, and inventing the answer here would hide a lookup that failed.
|
||||
function TrainerHouse.customName(save, class)
|
||||
if class ~= TrainerHouse.CAL then return nil end
|
||||
if not TrainerHouse.hasCustomTrainer(save) then return nil end
|
||||
local gift = save and save.mysteryGift
|
||||
return gift and gift.partnerName or nil
|
||||
end
|
||||
|
||||
-- The pair of lookups the World hands to the VM, with the CAL2 redirect
|
||||
-- applied. `trainerData` is the cache's trainers table.
|
||||
function TrainerHouse.lookup(trainerData, save, class, member)
|
||||
return Trainers.lookup(trainerData, class,
|
||||
TrainerHouse.resolveMember(save, class, member))
|
||||
end
|
||||
|
||||
function TrainerHouse.name(trainerData, save, class, member)
|
||||
local custom = TrainerHouse.customName(save, class)
|
||||
if custom then return custom end
|
||||
local entry = TrainerHouse.lookup(trainerData, save, class, member)
|
||||
return entry and entry.name or nil
|
||||
end
|
||||
|
||||
-- The daily gate, for a reader holding nothing but a save file. The script
|
||||
-- owns both sides of it in game (checkflag / setflag), so neither of these has
|
||||
-- a call site in the engine: they exist so the test can state the rule, and so
|
||||
-- a future rematch feature has one name for the bit rather than the number 86
|
||||
-- written out again.
|
||||
function TrainerHouse.foughtToday(save)
|
||||
local flags = type(save) == "table" and save.engineFlags or nil
|
||||
return (flags and flags[TrainerHouse.ENGINE_FOUGHT_IN_TRAINER_HALL_TODAY])
|
||||
== true
|
||||
end
|
||||
|
||||
return TrainerHouse
|
||||
Reference in New Issue
Block a user