Ring with trainers' calls, rematch them and play the radio (Closes #30)
Checks / web (push) Successful in 2m38s
Private ROM checks / private-rom (push) Successful in 20m37s

Every registered trainer's callee and caller scripts run, with the
missing specials and commands (RandomPhoneWildMon, RandomUnseenWildMon,
swarms, gettrainerclassname, Kenji's break, the Blue Card and Buena's
password variables). Fight counts are saved, so rematches and gifts work
on every open map, and the daily reset clears the phone's flags.

Random calls follow CheckPhoneCall and the 20/10/5/3-minute call timer;
received calls ring twice with the caller's name, and calls play their
ringing, hang-up and no-signal sounds. Grass and fishing swarms use the
swarm tables.

The radio ports PlayRadioShow and every station frame by frame, with its
music: the Pokégear's radio card tunes by knob, house radios play through
MapRadio, and Buena's show takes the day's password for Blue Card points
that her receptionist exchanges for prizes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
kawaandClaude Opus 5.5 committed 2026-10-16 11:10:02 +02:00
1 parent 7f30c3f450
commit 7c12c81a27
21 files changed
+2021 -64

No files matched your search

+21 -3
View File
@@ -7,6 +7,7 @@ import {
NUM_ENGINE_FLAGS,
NUM_SPRITEMOVEDATA,
OVERWORLD,
PHONE,
RAM,
SYMBOLS,
} from "./profile";
@@ -387,9 +388,15 @@ export class CrystalAssets {
grassWildMons(
id: string,
kanto = false,
/** SwarmGrassWildMons in place of the region's table. */
swarm = false,
): { rates: number[]; slots: { level: number; species: number }[][] } | undefined {
const r = this.reader;
const table = kanto ? OVERWORLD.KantoGrassWildMons : SYMBOLS.johtoGrassWildMons;
const table = swarm
? PHONE.SwarmGrassWildMons
: kanto
? OVERWORLD.KantoGrassWildMons
: SYMBOLS.johtoGrassWildMons;
for (let p = table, i = 0; i < 256; i++, p += 47) {
const group = r.byte(p);
if (group === 0xff) return undefined;
@@ -410,9 +417,15 @@ export class CrystalAssets {
waterWildMons(
id: string,
kanto = false,
/** SwarmWaterWildMons in place of the region's table. */
swarm = false,
): { rate: number; slots: { level: number; species: number }[] } | undefined {
const r = this.reader;
const table = kanto ? OVERWORLD.KantoWaterWildMons : OVERWORLD.JohtoWaterWildMons;
const table = swarm
? PHONE.SwarmWaterWildMons
: kanto
? OVERWORLD.KantoWaterWildMons
: OVERWORLD.JohtoWaterWildMons;
for (let p = table, i = 0; i < 256; i++, p += 9) {
const group = r.byte(p);
if (group === 0xff) return undefined;
@@ -434,7 +447,12 @@ export class CrystalAssets {
flags: number[] = [];
for (let index = 0; index < NUM_ENGINE_FLAGS; index++) {
const address = r.word(SYMBOLS.engineFlags + index * 3);
if (address >= RAM.dailyFlags1 && address <= RAM.unusedDailyFlag)
// CheckDailyResetTimer: wDailyFlags1 to wUnusedDailyFlag, then the
// rematch, phone item and phone time-of-day flags (4 bytes each).
if (
(address >= RAM.dailyFlags1 && address <= RAM.unusedDailyFlag) ||
(address >= RAM.dailyRematchFlags && address < RAM.dailyPhoneTimeOfDayFlags + 4)
)
flags.push(index);
}
return flags;
+167
View File
@@ -154,6 +154,8 @@ export const RAM = {
curFruit: 0xd03f,
dailyFlags1: 0xdc1e,
unusedDailyFlag: 0xdc21,
dailyRematchFlags: 0xdc4c,
dailyPhoneTimeOfDayFlags: 0xdc54,
itemQuantityChange: 0xd10c,
movementBuffer: 0xd007,
hiddenItemID: 0xd040,
@@ -211,6 +213,12 @@ export const ENGINE_FLAGS = {
bugContestTimer: 17,
rocketsInRadioTower: 19,
rocketsInMahogany: 23,
expnCard: 3,
rocketSignal: 14,
fishSwarm: 82,
buenasPassword: 95,
dunsparceSwarm: 160,
yanmaSwarm: 161,
} as const;
/** Overworld scripts, routines and tables, keyed by their PRET labels so
@@ -1065,6 +1073,130 @@ export const OPTIONS = {
"Options_MenuAccount.On": 0xe44f6,
} as const;
/** The phone's calls and their specials (engine/phone/phone.asm,
* engine/overworld/wildmons.asm, engine/events/specials.asm), keyed by
* their PRET labels. */
export const PHONE = {
ActivateFishingSwarm: 0xc3fc,
MapRadio: 0xc355,
InitCallReceiveDelay: 0x113e5,
RandomUnseenWildMon: 0x2a4ab,
"RandomUnseenWildMon.JustSawSomeRareMonText": 0x2a51a,
RandomPhoneWildMon: 0x2a51f,
JohtoGrassWildMons: 0x2a5e9,
KantoGrassWildMons: 0x2b274,
SwarmGrassWildMons: 0x2b8d0,
SwarmWaterWildMons: 0x2b92f,
Script_ReceivePhoneCall: 0x90241,
RingTwice_StartCall: 0x9026f,
HangUp: 0x902eb,
AskRememberPassword: 0x4ae12,
BuenasPassword: 0x8af6b,
BuenaPrize: 0x8afd4,
"BuenaPrize.BuenaAskWhichPrizeText": 0x8b072,
"BuenaPrize.BuenaIsThatRightText": 0x8b077,
"BuenaPrize.BuenaHereYouGoText": 0x8b07c,
"BuenaPrize.BuenaNotEnoughPointsText": 0x8b081,
"BuenaPrize.BuenaNoRoomText": 0x8b086,
"BuenaPrize.BuenaComeAgainText": 0x8b08b,
"PrintBlueCardBalance.Points_string": 0x8b0ca,
BuenaPrizeItems: 0x8b15e,
} as const;
/** The radio (engine/pokegear/radio.asm, pokegear.asm's stations):
* its texts and tables, keyed by their PRET labels. */
export const RADIO = {
OPT_IntroText1: 0xb8820,
OPT_IntroText2: 0xb8825,
OPT_IntroText3: 0xb882a,
OPT_OakText1: 0xb882f,
OPT_OakText2: 0xb8834,
OPT_OakText3: 0xb8839,
OPT_MaryText1: 0xb884f,
"OaksPKMNTalk8.Adverbs": 0xb8869,
"OaksPKMNTalk9.Adjectives": 0xb88fe,
OPT_PokemonChannelText: 0xb898e,
OaksPKMNTalkRoutes: 0xb87f2,
"OaksPKMNTalkRoutes.End": 0xb8810,
PokedexShowText: 0xb8b30,
BenIntroText1: 0xb8baa,
BenIntroText2: 0xb8baf,
BenIntroText3: 0xb8bb4,
FernIntroText1: 0xb8bb9,
FernIntroText2: 0xb8bbe,
BenFernText1: 0xb8bc3,
BenFernText2A: 0xb8bc8,
BenFernText2B: 0xb8bcd,
BenFernText3A: 0xb8bd2,
BenFernText3B: 0xb8bd7,
LC_Text1: 0xb8c7e,
LC_Text2: 0xb8c83,
LC_Text3: 0xb8c88,
LC_Text4: 0xb8c8d,
LC_Text5: 0xb8c92,
LC_Text6: 0xb8c97,
LC_Text7: 0xb8c9c,
LC_Text8: 0xb8ca1,
LC_Text9: 0xb8ca6,
LC_Text10: 0xb8cab,
LC_Text11: 0xb8cb0,
LC_DragText1: 0xb8cb5,
LC_DragText2: 0xb8cba,
PnP_Text1: 0xb8ce3,
PnP_Text2: 0xb8ce8,
PnP_Text3: 0xb8ced,
PnP_Text4: 0xb8d51,
PnP_Text5: 0xb8e23,
"PeoplePlaces5.Adjectives": 0xb8d7d,
"PeoplePlaces7.Adjectives": 0xb8e52,
PnP_HiddenPeople: 0xb8d3e,
PnP_HiddenPeople_BeatE4: 0xb8d43,
PnP_HiddenPeople_BeatKanto: 0xb8d4b,
PnP_Places: 0xb8e11,
"PnP_Places.End": 0xb8e23,
RocketRadioText1: 0xb8ec5,
RocketRadioText2: 0xb8eca,
RocketRadioText3: 0xb8ecf,
RocketRadioText4: 0xb8ed4,
RocketRadioText5: 0xb8ed9,
RocketRadioText6: 0xb8ede,
RocketRadioText7: 0xb8ee3,
RocketRadioText8: 0xb8ee8,
RocketRadioText9: 0xb8eed,
RocketRadioText10: 0xb8ef2,
BuenaRadioText1: 0xb9182,
BuenaRadioText2: 0xb9187,
BuenaRadioText3: 0xb918c,
BuenaRadioText4: 0xb9191,
BuenaRadioText5: 0xb9196,
BuenaRadioText6: 0xb919b,
BuenaRadioText7: 0xb91a0,
BuenaRadioMidnightText1: 0xb91a5,
BuenaRadioMidnightText2: 0xb91aa,
BuenaRadioMidnightText3: 0xb91af,
BuenaRadioMidnightText4: 0xb91b4,
BuenaRadioMidnightText5: 0xb91b9,
BuenaRadioMidnightText6: 0xb91be,
BuenaRadioMidnightText7: 0xb91c3,
BuenaRadioMidnightText8: 0xb91c8,
BuenaRadioMidnightText9: 0xb91cd,
BuenaRadioMidnightText10: 0xb91d2,
BuenaOffTheAirText: 0xb91d7,
BuenasPasswordTable: 0xb8ff9,
BuenasPasswordChannelName: 0xb9171,
RadioChannelSongs: 0xb920b,
RadioChannels: 0x916ad,
OaksPKMNTalkName: 0x918bf,
PokedexShowName: 0x918cc,
PokemonMusicName: 0x918d6,
LuckyChannelName: 0x918e1,
UnownStationName: 0x918ef,
PlacesAndPeopleName: 0x918f5,
LetsAllSingName: 0x91905,
PokeFluteStationName: 0x91914,
BuenasPasswordName: 0x917c3,
} as const;
/** The sound engine (audio/engine.asm) and its tables, keyed by their
* PRET labels. */
export const AUDIO = {
@@ -1329,6 +1461,41 @@ export const JOHTO = {
* writemem, loadmem and readvar), keyed by their PRET labels; the native
* save keeps them by label in `memory`. */
export const SAVED_WRAM = {
wJackFightCount: 0xd9f2,
wBeverlyFightCount: 0xd9f3,
wHueyFightCount: 0xd9f4,
wGavenFightCount: 0xd9f5,
wBethFightCount: 0xd9f6,
wJoseFightCount: 0xd9f7,
wReenaFightCount: 0xd9f8,
wJoeyFightCount: 0xd9f9,
wWadeFightCount: 0xd9fa,
wRalphFightCount: 0xd9fb,
wLizFightCount: 0xd9fc,
wAnthonyFightCount: 0xd9fd,
wToddFightCount: 0xd9fe,
wGinaFightCount: 0xd9ff,
wIrwinFightCount: 0xda00,
wArnieFightCount: 0xda01,
wAlanFightCount: 0xda02,
wDanaFightCount: 0xda03,
wChadFightCount: 0xda04,
wDerekFightCount: 0xda05,
wTullyFightCount: 0xda06,
wBrentFightCount: 0xda07,
wTiffanyFightCount: 0xda08,
wVanceFightCount: 0xda09,
wWiltonFightCount: 0xda0a,
wKenjiFightCount: 0xda0b,
wParryFightCount: 0xda0c,
wErinFightCount: 0xda0d,
wBuenasPassword: 0xdc4a,
wKenjiBreakTimer: 0xdc58,
wYanmaMapGroup: 0xdc5a,
wYanmaMapNumber: 0xdc5b,
wDunsparceMapGroup: 0xdfcc,
wDunsparceMapNumber: 0xdfcd,
wFishingSwarmFlag: 0xdfce,
wMooMooBerries: 0xd962,
wFarfetchdPosition: 0xd964,
wKurtApricornQuantity: 0xdca4,