feat: Rollback steps back through the last 50 interactions (wheel up, PageUp, Rollback action)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
mrleo1nidandClaude Sonnet 5.5 committed 2026-10-27 01:48:56 +03:00
1 parent ea351e44b3
commit 7652f7d010
2 files changed
+28 -2

No files matched your search

+25 -1
View File
@@ -48,6 +48,9 @@ function fontGroup() {
return group;
}
/** How many interactions Rollback can step back through. */
const ROLLBACK_LIMIT = 50;
export class Engine {
private liveImages?: LiveImages;
private imageScales = new Map<string, number>();
@@ -83,6 +86,8 @@ export class Engine {
private windowShown = true;
private lastSay: { name: string; text: string; color?: string; char?: CharacterDef } | null = null;
private history: { name: string; text: string }[] = [];
/** One state per interaction, newest last: what Rollback steps back through. */
private rollbackLog: { snap: SaveData; history: { name: string; text: string }[] }[] = [];
private nvlLines: HTMLElement[] = [];
private interactions = 0;
@@ -1061,6 +1066,7 @@ export class Engine {
quit: () => this.onExit(),
mainMenu: () => this.restart(() => { this.stack = []; this.cur = { label: "", path: [0] }; }),
openMenu: () => this.openGameMenu(),
rollback: () => this.rollback(),
openUrl: (url) => {
if (/^https?:\/\//i.test(url)) window.open(url, "_blank", "noopener,noreferrer");
},
@@ -1531,11 +1537,24 @@ export class Engine {
private interactionStart() {
this.renderer.commit();
this.rollbackLog.push({ snap: this.snapshotState(), history: this.history.slice() });
if (this.rollbackLog.length > ROLLBACK_LIMIT) this.rollbackLog.shift();
this.interactions++;
if (this.interactions % 15 === 0) void this.save("auto", true);
this.preloadAhead();
}
/** Back to the previous interaction: the one being shown is dropped, the one before it is shown again. */
private rollback() {
if (!this.running || this.rollbackLog.length < 2) return;
this.rollbackLog.pop();
const entry = this.rollbackLog.pop()!;
this.restart(() => {
this.applySave(entry.snap);
this.history = entry.history;
});
}
private preloadAhead() {
const block = this.labels.has(this.cur.label) ? this.blockFor(this.cur) : [];
const start = this.cur.path[this.cur.path.length - 1];
@@ -1878,6 +1897,7 @@ export class Engine {
this.newStore();
this.stack = [];
this.history = [];
this.rollbackLog = [];
this.lastSay = null;
this.textbox.classList.add("pg-hidden");
this.cur = at ?? { label: this.game.entry, path: [0] };
@@ -1973,6 +1993,7 @@ export class Engine {
}
if (!data.revision && !(await this.confirmAsk("Старое сохранение не содержит ревизию игры. После изменения скрипта его позиции могут быть неверны. Попробовать загрузить?", "Загрузить"))) return;
this.replayStash = null;
this.rollbackLog = [];
this.restart(() => this.applySave(data));
}
@@ -2123,7 +2144,7 @@ export class Engine {
this.advanceInput();
});
stage.addEventListener("wheel", (ev) => {
if (ev.deltaY < 0 && this.screen.classList.contains("pg-hidden") && this.running) this.openHistory();
if (ev.deltaY < 0 && this.screen.classList.contains("pg-hidden") && this.running) this.rollback();
}, { passive: true });
}
@@ -2203,6 +2224,9 @@ export class Engine {
this.setSkip(!this.skip);
} else if (ev.key === "a" || ev.key === "ф") {
this.setAuto(!this.auto);
} else if (ev.key === "PageUp") {
ev.preventDefault();
this.rollback();
} else if (ev.key === "h" || ev.key === "р") {
this.textbox.classList.toggle("pg-ui-hidden");
}
+3 -1
View File
@@ -43,6 +43,8 @@ export interface ScreenHost {
quit(): void;
mainMenu(): void;
openMenu(): void;
/** The Rollback action: back to the previous interaction. */
rollback?(): void;
openUrl(url: string): void;
quickSave(): void;
quickLoad(): void;
@@ -1797,7 +1799,7 @@ export class ScreenManager {
case "UploadSync":
case "DownloadSync": this.host.notify("Синхронизация недоступна в браузере"); return;
case "Skip": this.host.setSkipping?.(true); return;
case "Rollback": return; // there is no rollback in the browser player
case "Rollback": this.host.rollback?.(); return;
case "ToggleLocalVariable": {
const now = owner?.vars.get(a);
owner?.vars.set(a, !truthy(now));