feat: eval/exec of strings, gallery Replay, callable actions; iNSight and SomethingBetter now pass
- pyparse.ts: parser for expressions and simple statements built at run time, producing
the sandbox's own IR; backs eval() and exec() (e.g. exec("persistent." + var + " = 0"))
- Replay/EndReplay and renpy.end_replay: the engine stashes the full game state, runs the
label with the given scope and restores everything when it ends; locked replays are
insensitive. Save loading shared as applySave()
- Screen actions are callable (MainMenu(confirm=False)())
- jump main_menu_screen is Ren'Py's own label: it opens the player's main menu
- Screens: a closing quote ends the word before a letter ("text"size 15) but not before
an operator ("a"+b); Text(...) pictures in image blocks
- README: current capabilities and limits of the Ren'Py support
Strict compatibility check on the local collection: Tomie, Twisted-Thoughts, Hooked,
iNSight-of-you and SomethingBetter pass; TheFixer (custom AlphaMask wardrobe, missing
labels) and That_New_Teacher (custom statements, Render API) do not.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3923ee72fb
commit
3ac4f0371f
11 files changed
+448
-36
No files matched your search
@@ -38,6 +38,8 @@ export interface ScreenHost {
|
||||
openUrl(url: string): void;
|
||||
quickSave(): void;
|
||||
quickLoad(): void;
|
||||
startReplay(label: string, scope: Record<string, any> | null): void;
|
||||
endReplay(): void;
|
||||
setLanguage(code: string | null): void;
|
||||
transform(name: string): Record<string, any> | null;
|
||||
atl(name: string): AtlBlock | null;
|
||||
@@ -49,6 +51,8 @@ class ActionAbort extends Error {}
|
||||
|
||||
/** A screen action such as Jump("x"); run when the player activates a widget. */
|
||||
export class ScreenAction {
|
||||
/** Actions are callable in Ren'Py: `MainMenu(confirm=False)()` performs the action at once. */
|
||||
__call__: () => null = () => null;
|
||||
constructor(public kind: string, public args: any[], public kw: Props, public screen: Instance | null) {}
|
||||
}
|
||||
|
||||
@@ -1084,7 +1088,8 @@ export class ScreenManager {
|
||||
|
||||
private actionSensitive(action: any): boolean {
|
||||
const list = Array.isArray(action) ? action : [action];
|
||||
return list.every((a) => !(a instanceof ScreenAction) || a.kind !== "SensitiveIf" || truthy(a.args[0]));
|
||||
return list.every((a) => !(a instanceof ScreenAction) ||
|
||||
(a.kind === "SensitiveIf" ? truthy(a.args[0]) : a.kind === "Replay" ? !truthy(a.kw.locked ?? a.args[2]) : true));
|
||||
}
|
||||
|
||||
private actionSelected(action: any): boolean {
|
||||
@@ -1169,6 +1174,12 @@ export class ScreenManager {
|
||||
}
|
||||
case "SetLocalVariable":
|
||||
case "SetScreenVariable": owner?.vars.set(a, b); return;
|
||||
case "Replay": {
|
||||
const scope = action.kw.scope ?? b;
|
||||
this.host.startReplay(String(a), scope instanceof PyDict ? Object.fromEntries([...scope.map.entries()].map(([k, v]) => [String(k), v])) : null);
|
||||
throw new ActionAbort();
|
||||
}
|
||||
case "EndReplay": this.host.endReplay(); throw new ActionAbort();
|
||||
case "QuickSave": this.host.quickSave(); return;
|
||||
case "QuickLoad": this.host.quickLoad(); throw new ActionAbort();
|
||||
case "ToggleScreenVariable": {
|
||||
@@ -1217,13 +1228,18 @@ export class ScreenManager {
|
||||
const make = (kind: string) => (...args: any[]) => {
|
||||
const last = args[args.length - 1];
|
||||
const hasKw = last && typeof last === "object" && !Array.isArray(last) && Object.hasOwn(last, "__kw");
|
||||
return new ScreenAction(kind, hasKw ? args.slice(0, -1) : args, hasKw ? last.__kw : {}, this.rendering);
|
||||
const action = new ScreenAction(kind, hasKw ? args.slice(0, -1) : args, hasKw ? last.__kw : {}, this.rendering);
|
||||
action.__call__ = () => {
|
||||
this.run(action, action.screen);
|
||||
return null;
|
||||
};
|
||||
return action;
|
||||
};
|
||||
const out: Record<string, any> = {};
|
||||
for (const kind of ["Jump", "Call", "Return", "SetVariable", "ToggleVariable", "SetDict", "SetField", "ToggleField",
|
||||
"SetScreenVariable", "ToggleScreenVariable", "Show", "Hide", "ShowTransient", "Function", "NullAction", "Play",
|
||||
"Queue", "Stop", "Notify", "Quit", "MainMenu", "SelectedIf", "SensitiveIf", "ToggleScreen", "Language", "ShowMenu",
|
||||
"OpenURL", "SetLocalVariable", "QuickSave", "QuickLoad"]) out[kind] = make(kind);
|
||||
"OpenURL", "SetLocalVariable", "QuickSave", "QuickLoad", "Replay", "EndReplay"]) out[kind] = make(kind);
|
||||
const shape = (kind: string) => (...args: any[]) => {
|
||||
const last = args[args.length - 1];
|
||||
const hasKw = last && typeof last === "object" && !Array.isArray(last) && Object.hasOwn(last, "__kw");
|
||||
|
||||
Reference in new issue
Block a user