Open Routes 35 to 37, the National Park and Ecruteak to the Fog Badge (#32)
Enable Route 35, Route 36, Route 37, the National Park and its gates, Ecruteak City and its buildings, the Tin Tower entrance, the Wise Trio's room and both floors of the Burned Tower. The story runs from Floria, the SquirtBottle and Sudowoodo through the Kimono Girls' HM03 Surf, the rival in the Burned Tower and the beasts' release to Ecruteak Gym, Morty, the FOGBADGE and TM30. Port roaming Pokémon (InitRoamMons, UpdateRoamMons, JumpRoamMons, CheckEncounterRoamMon) with their state in the save; only Continue moves them on restore, so a failed import's rollback keeps them in place. Port the Bug-Catching Contest: registration on contest days, Park Balls, contestants, the timer, QUIT, the catch-and-swap prompt, judging from the ROM's contestant table, prizes and the saved contest state. Port the Game Corner's SlotMachine and CardFlip on a new realtime tile screen that advances two frames per tick with buffered buttons: reel strips and scrolling, bias tables, the seven streak, Golem and Chansey, payouts, the Card Flip board, cursor and navigation. Their tilemaps are checked against the ROM's own data. Also port getnum, a plain warp that continues its script on the new map, and PokemonNames' EGG entry. A trainer whose script is ObjectEvent (the Burned Tower rival) is no longer sighted once its own flag is set, as the original's read of bit 1 of wNumKeyItems gives with the usual key items. The playthrough walks through the National Park and continues to Morty; its driver chooses moves by type matchup, buys at Marts and uses Super Potions and Awakenings in trainer battles. New tests cover roaming, the contest, the Game Corner and Ecruteak's side content. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0930b780fc
commit
2640fa2725
31 files changed
+4743
-91
No files matched your search
@@ -9,6 +9,11 @@ const TEXTBOX_FRAME_TILES = 6,
|
||||
export const FRAME_TILE = 0x79,
|
||||
SPACE_TILE = 0x7f,
|
||||
FONT_TILE = 0x80;
|
||||
/** "…" ($75): the one _LoadFontsExtra1 glyph a ported screen needs
|
||||
* (_CardFlipDarnText's "Darn…"). _LoadFontsExtra1 loads 22 raw 2bpp tiles
|
||||
* from `FontExtra + 3 tiles` into vTiles2 tile $63 ('<BOLD_D>'); $75 is
|
||||
* the 18th of those, so tile 21 of FontExtra itself. */
|
||||
export const ELLIPSIS_TILE = 0x75;
|
||||
|
||||
/** Get1bpp: both bit planes copy the 1bpp row, so set pixels are color 3. */
|
||||
function oneBpp(target: Uint8Array, tile: number, source: Uint8Array) {
|
||||
@@ -29,6 +34,8 @@ export function fontTiles(reader: RomReader, frame: number) {
|
||||
oneBpp(tiles, SPACE_TILE, reader.slice(JOHTO.TextboxSpaceGFX, 8));
|
||||
for (let i = 0; i < 128; i++)
|
||||
oneBpp(tiles, FONT_TILE + i, reader.slice(JOHTO.Font + i * 8, 8));
|
||||
// _LoadFontsExtra1's ellipsis: raw 2bpp, unlike the standard font's 1bpp.
|
||||
tiles.set(reader.slice(JOHTO.FontExtra + 21 * 16, 16), ELLIPSIS_TILE * 16);
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@@ -59,6 +66,7 @@ const CODES: Record<string, number> = {
|
||||
"/": 0xf3,
|
||||
",": 0xf4,
|
||||
"♀": 0xf5,
|
||||
"…": ELLIPSIS_TILE,
|
||||
};
|
||||
/** Text as tile numbers, one character per tile. */
|
||||
export function textTiles(text: string): number[] {
|
||||
|
||||
@@ -92,6 +92,8 @@ const STAT_TRIGGERS = ["gt", "lt", "eq"] as const;
|
||||
* user's ROM on demand and cached in memory only. */
|
||||
/** UNOWN and NUM_UNOWN (constants/pokemon_constants.asm). */
|
||||
const UNOWN = 201;
|
||||
/** EGG (constants/pokemon_constants.asm). */
|
||||
export const EGG = 0xfd;
|
||||
export const NUM_UNOWN = 26;
|
||||
|
||||
export class PokemonData {
|
||||
@@ -128,9 +130,10 @@ export class PokemonData {
|
||||
return data;
|
||||
}
|
||||
|
||||
/** Fixed-length names are padded with "@"; a full-length name has none. */
|
||||
/** Fixed-length names are padded with "@"; a full-length name has none.
|
||||
* PokemonNames also names EGG, which GetPokemonName reads the same way. */
|
||||
speciesName(species: number): string {
|
||||
this.checkSpecies(species);
|
||||
if (species !== EGG) this.checkSpecies(species);
|
||||
return this.cachedName(`mon:${species}`, () => {
|
||||
const r = this.reader,
|
||||
p = SYMBOLS.pokemonNames + (species - 1) * 10;
|
||||
|
||||
@@ -188,6 +188,18 @@ export const EVENTS = {
|
||||
wallOpenedInKabutoChamber: 807,
|
||||
wallOpenedInHoOhChamber: 806,
|
||||
wallOpenedInOmanyteChamber: 808,
|
||||
/** BugCatchingContestantEventFlagTable: SelectRandomBugContestContestants
|
||||
* sets one of these for each of the five contestants a round excludes. */
|
||||
bugCatchingContestant1A: 1814,
|
||||
bugCatchingContestant2A: 1815,
|
||||
bugCatchingContestant3A: 1816,
|
||||
bugCatchingContestant4A: 1817,
|
||||
bugCatchingContestant5A: 1818,
|
||||
bugCatchingContestant6A: 1819,
|
||||
bugCatchingContestant7A: 1820,
|
||||
bugCatchingContestant8A: 1821,
|
||||
bugCatchingContestant9A: 1822,
|
||||
bugCatchingContestant10A: 1823,
|
||||
} as const;
|
||||
|
||||
/** Engine flag indices named directly by translated routines. */
|
||||
@@ -195,11 +207,14 @@ export const ENGINE_FLAGS = {
|
||||
pokegear: 4,
|
||||
pokedex: 11,
|
||||
allFruitTrees: 84,
|
||||
/** STATUSFLAGS2_BUG_CONTEST_TIMER_F is the same WRAM bit. */
|
||||
bugContestTimer: 17,
|
||||
} as const;
|
||||
|
||||
/** Overworld scripts, routines and tables, keyed by their PRET labels so
|
||||
* that `npm run check:reference` verifies each address by name. */
|
||||
export const OVERWORLD = {
|
||||
ObjectEvent: 0x26ef,
|
||||
NPCTrades: 0xfce58,
|
||||
TradeTexts: 0xfcf53,
|
||||
NPCTradeCableText: 0xfcf7b,
|
||||
@@ -207,6 +222,8 @@ export const OVERWORLD = {
|
||||
"TradedForText.done": 0xfcf92,
|
||||
BugCatchingContestBattleScript: 0x135eb,
|
||||
ContestMons: 0x97d87,
|
||||
InitRoamMons: 0x2a2a0,
|
||||
RoamMaps: 0x2a40f,
|
||||
"OverworldTownMap": 0xc2c0,
|
||||
"TownMapGFX": 0xf8ba0,
|
||||
"JohtoMap": 0x91fff,
|
||||
@@ -335,6 +352,8 @@ export const OVERWORLD = {
|
||||
PlayMapMusic: 0x3cdf,
|
||||
SurfStartStep: 0x8379,
|
||||
UpdateTimePals: 0x485,
|
||||
LoadUsedSpritesGFX: 0x14209,
|
||||
RefreshSprites: 0x14168,
|
||||
WaitTileAnimation: 0xfc2fe,
|
||||
DoneTileAnimation: 0xfc2fb,
|
||||
StandingTileFrame8: 0xfc2ff,
|
||||
@@ -1133,6 +1152,83 @@ export const JOHTO = {
|
||||
BattleTowerAction: 0x170687,
|
||||
GameboyCheck: 0xc478,
|
||||
UnlockMysteryGift: 0x1050b9,
|
||||
CheckMobileAdapterStatusSpecial: 0x10630f,
|
||||
ClearBGPalettes: 0x31f3,
|
||||
UpdateSprites: 0x1ad2,
|
||||
GiveParkBalls: 0x135db,
|
||||
SelectRandomBugContestContestants: 0x139a8,
|
||||
BugContestantPointers: 0x13783,
|
||||
ContestDropOffMons: 0x13a12,
|
||||
ContestReturnMons: 0x13a31,
|
||||
CheckPartyFullAfterContest: 0x4d9e5,
|
||||
BugContestJudging: 0xc34a,
|
||||
BugCatchingContestOverScript: 0x135f8,
|
||||
BugCatchingContestReturnToGateScript: 0x1360b,
|
||||
ContestJudging_FirstPlaceText: 0x136eb,
|
||||
ContestJudging_FirstPlaceScoreText: 0x136fd,
|
||||
ContestJudging_SecondPlaceText: 0x13702,
|
||||
ContestJudging_SecondPlaceScoreText: 0x13714,
|
||||
ContestJudging_ThirdPlaceText: 0x13719,
|
||||
ContestJudging_ThirdPlaceScoreText: 0x1372b,
|
||||
"StartMenu_Quit.StartMenuContestEndText": 0x12906,
|
||||
"StartMenu_PrintBugContestStatus.CaughtString": 0x24c4b,
|
||||
"StartMenu_PrintBugContestStatus.BallsString": 0x24c52,
|
||||
"StartMenu_PrintBugContestStatus.NoneString": 0x24c59,
|
||||
"StartMenu_PrintBugContestStatus.LevelString": 0x24c5e,
|
||||
|
||||
// Game Corner: the slot machine and Card Flip (engine/games/*, both
|
||||
// reached through StartGameCornerGame's farcall; CheckCoinsAndCoinCase's
|
||||
// refusal texts gate both games before either special even runs).
|
||||
SlotMachine: 0xc373,
|
||||
CardFlip: 0xc380,
|
||||
_NoCoinsText: 0x1bd3d7,
|
||||
_NoCoinCaseText: 0x1bd3eb,
|
||||
_SlotsBetHowManyCoinsText: 0x1c5049,
|
||||
_SlotsStartText: 0x1c505e,
|
||||
_SlotsNotEnoughCoinsText: 0x1c5066,
|
||||
_SlotsRanOutOfCoinsText: 0x1c5079,
|
||||
_SlotsPlayAgainText: 0x1c5092,
|
||||
_SlotsLinedUpText: 0x1c509f,
|
||||
_SlotsDarnText: 0x1c50bb,
|
||||
"Slots_InitBias.Normal": 0x93023,
|
||||
"Slots_InitBias.Lucky": 0x93031,
|
||||
"Slots_GetPayout.PayoutTable": 0x93145,
|
||||
Reel1Tilemap: 0x93327,
|
||||
Reel2Tilemap: 0x93339,
|
||||
Reel3Tilemap: 0x9334b,
|
||||
SlotsTilemap: 0x9335d,
|
||||
Slots1LZ: 0x9344d,
|
||||
Slots2LZ: 0x935cd,
|
||||
Slots3LZ: 0x9382d,
|
||||
SlotMachinePals: 0xb7a9,
|
||||
"SpriteAnimOAMData.OAMData_SlotsGolem": 0x8df29,
|
||||
"SpriteAnimOAMData.OAMData_SlotsChansey1": 0x8df42,
|
||||
"SpriteAnimOAMData.OAMData_SlotsChansey2": 0x8df5b,
|
||||
"SpriteAnimOAMData.OAMData_SlotsChansey3": 0x8df74,
|
||||
"SpriteAnimOAMData.OAMData_SlotsChansey4": 0x8df8d,
|
||||
"SpriteAnimOAMData.OAMData_SlotsChansey5": 0x8dfa6,
|
||||
"SpriteAnimOAMData.OAMData_1x1_Palette0": 0x8daf1,
|
||||
_CardFlipPlayWithThreeCoinsText: 0x1c5793,
|
||||
_CardFlipNotEnoughCoinsText: 0x1c57ab,
|
||||
_CardFlipChooseACardText: 0x1c57be,
|
||||
_CardFlipPlaceYourBetText: 0x1c57ce,
|
||||
_CardFlipPlayAgainText: 0x1c57df,
|
||||
_CardFlipShuffledText: 0x1c57f4,
|
||||
_CardFlipYeahText: 0x1c5813,
|
||||
_CardFlipDarnText: 0x1c581a,
|
||||
CardFlipOffButtonGFX: 0xe0cf6,
|
||||
CardFlipOnButtonGFX: 0xe0d06,
|
||||
CardFlipLZ01: 0xe0d16,
|
||||
CardFlipLZ02: 0xe0ea8,
|
||||
CardFlipLZ03: 0xe0cdb,
|
||||
CardFlipTilemap: 0xe110c,
|
||||
"CardFlip_DisplayCardFaceUp.Deck": 0xe0459,
|
||||
"CardFlip_DisplayCardFaceUp.FaceUpCardTilemap": 0xe043b,
|
||||
"PlaceCardFaceDown.FaceDownCardTilemap": 0xe03ce,
|
||||
"PlaceOAMCardBorder.SpriteData": 0xe0853,
|
||||
"CardFlip_UpdateCursorOAM.OAMData": 0xe0981,
|
||||
"CardFlip_InitAttrPals.palettes": 0xe0c93,
|
||||
FontExtra: 0xf8000,
|
||||
} as const;
|
||||
|
||||
/** Saved WRAM bytes that map scripts read and write directly (readmem,
|
||||
@@ -1148,6 +1244,10 @@ export type SavedByte = keyof typeof SAVED_WRAM;
|
||||
* PRET labels. */
|
||||
export const WRAM = {
|
||||
wTilemap: 0xc4a0,
|
||||
wBugContestWinnerName: 0xd016,
|
||||
wBugContestFirstPlaceScore: 0xd004,
|
||||
wBugContestSecondPlaceScore: 0xd008,
|
||||
wBugContestThirdPlaceScore: 0xd00c,
|
||||
} as const;
|
||||
|
||||
export const MAP_GROUP_COUNT = 26;
|
||||
@@ -1259,5 +1359,23 @@ export const NATIVE_MAPS: Record<string, string> = {
|
||||
"3:20": "Radio Tower 4F",
|
||||
"3:21": "Radio Tower 5F",
|
||||
"10:14": "Route 35 gate",
|
||||
"10:2": "Route 35",
|
||||
"10:3": "Route 36",
|
||||
"10:4": "Route 37",
|
||||
"4:1": "Tin Tower entrance",
|
||||
"4:2": "Wise Trio’s room",
|
||||
"4:3": "Pokémon Center",
|
||||
"4:4": "Lugia speech house",
|
||||
"4:5": "Dance Theater",
|
||||
"4:6": "Ecruteak Mart",
|
||||
"4:7": "Ecruteak Gym",
|
||||
"4:8": "Itemfinder house",
|
||||
"4:9": "Ecruteak City",
|
||||
"3:13": "Burned Tower 1F",
|
||||
"3:14": "Burned Tower B1F",
|
||||
"10:15": "Route 35 National Park gate",
|
||||
"10:17": "Route 36 National Park gate",
|
||||
"3:15": "National Park",
|
||||
"3:16": "National Park (Bug-Catching Contest)",
|
||||
};
|
||||
export const INITIAL_POSITION = { map: "24:7", x: 3, y: 3 } as const;
|
||||
Reference in new issue
Block a user