Merge pull request #2398 from thibautbus/fix/game3-trainer-class-ids

Recognise FireRed trainer classes by id, not by their name
This commit is contained in:
bryanthaboi
2026-09-22 20:12:50 -04:00
committed by GitHub
8 changed files with 105 additions and 21 deletions
+2
View File
@@ -534,10 +534,12 @@ function Battle.start(opts)
}))
st.trainerId = trainerId
st.trainerClass = trainerInfo and tonumber(trainerInfo.class)
st.trainerClassName = trainerInfo and trainerInfo.className
st.trainerName = (trainerInfo and trainerInfo.name) or opts.trainerName
-- pokefirered/src/battle_message.c:394 the link opponent is named, never classed
if st.link and not st.unionRoom and st.peerName then
st.trainerClass = nil
st.trainerClassName = ""
st.trainerName = st.peerName
end
+3
View File
@@ -480,6 +480,9 @@ function BattleBridge.start(mod, game, foe, opts)
playerLevel = playerLv,
enemyLevel = foeLv,
trainerId = startOpts.trainerId,
trainerClass = (not opts.wild) and foe and foe.trainerClass or nil,
trainerTower = startOpts.trainerTower,
eReader = startOpts.eReader,
playerGender = startOpts.playerGender,
transitionId = opts.transitionId,
}
+25 -8
View File
@@ -121,19 +121,36 @@ function BattleTransition.pickWild(opts)
end
end
-- pokefirered/include/constants/trainers.h:270, :273
local TRAINER_CLASS_ELITE_FOUR = 87
local TRAINER_CLASS_CHAMPION = 90
-- pokefirered/include/constants/opponents.h:416-419, :741-744 (first run, rematch)
local ELITE_FOUR_TRANSITION = {
[410] = ID.LORELEI, [735] = ID.LORELEI,
[411] = ID.BRUNO, [736] = ID.BRUNO,
[412] = ID.AGATHA, [737] = ID.AGATHA,
[413] = ID.LANCE, [738] = ID.LANCE,
}
-- pokefirered/src/battle_setup.c:624 GetTrainerBattleTransition: the Elite Four
-- and the champion are recognised by class id, never by the class's name.
function BattleTransition.pickTrainer(opts)
opts = opts or {}
local tid = tonumber(opts.trainerId) or 0
local tClass = opts.trainerClass
-- A Trainer Tower or e-Reader foe carries a facility class, whose numbers
-- overlap the trainer classes (FACILITY_CLASS_LASS is 90, the champion's,
-- pokefirered/include/constants/trainers.h:381); pret never picks their
-- transition by class (battle_setup.c:660).
local tClass = not (opts.trainerTower or opts.eReader) and tonumber(opts.trainerClass) or nil
if tClass == "ELITE_FOUR" or tClass == 57 then
if tid == 412 or tid == 413 or opts.isLorelei then return ID.LORELEI end
if tid == 414 or tid == 415 or opts.isBruno then return ID.BRUNO end
if tid == 416 or tid == 417 or opts.isAgatha then return ID.AGATHA end
if tid == 418 or tid == 419 or opts.isLance then return ID.LANCE end
return ID.BLUE
if tClass == TRAINER_CLASS_ELITE_FOUR then
if opts.isLorelei then return ID.LORELEI end
if opts.isBruno then return ID.BRUNO end
if opts.isAgatha then return ID.AGATHA end
if opts.isLance then return ID.LANCE end
return ELITE_FOUR_TRANSITION[tid] or ID.BLUE
end
if tClass == "CHAMPION" or tClass == "RIVAL" or tClass == 58 or opts.isRival or opts.isChampion then
if tClass == TRAINER_CLASS_CHAMPION or opts.isRival or opts.isChampion then
return ID.BLUE
end
if opts.isLorelei then return ID.LORELEI end
+6 -4
View File
@@ -91,12 +91,14 @@ function R.battle(session,st)
local outcome=hp>=math.floor(max/3)*2 and 'Handily' or (hp>=math.floor(max/3) and 'Tenaciously' or 'Somehow')
local args={D0=loc,D1=st.trainerName or 'TRAINER',D2=enemy,D3=player,D4={text=outcome}}
local key='TookOnTrainersMonWithMonAndWon'
local class=st.trainerClassName or ''
if class=='LEADER' then key='TookOnGymLeadersMonWithMonAndWon'
elseif class=='ELITE FOUR' then
-- pokefirered/src/quest_log_battle.c:25 switches on the class id, which a
-- mod renaming the class leaves alone (include/constants/trainers.h:267-273)
local class=tonumber(st.trainerClass)
if class==84 then key='TookOnGymLeadersMonWithMonAndWon'
elseif class==87 then
key='TookOnEliteFoursMonWithMonAndWon'
args={D0=st.trainerName,D1=enemy,D2=player,D3={text=outcome}}
elseif class=='CHAMPION' then
elseif class==90 then
key='PlayerBattledChampionRival';args={D0=session.name,D1=st.trainerName}
end
R.event(session,key,args)
+12 -2
View File
@@ -8,6 +8,11 @@ local SPECIES_BULBASAUR = 1
local SPECIES_CHARMANDER = 4
local SPECIES_SQUIRTLE = 7
-- pokefirered/include/constants/trainers.h:264, :272, :273
local TRAINER_CLASS_RIVAL_EARLY = 81
local TRAINER_CLASS_RIVAL_LATE = 89
local TRAINER_CLASS_CHAMPION = 90
local TRAINER_RIVAL_OAKS_LAB_SQUIRTLE = 326
local TRAINER_RIVAL_OAKS_LAB_BULBASAUR = 327
local TRAINER_RIVAL_OAKS_LAB_CHARMANDER = 328
@@ -263,7 +268,8 @@ function Trainers.foeFromId(trainerId)
end
--- ROM-derived trainer presentation info (class / name / pic / partySize / dialogs).
-- opts.rivalName replaces placeholder "TERRY" for class RIVAL when provided.
-- opts.rivalName replaces the placeholder "TERRY" of the rival and champion
-- classes when provided.
function Trainers.info(trainerId, opts)
opts = opts or {}
trainerId = tonumber(trainerId)
@@ -289,7 +295,11 @@ function Trainers.info(trainerId, opts)
dialogs = t.dialogs,
}
if info.className == "RIVAL" and opts.rivalName and opts.rivalName ~= "" then
-- pokefirered/src/battle_message.c:2078 names these classes by the player's
-- rival, recognised by class id: a mod may rename the class itself.
local class = tonumber(info.class)
if (class == TRAINER_CLASS_RIVAL_EARLY or class == TRAINER_CLASS_RIVAL_LATE
or class == TRAINER_CLASS_CHAMPION) and opts.rivalName and opts.rivalName ~= "" then
info.name = opts.rivalName
end
return info