Open Route 29 and Cherrygrove City
Checks / web (push) Canceled after 0s

- Enable Route 29, its Route 46 gate, Cherrygrove City and its buildings.
- Run item balls through FindItemInBallScript and fruit trees through
  FruitTreeScript, with a native daily reset of the daily engine flags.
- Port the Pokémon Center nurse (HealParty with the PP Up clamp,
  CheckPokerus, phone-call checks) and record the whiteout spawn on
  entering a Pokémon Center, as EnterMapWarp does.
- Port standard Poké Marts with keyboard, D-pad and pointer menus.
- Add the recoil battle command (Struggle) and VAR_BOXSPACE.
- Save fruit trees, the daily reset day and the last spawn map; older
  version-3 backups migrate by absence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Sébastien QUEROLandClaude Opus 5.5 committed 2026-09-23 17:25:02 +02:00
1 parent 11d1153802
commit 796fe8f6d5
24 files changed
+1549 -59

No files matched your search

+40 -2
View File
@@ -75,6 +75,7 @@ const names = {
giveItemScript: "GiveItemScript",
putItemInPocketText: "PutItemInPocketText",
pocketIsFullText: "PocketIsFullText",
findItemInBallScript: "FindItemInBallScript",
caughtAskNicknameText: "CaughtAskNicknameText",
nicknameStrings: "NamingScreenJumptable.NicknameStrings",
wildBattleScript: "WildBattleScript",
@@ -83,12 +84,36 @@ const names = {
johtoGrassWildMons: "JohtoGrassWildMons",
grassMonProbTable: "GrassMonProbTable",
grassCollisions: "CheckGrassCollision.blocks",
fruitTreeScript: "FruitTreeScript",
fruitTreeItems: "FruitTreeItems",
engineFlags: "EngineFlags",
getCurTreeFruit: "GetCurTreeFruit",
tryResetFruitTrees: "TryResetFruitTrees",
checkFruitTree: "CheckFruitTree",
pickedFruitTree: "PickedFruitTree",
marts: "Marts",
martWelcomeText: "MartWelcomeText",
martAskMoreText: "MartAskMoreText",
martComeAgainText: "MartComeAgainText",
martHowManyText: "MartHowManyText",
martFinalPriceText: "MartFinalPriceText",
martNoMoneyText: "MartNoMoneyText",
martPackFullText: "MartPackFullText",
martThanksText: "MartThanksText",
martCantBuyText: "MartCantBuyText",
martSellHowManyText: "MartSellHowManyText",
martSellPriceText: "MartSellPriceText",
martBoughtText: "MartBoughtText",
fadeOutToWhite: "FadeOutToWhite",
healParty: "HealParty",
healMachineAnim: "HealMachineAnim",
checkPokerus: "CheckPokerus",
stubbedTrainerRankingsHealings: "StubbedTrainerRankings_Healings",
warpToSpawnPoint: "WarpToSpawnPoint",
battleBGMap: "BattleBGMap",
halveMoney: "HalveMoney",
getWhiteoutSpawn: "GetWhiteoutSpawn",
tryReceiveItem: "FindItemInBallScript.TryReceiveItem",
giveItemDummy: "GiveItemScript_DummyFunction",
restartMapMusic: "RestartMapMusic",
setDayOfWeek: "SetDayOfWeek",
@@ -97,12 +122,15 @@ const names = {
toggleDecorationsVisibility: "ToggleDecorationsVisibility",
toggleMaptileDecorations: "ToggleMaptileDecorations",
};
// Work RAM symbols are compared by their CPU address, not a ROM offset.
// Work and hardware RAM symbols are compared by their CPU address, not a
// ROM offset. Hardware RAM (hMoneyTemp and similar) lives at $FF80-$FFFE.
const ram = new Map(
readFileSync(`${root}/pokecrystal11.sym`, "utf8")
.split("\n")
.flatMap((line) => {
const match = /^0[01]:([c-d][0-9a-f]{3}) (\S+)$/.exec(line);
const match = /^0[01]:((?:[c-d][0-9a-f]{3}|ff[0-9a-f]{2})) (\S+)$/.exec(
line,
);
return match ? [[match[2], parseInt(match[1], 16)]] : [];
}),
);
@@ -119,6 +147,11 @@ const ramNames = {
enemyMonNickname: "wEnemyMonNickname",
curPartyLevel: "wCurPartyLevel",
monOrItemNameBuffer: "wMonOrItemNameBuffer",
curFruit: "wCurFruit",
dailyFlags1: "wDailyFlags1",
unusedDailyFlag: "wUnusedDailyFlag",
itemQuantityChange: "wItemQuantityChange",
moneyTemp: "hMoneyTemp",
};
const profile = readFileSync("src/game/rom/profile.ts", "utf8");
for (const [key, symbol] of Object.entries(names)) {
@@ -187,6 +220,7 @@ const constantCount = (file, name) => {
for (const [name, file] of [
["NUM_POKEMON", "constants/pokemon_constants.asm"],
["NUM_ITEMS", "constants/item_constants.asm"],
["NUM_FRUIT_TREES", "constants/script_constants.asm"],
])
if (!profile.includes(`${name} = ${constantCount(file, name)};`))
throw new Error(`${name} does not match the reference.`);
@@ -244,6 +278,10 @@ for (const [, block] of battleConstants.matchAll(/export const \w+ = \{([\s\S]*?
throw new Error(`Battle constant mismatch: ${name} is ${literal}, PRET has ${expected}`);
}
}
if (!profile.includes(`allFruitTrees: ${pretConstants.get("ENGINE_ALL_FRUIT_TREES")},`))
throw new Error("ENGINE_ALL_FRUIT_TREES does not match the reference.");
if (!profile.includes(`NUM_ENGINE_FLAGS = ${pretConstants.get("NUM_ENGINE_FLAGS")};`))
throw new Error("NUM_ENGINE_FLAGS does not match the reference.");
const groups = readFileSync(`${root}/data/maps/maps.asm`, "utf8")
.split(/^MapGroup_/m)
.slice(1)