Port Fly, Dig, Teleport and the Escape Rope
FLY opens _FlyMap in the party menu: the region's Town Map with "Where?", the visited flypoints in turn and the Fly user's icon, then FlyScript flies the icon off along SpriteAnimFunc_FlyFrom's path, enters the spawn point and lands it with SpriteAnimFunc_FlyTo. Teleport returns to the last Pokémon Center's town through IsSpawnPoint. Dig and the Escape Rope lead out of a cave to the warp EnterMapWarp's SaveDigWarp recorded, which saves now keep. newloadmap stops its script as StopScript does and the world resumes it on the new map, which the landing and return movements need. A destination that is not ported yet gives a notice. Closes #25 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c4cc0e756c
commit
a7aceda089
21 files changed
+1166
-97
No files matched your search
@@ -13,6 +13,7 @@ import type { GameTime } from "../world/game-time";
|
||||
import { PartyEvolution, evoStoneCompatible } from "./evolution";
|
||||
import {
|
||||
copyScriptState,
|
||||
type FieldMoveContext,
|
||||
type MartEntry,
|
||||
type ScriptEffect,
|
||||
type ScriptHost,
|
||||
@@ -21,6 +22,8 @@ import {
|
||||
import { Pokegear } from "./pokegear";
|
||||
import { TrainerCard } from "./trainer-card";
|
||||
import { OptionsMenu, menuAccount } from "./options";
|
||||
import { flyMap } from "./fly-map";
|
||||
import { OW } from "../world/constants";
|
||||
import { readString, readText, type TextContext } from "./text";
|
||||
|
||||
type Input = number | string | undefined;
|
||||
@@ -86,19 +89,17 @@ export interface MenuCursors {
|
||||
|
||||
/** The world's verdict on a field move chosen from the party menu: a
|
||||
* script to run once the menu closes (with the context the world checked),
|
||||
* the messages of a failed attempt, or a notice for what is not ported. */
|
||||
* the messages of a failed attempt, a notice for what is not ported, or
|
||||
* Fly's map to choose a destination from. */
|
||||
export type MenuFieldMove =
|
||||
| {
|
||||
script: number;
|
||||
fieldMove: {
|
||||
mon: number;
|
||||
surfState?: number;
|
||||
block?: { x: number; y: number; block: number };
|
||||
};
|
||||
fieldMove: FieldMoveContext;
|
||||
wild?: { species: number; level: number };
|
||||
}
|
||||
| { texts: number[] }
|
||||
| { notice: string };
|
||||
| { notice: string }
|
||||
| { flyMap: true };
|
||||
|
||||
/** What the start menu needs from the world. */
|
||||
export interface StartMenuHost {
|
||||
@@ -114,6 +115,12 @@ export interface StartMenuHost {
|
||||
landmark(): number;
|
||||
/** A field move's checks for party slot `mon`, on the menu's draft. */
|
||||
fieldMove(state: ScriptState, mon: number, move: number): MenuFieldMove;
|
||||
/** FlyFunction after the Fly map: flying to SPAWN_* point `spawn`. */
|
||||
flyTo(state: ScriptState, mon: number, spawn: number): MenuFieldMove;
|
||||
/** EscapeRopeFunction's checks, on the menu's draft. */
|
||||
escapeRope(state: ScriptState): MenuFieldMove;
|
||||
/** HasVisitedSpawn: the wVisitedSpawns bit of a SPAWN_* point. */
|
||||
visitedSpawn(state: ScriptState, spawn: number): boolean;
|
||||
/** The script host a Pokégear phone call runs with. */
|
||||
scriptHost(): ScriptHost;
|
||||
/** GetWorldMapLocation for any map. */
|
||||
@@ -512,14 +519,36 @@ export class StartMenuRun {
|
||||
yield* this.softboiledMilkDrink(slot);
|
||||
return false;
|
||||
}
|
||||
const result = this.host.fieldMove(this.state, slot, move);
|
||||
let result = this.host.fieldMove(this.state, slot, move);
|
||||
if ("flyMap" in result) {
|
||||
// FlyFunction.TryFly: B on the map (.illegal) returns to the party
|
||||
// menu like a failure.
|
||||
const spawn = yield* flyMap(
|
||||
{
|
||||
reader: this.reader,
|
||||
text: () => this.text(),
|
||||
landmark: () => {
|
||||
const landmark = this.host.landmark();
|
||||
const backup = this.state.backupWarp?.map;
|
||||
return landmark === OW.LANDMARK_SPECIAL && backup
|
||||
? this.host.landmarkOf(backup)
|
||||
: landmark;
|
||||
},
|
||||
visited: (spawn) => this.host.visitedSpawn(this.state, spawn),
|
||||
},
|
||||
this.state.party[slot].species,
|
||||
);
|
||||
if (spawn < 0) return false;
|
||||
result = this.host.flyTo(this.state, slot, spawn);
|
||||
}
|
||||
if ("script" in result) {
|
||||
this.deferred = result.script;
|
||||
this.deferredMove = result;
|
||||
return true;
|
||||
}
|
||||
if ("notice" in result) yield* this.notice(result.notice);
|
||||
else for (const text of result.texts) yield* this.print(text);
|
||||
else if ("texts" in result)
|
||||
for (const text of result.texts) yield* this.print(text);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -893,6 +922,21 @@ export class StartMenuRun {
|
||||
return yield* this.vitaminEffect(use);
|
||||
case MENU.EvoStoneEffect:
|
||||
return yield* this.evoStoneEffect(use);
|
||||
case MENU.EscapeRopeEffect: {
|
||||
// EscapeRopeEffect: UseDisposableItem once EscapeRopeOrDig queued
|
||||
// its script; a failure prints nothing before Oak's message.
|
||||
const result = this.host.escapeRope(this.state);
|
||||
if ("notice" in result) {
|
||||
yield* this.notice(result.notice);
|
||||
return "unported";
|
||||
}
|
||||
if (!("script" in result)) return false;
|
||||
this.useDisposableItem(use);
|
||||
this.deferred = result.script;
|
||||
this.deferredItem = item;
|
||||
this.deferredMove = result;
|
||||
return true;
|
||||
}
|
||||
case MENU.BicycleEffect: {
|
||||
const script = this.host.bicycleScript(this.state, this.usingSelect);
|
||||
if (script === undefined) return false;
|
||||
|
||||
Reference in new issue
Block a user