Converter: cover real Ren'Py games better, add folder import and dry run
Checked against six real games (Ren'Py 7.5 and 8.x): - nested labels and named menus become jump anchors inside label bodies - call with arguments and labels with parameters (dynamically scoped) - frame-by-frame ATL images, Movie frames, layeredimage, camera, Shake, custom audio channels via register_channel, show text with expressions - generator expressions in Python, no SyntaxWarning noise from decompiled code Also: - app.import_dir converts an unpacked game folder without changing it - app.converter.analyze dry-runs the script part of the converter - imports use their own "importing" status so a worker restart can't hijack them Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
35033fb86c
commit
a97b46079e
18 files changed
+3456
-2695
No files matched your search
@@ -79,3 +79,44 @@ def test_full_pipeline(data_dir, tmp_path):
|
||||
assert [lbl["name"] for lbl in script["labels"]] == ["start", "walk", "stay_home", "ending"]
|
||||
if shutil.which("ffmpeg"):
|
||||
assert "audio/theme.ogg" in files
|
||||
|
||||
|
||||
def test_nested_labels_and_named_menus_become_anchors():
|
||||
from app.converter.pipeline import anchors
|
||||
d = parse("label start:\n if x:\n label inner:\n \"a\"\n"
|
||||
" menu pick:\n \"A\":\n jump .loc\n"
|
||||
"label .loc:\n return\n")
|
||||
labels = d.modules["t.rpy"]
|
||||
assert [lbl["name"] for lbl in labels] == ["start", "inner.loc"] # local labels follow the last global one
|
||||
found = anchors(labels)
|
||||
assert found["inner"] == {"label": "start", "path": [0, 0, 0], "params": None}
|
||||
assert found["pick"]["path"] == [1]
|
||||
assert labels[0]["body"][2]["items"][0]["body"][0]["target"] == "inner.loc"
|
||||
|
||||
|
||||
def test_call_arguments_and_label_params():
|
||||
d = parse('label start:\n call greet("Bob", loud=True) from _c1\n'
|
||||
'label greet(name, loud=False):\n "Hi [name]"\n return\n')
|
||||
call = d.modules["t.rpy"][0]["body"][0]
|
||||
assert call["target"] == "greet" and call["args"]["a"][0]["v"] == "Bob"
|
||||
assert "loud" in call["args"]["kw"]
|
||||
params = d.modules["t.rpy"][1]["params"]
|
||||
assert params[0] == ["name", None] and params[1][1]["v"] is False
|
||||
|
||||
|
||||
def test_atl_frames_layeredimage_and_channels():
|
||||
d = parse('image blink:\n "a.png" with dissolve\n pause 0.5\n "b.png"\n 0.25\n'
|
||||
' repeat\n'
|
||||
'image vid:\n Movie(play="v.webm")\n'
|
||||
'layeredimage eileen:\n always:\n "eileen_base"\n'
|
||||
' group face:\n attribute happy default\n attribute sad "sad.png"\n'
|
||||
'init python:\n renpy.music.register_channel("amb", mixer="sfx", loop=True)\n')
|
||||
blink = d.images["blink"]
|
||||
assert [f.get("d") for f in blink["frames"]] == [0.5, 0.25] and blink["repeat"]
|
||||
assert d.images["vid"] == {"movie": "v.webm"}
|
||||
layers = d.images["eileen"]["layered"]
|
||||
assert layers[0] == {"always": {"ref": "eileen_base"}}
|
||||
assert layers[1] == {"attr": "happy", "group": "face", "default": True,
|
||||
"img": {"ref": "eileen_face_happy"}}
|
||||
assert layers[2]["img"] == {"file": "sad.png"}
|
||||
assert d.channels["amb"] == {"mixer": "sfx", "loop": True}
|
||||
Reference in new issue
Block a user