Open Route 31, Violet City and Sprout Tower (#31)
Enable Route 31 and its gate, Dark Cave's Violet entrance, Violet City with its buildings and Sprout Tower. Port the script commands they use: gettrainername, askforphonenumber (with AddPhoneNumber's room for Mom and Elm; addcellnum now reports failure as the original does), checkpokemail, loadmenu/_2dmenu/closewindow, giveegg and trade (NPCTrade, with wTradeFlags saved). Eggs keep their species with an egg flag, as wPartySpecies does. The pack's TM/HM counts receive, check and toss TMs and HMs, which are named TM01-HM07. The playthrough continues through Route 31 (with Mom's call), Violet City and Sprout Tower to Elder Li's Flash. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ec3d1cd5c4
commit
7909302db1
16 files changed
+585
-63
No files matched your search
+50
-1
@@ -11,6 +11,32 @@ import {
|
||||
SYMBOLS,
|
||||
} from "./profile";
|
||||
import { defaultTextContext, readString } from "../scripts/text";
|
||||
|
||||
/** The TM and HM items (constants/item_constants.asm): TM01-TM50, then
|
||||
* HM01-HM07, skipping the dummy ITEM_C3 and ITEM_DC. */
|
||||
export const TM01 = 0xbf,
|
||||
HM01 = 0xf3,
|
||||
NUM_TMS = 50,
|
||||
NUM_HMS = 7,
|
||||
NUM_TMS_HMS = NUM_TMS + NUM_HMS;
|
||||
const ITEM_C3 = 0xc3,
|
||||
ITEM_DC = 0xdc;
|
||||
/** GetTMHMNumber: TM01 is 1, HM01 is 51. */
|
||||
export function tmhmNumber(item: number): number {
|
||||
let value = item;
|
||||
if (item >= ITEM_C3) value--;
|
||||
if (item >= ITEM_DC) value--;
|
||||
return value - TM01 + 1;
|
||||
}
|
||||
/** GetNumberedTMHM: the item for a TM/HM number. */
|
||||
export function tmhmItem(number: number): number {
|
||||
let item = number + TM01 - 1;
|
||||
if (item >= ITEM_C3) item++;
|
||||
if (item >= ITEM_DC) item++;
|
||||
return item;
|
||||
}
|
||||
export const isTMHM = (item: number) =>
|
||||
item >= TM01 && item < HM01 + NUM_HMS && item !== ITEM_C3 && item !== ITEM_DC;
|
||||
import type { Palette } from "../world/types";
|
||||
|
||||
/** data/pokemon/base_stats: one 32-byte record per species. */
|
||||
@@ -131,10 +157,33 @@ export class PokemonData {
|
||||
}
|
||||
itemName(item: number): string {
|
||||
this.checkItem(item);
|
||||
if (isTMHM(item)) {
|
||||
// GetTMHMName: "TM" or "HM" and two digits; HMs count from 1.
|
||||
const number = tmhmNumber(item),
|
||||
hm = item >= HM01;
|
||||
return `${hm ? "HM" : "TM"}${String(hm ? number - NUM_TMS : number).padStart(2, "0")}`;
|
||||
}
|
||||
return this.cachedName(`item:${item}`, () =>
|
||||
this.listName(SYMBOLS.itemNames, item),
|
||||
);
|
||||
}
|
||||
/** GetTMHMMove: the move a TM/HM number teaches. */
|
||||
tmhmMove(number: number): number {
|
||||
if (!Number.isInteger(number) || number < 1 || number > NUM_TMS_HMS)
|
||||
throw new Error("Unknown TM or HM.");
|
||||
return this.reader.byte(SYMBOLS.tmhmMoves + number - 1);
|
||||
}
|
||||
/** CanLearnTMHMMove: the move's place in TMHMMoves (with the tutor
|
||||
* moves after the HMs) as a bit of the species' base TM/HM flags. */
|
||||
canLearnTMHM(species: number, move: number): boolean {
|
||||
this.checkSpecies(species);
|
||||
const r = this.reader;
|
||||
let index = 0;
|
||||
for (let p = SYMBOLS.tmhmMoves; r.byte(p); p++, index++)
|
||||
if (r.byte(p) === move)
|
||||
return Boolean(r.byte(SYMBOLS.baseData + (species - 1) * 32 + 24 + (index >> 3)) & (1 << (index & 7)));
|
||||
return false;
|
||||
}
|
||||
private cachedName(key: string, read: () => string) {
|
||||
let name = this.names.get(key);
|
||||
if (name === undefined) {
|
||||
@@ -144,7 +193,7 @@ export class PokemonData {
|
||||
return name;
|
||||
}
|
||||
private checkItem(item: number) {
|
||||
if (!Number.isInteger(item) || item < 1 || item > NUM_ITEMS)
|
||||
if (!Number.isInteger(item) || ((item < 1 || item > NUM_ITEMS) && !isTMHM(item)))
|
||||
throw new Error("Unknown item.");
|
||||
}
|
||||
/** ItemAttributes: price, held effect, parameter, property, pocket, menus. */
|
||||
|
||||
@@ -57,6 +57,8 @@ export const SYMBOLS = {
|
||||
pocketIsFullText: 0x97060,
|
||||
findItemInBallScript: 0x122ce,
|
||||
caughtAskNicknameText: 0x4db44,
|
||||
stringEgg: 0xe035,
|
||||
tmhmMoves: 0x1167a,
|
||||
nicknameStrings: 0x11780,
|
||||
rivalNameString: 0x117c3,
|
||||
rivalDefaultName: 0xc2b2,
|
||||
@@ -194,6 +196,11 @@ export const ENGINE_FLAGS = {
|
||||
/** Overworld scripts, routines and tables, keyed by their PRET labels so
|
||||
* that `npm run check:reference` verifies each address by name. */
|
||||
export const OVERWORLD = {
|
||||
NPCTrades: 0xfce58,
|
||||
TradeTexts: 0xfcf53,
|
||||
NPCTradeCableText: 0xfcf7b,
|
||||
TradedForText: 0xfcf80,
|
||||
"TradedForText.done": 0xfcf92,
|
||||
BugCatchingContestBattleScript: 0x135eb,
|
||||
ContestMons: 0x97d87,
|
||||
"OverworldTownMap": 0xc2c0,
|
||||
@@ -1028,5 +1035,18 @@ export const NATIVE_MAPS: Record<string, string> = {
|
||||
"26:8": "Evolution speech house",
|
||||
"26:9": "Berry house",
|
||||
"26:10": "Mr. Pokémon’s house",
|
||||
"26:2": "Route 31",
|
||||
"26:11": "Route 31 Violet gate",
|
||||
"3:78": "Dark Cave",
|
||||
"10:5": "Violet City",
|
||||
"10:6": "Violet Mart",
|
||||
"10:7": "Violet Gym",
|
||||
"10:8": "Earl’s Pokémon Academy",
|
||||
"10:9": "Nickname speech house",
|
||||
"10:10": "Pokémon Center",
|
||||
"10:11": "Kyle’s house",
|
||||
"3:1": "Sprout Tower 1F",
|
||||
"3:2": "Sprout Tower 2F",
|
||||
"3:3": "Sprout Tower 3F",
|
||||
};
|
||||
export const INITIAL_POSITION = { map: "24:7", x: 3, y: 3 } as const;
|
||||
Reference in new issue
Block a user