From 1c2a7d64dec62836ec97e034b731bdd08aecd739 Mon Sep 17 00:00:00 2001 From: thibautbus <310327033+thibautbus@users.noreply.github.com> Date: Tue, 22 Sep 2026 15:20:31 +0200 Subject: [PATCH] Give the FireRed rival and champion the rival's name by class id Trainers.info gave a trainer the player's chosen rival name only when its class was called "RIVAL". A mod that translates the class (RIVALE in German and Italian) made the rival battle as the ROM's placeholder TERRY, and the champion, whose class is CHAMPION, was always TERRY. pret's B_TXT_TRAINER1_NAME names TRAINER_CLASS_RIVAL_EARLY, _RIVAL_LATE and _CHAMPION by the rival's name (pokefirered/src/battle_message.c:2078); Trainers.info now does the same, by class id. --- src/core/game3/scripting/trainers.lua | 14 ++++++++-- tests/engine/game3_trainer_class_ids_test.lua | 28 +++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 tests/engine/game3_trainer_class_ids_test.lua diff --git a/src/core/game3/scripting/trainers.lua b/src/core/game3/scripting/trainers.lua index 602971e3..35ce494f 100644 --- a/src/core/game3/scripting/trainers.lua +++ b/src/core/game3/scripting/trainers.lua @@ -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 diff --git a/tests/engine/game3_trainer_class_ids_test.lua b/tests/engine/game3_trainer_class_ids_test.lua new file mode 100644 index 00000000..6f76377b --- /dev/null +++ b/tests/engine/game3_trainer_class_ids_test.lua @@ -0,0 +1,28 @@ +#!/usr/bin/env luajit +-- The rival and the champion take the player's chosen rival name by class id, +-- as pret's B_TXT_TRAINER1_NAME does (pokefirered/src/battle_message.c:2078), +-- so a mod that translates the class ("RIVALE", "MAÎTRE") keeps the name. + +package.path = "./?.lua;./?/init.lua;" .. package.path +love = require("tests.love_stub") + +local T = require("tests.harness") +local check = T.check + +local Trainers = require("src.core.game3.scripting.trainers") +local rows = { + [326] = { id = 326, class = 81, className = "RIVALE", name = "TERRY", party = {} }, + [438] = { id = 438, class = 90, className = "MAÎTRE", name = "TERRY", party = {} }, + [500] = { id = 500, class = 89, className = "RIVAL", name = "TERRY", party = {} }, + [347] = { id = 347, class = 82, className = "RIVAL", name = "IVAN", party = {} }, +} +Trainers.get = function(id) return rows[tonumber(id)] end + +local function nameOf(id) return Trainers.info(id, { rivalName = "GARY" }).name end +check(nameOf(326) == "GARY", "the early rival takes the rival name, whatever its class is called") +check(nameOf(500) == "GARY", "so does the late rival") +check(nameOf(438) == "GARY", "and the champion, as on the cart") +check(nameOf(347) == "IVAN", "another class keeps its own trainer's name, even one called RIVAL") +check(Trainers.info(326, {}).name == "TERRY", "with no rival name, the ROM's placeholder stays") + +T.finish("game3_trainer_class_ids_test")