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
@@ -215,10 +215,14 @@ def mark_screen_calls(data) -> None:
|
||||
|
||||
IMPLICIT_METHODS = {"__init__", "__str__", "__repr__"}
|
||||
|
||||
# Labels Ren'Py itself defines (renpy/common); jumping to them goes to the player's own menu
|
||||
ENGINE_LABELS = {"main_menu_screen", "_main_menu_screen", "main_menu"}
|
||||
|
||||
# Calls that make the player wait; Python containing them runs on the player's generator interpreter
|
||||
BLOCKING_BUILTINS = {"renpy.input", "renpy.say", "renpy.pause", "renpy.display_menu", "renpy.with_statement",
|
||||
"renpy.call_screen"}
|
||||
CALLS |= BLOCKING_BUILTINS | {"renpy.show", "renpy.hide", "renpy.scene", "Borders", "renpy.set_tag_attributes"}
|
||||
CALLS |= BLOCKING_BUILTINS | {"renpy.show", "renpy.hide", "renpy.scene", "Borders", "renpy.set_tag_attributes",
|
||||
"eval", "exec", "Replay", "EndReplay", "renpy.end_replay"}
|
||||
|
||||
|
||||
def blocking_names(data) -> list[str]:
|
||||
@@ -340,6 +344,9 @@ def audit_ir(data) -> None:
|
||||
is_method = (function.get("k") == "attr" and not name.startswith(("renpy.", "math.", "random."))
|
||||
and (function.get("a") in METHODS or function.get("a") in user_methods
|
||||
or function.get("a") in user_names))
|
||||
# `MainMenu(confirm=False)()`: calling the action a screen action constructor returned
|
||||
if function.get("k") == "call" and dotted(function.get("f", {})) in SCREEN_CALLS:
|
||||
is_method = True
|
||||
if name not in CALLS and name not in user_names and not is_method:
|
||||
data.warn(f"Вызов не поддерживается плеером: {name or 'динамическая функция'}", location)
|
||||
if value.get("op") in {"jump", "call"} and value.get("target"):
|
||||
@@ -349,7 +356,7 @@ def audit_ir(data) -> None:
|
||||
visit(child, location)
|
||||
|
||||
from app.converter.pipeline import anchors
|
||||
labels = {label["name"] for module in data.modules.values() for label in module}
|
||||
labels = {label["name"] for module in data.modules.values() for label in module} | ENGINE_LABELS
|
||||
labels |= {name for module in data.modules.values() for name in anchors(module)}
|
||||
for path, module in data.modules.items():
|
||||
visit(module, path)
|
||||
|
||||
Reference in new issue
Block a user