Integrate battles with the overworld UI and validate browser flows
Checks / web (push) Canceled after 0s

- Drive wild battles through the real UI in Chromium and WebKit using a
  test-only encounter fixture: victory with level-up, escape, whiteout,
  mid-battle backup restore and unsupported-effect rollback.
- Add keyboard and on-screen D-pad navigation for battle menus.
- Accept pending stat experience in saves (awarded without CalcMonStats).
- Commit whiteout healing, money loss and destination callbacks together;
  aborted scripts restore state and map.
- Give long notices their own status-bar row on narrow screens.

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 16:37:07 +02:00
1 parent c08d250cf6
commit 11d1153802
11 files changed
+462 -13

No files matched your search

+22 -6
View File
@@ -143,7 +143,13 @@ interface ActiveScript {
effect?: ScriptEffect;
/** Committed save data for the whole interaction, used for mid-script saves. */
committed: NativeSave;
rollback: { player: Actor; actors: Map<number, Actor>; spawned: Set<number> };
rollback: {
player: Actor;
actors: Map<number, Actor>;
spawned: Set<number>;
state: ScriptState;
mapId: string;
};
waitTicks: number;
moving: Map<number, { steps: MovementStep[]; index: number; wait: number }>;
follow?: { leader: number; follower: number };
@@ -644,9 +650,13 @@ export class GameWorld {
/** Evaluate map callbacks on a copy, so an unsupported callback leaves the
* current map untouched. Methods follow the original map setup scripts. */
private prepareMap(id: string, method: "warp" | "connection" | "continue") {
private prepareMap(
id: string,
method: "warp" | "connection" | "continue",
sourceState = this.state,
) {
const map = this.assets.map(id);
const state = copyScriptState(this.state);
const state = copyScriptState(sourceState);
const actors = new Map<number, Actor>();
if (method !== "continue")
for (let event = 0; event < 8; event++) state.events.delete(event);
@@ -779,6 +789,8 @@ export class GameWorld {
),
committed,
rollback: {
state: this.state,
mapId: this.mapId,
player: copyActor(this.player),
actors: new Map(
[...this.actors].map(([id, actor]) => [id, copyActor(actor)]),
@@ -1045,10 +1057,12 @@ export class GameWorld {
private finishScript() {
const script = this.script!;
this.state = script.run.state;
if (script.warp) {
const { map, x, y } = script.warp;
const entry = this.prepareMap(map, "warp");
if (!this.assets.isPorted(map) || !this.walkable(this.assets.map(map), x, y))
throw new Error("Script warp destination is not supported.");
// Whiteout healing/money and destination callbacks commit together.
const entry = this.prepareMap(map, "warp", script.run.state);
this.player = {
...this.player,
x,
@@ -1057,7 +1071,7 @@ export class GameWorld {
motion: undefined,
};
this.applyMap(entry);
}
} else this.state = script.run.state;
this.script = undefined;
this.dialogue = undefined;
this.emote = undefined;
@@ -1071,6 +1085,8 @@ export class GameWorld {
const script = this.script!;
this.battle = undefined;
this.battleRun = undefined;
this.state = script.rollback.state;
this.mapId = script.rollback.mapId;
this.player = script.rollback.player;
this.actors = script.rollback.actors;
this.spawned = script.rollback.spawned;