Name the Battle Tower's ZABOROWSKI instead of refusing the battle

BattleTowerTrainers' names are ten bytes padded with "@" only when
shorter, and the cartridge copies the ten bytes to wOTPlayerName before
adding the terminator. The port read the name as an "@"-terminated
string, so drawing ZABOROWSKI (trainer 5) threw "Unterminated string."
and the challenge was refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
kawaandClaude Opus 5.5 committed 2026-10-17 16:06:28 +02:00
1 parent d3be732118
commit acdaf62eaa
3 files changed
+45 -3

No files matched your search

+4 -2
View File
@@ -2,7 +2,7 @@ import { JOHTO, RAM, WRAM } from "../rom/profile";
import { maxPP, validPartyMon, type PartyMon } from "../party/pokemon";
import type { PokemonData } from "../rom/pokemon";
import { DIRECTION_ORDER, type Direction } from "../world/types";
import { readString } from "./text";
import { readFixedString, readString } from "./text";
import type { Gen, SpecialContext } from "./special-context";
/** BATTLETOWER_* (constants/battle_tower_constants.asm). */
@@ -209,7 +209,9 @@ export function loadOpponent(c: SpecialContext, bt: BattleTowerState): BattleTow
const trainer = sample(c, BATTLE_TOWER.uniqueTrainers, (index) => bt.trainers.includes(index));
bt.trainers[bt.beaten] = trainer;
const entry = JOHTO.BattleTowerTrainers + trainer * TRAINER_ENTRY;
const name = readString(r, entry, c.text(), TRAINER_ENTRY - 1);
// Copied to wOTPlayerName with a "@" after its ten bytes: ZABOROWSKI
// fills them.
const name = readFixedString(r, entry, c.text(), TRAINER_ENTRY - 1);
const trainerClass = r.byte(entry + TRAINER_ENTRY - 1);
// LoadRandomBattleTowerMon: wBT_OTMon1-3 start as species 0 and item $ff.
const species = [0, 0, 0],
+19
View File
@@ -153,6 +153,25 @@ function character(code: number, context: TextContext): string | undefined {
return CHARACTERS[code];
}
/** Decode a name of at most `length` characters, as CopyBytes followed by
* a "@" leaves it: one that fills its field has no terminator of its own. */
export function readFixedString(
reader: RomReader,
start: number,
context: TextContext,
length: number,
): string {
let output = "";
for (let p = start; p < start + length; p++) {
const code = reader.byte(p);
if (code === 0x50) break;
const value = character(code, context);
if (value === undefined) throw new UnsupportedText(p, code);
output += value;
}
return output;
}
/** Decode a plain "@"-terminated string such as a name or item label. */
export function readString(
reader: RomReader,