Layers (config.layers, onlayer, add_layer, show_layer_at, camera with perspective), the
game's config.custom_text_tags called like Ren'Py does (the built-in {bt}/{fi}/... copies
are gone), built-in {font} {alpha} {k} {cps} {a} {rb}/{rt} {space} {alt} {image}, FontGroup,
mouse displayable, renpy.fetch, preferences backed by the player's settings, Color class,
Render.subsurface/zoom, `default obj.attr` on provided objects, and a repaint loop that
survives text being laid out again.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Each letter is moved by one animation loop with the formulas of BounceText, FadeInText, ScareText and
GlitchText (fi follows the text speed, bt keeps its phase while a line is typed out, glitch cuts the text
into strips that jump sideways). Flatten and `at Flatten` isolate their children as one picture, screens
get the 3D turns, matrixtransform and blend of their widgets, and RotateMatrix/OffsetMatrix/ScaleMatrix
exist in Python.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Named stores (PyNamespace: dotted access, functions that see their own store first, restored after a
save is loaded), real generators run step by step (next, send, yield as an expression), len/bool through
__len__/__bool__, os/calendar/itertools/pygame_sdl2 helpers, Skip and ToggleLocalVariable actions,
text tags {glitch} and {wave} and plain display of explode/gradient/ruby, SnowBlossom and anim.Blink,
RoundedCorners as a transform, transforms listed in `at`, ATL choice weights that are flags, and
renpy.image_size from the converter's picture sizes.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Letters of these custom tags animate one after another (CSS). A closing tag now closes the style that
it opened (with anything left open inside) instead of popping whatever was on top, and an unknown tag
that has a closing tag later shows its text plainly and stays balanced.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Music, sound and voice volumes are shared by all games by default; a game that
has never been launched with its own volumes plays with the shared ones
- The sound section of the player settings has a "own volume for this game" switch:
turning it on starts from the current values and then the sliders change only this
game, turning it off removes the override and returns to the shared volumes
- The override belongs to the game (game_id from /api/versions/{id}), so forks and mods
share it; it falls back to the version id when the id is unavailable
- Reading speed and auto delay stay shared
- volume-store.ts keeps the storage logic (damaged data ignored, values clamped,
unavailable storage tolerated) and has its own tests
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Python containing renpy.input, narrator("..."), character calls, renpy.say,
renpy.pause, renpy.display_menu, renpy.with_statement or renpy.call_screen now
runs through loops, try, functions, methods and expressions.
Player
- python.ts: execG/evalG/callG run only the code that may wait (mayBlock);
everything else stays on the synchronous fast path. A waiting call returns a
BlockingCall; outside the generator interpreter it is an explicit error. The
interpreter scope is set aside while the player decides
- engine: drives the generator and answers requests (dialogue, text input with
length/allow/exclude, pause, choice menu, transition, call screen)
- renpy.show/hide/scene, transitions and transforms as Python values,
Character(...) objects, Borders, set_tag_attributes
Converter
- blocking_names() lists game functions that may wait, transitively, and ships
them as game.json "blocking"; character calls are allowed
- hide screen of an unknown screen is harmless; images defined by a parenthesised
expression are supported
Hooked now passes the strict compatibility check (with Tomie and Twisted-Thoughts).
Tests: waiting.py fixture run through the generator interpreter; blocking-name analysis.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Converter
- screens.py parses the screen language: containers (fixed, frame, vbox, hbox,
grid, vpgrid, viewport, button, has), widgets (text, textbutton, imagebutton,
add, bar ...), if/for/use/transclude, default, python, key, timer, on, inline ATL,
conditional properties, and decompiled forms (unparenthesised operators,
glued property values, comma separated actions)
- style statements, call/show/hide screen operators and Python-level
renpy.show_screen are translated; only screens the game shows are audited,
unused stock screens no longer block an import; undefined screens do
- ATL values may be computed at run time and transforms take parameters
(at blink(0.2, 0.3)); blur property
Player
- screens.ts builds the DOM from the IR and rebuilds it on state changes;
buttons with idle/hover/selected/insensitive states, tooltips, sounds, modal
screens, timers and keys that survive re-rendering, saved with the save file
- Actions: Jump, Call, Return, SetVariable, Toggle*, SetScreenVariable, Show, Hide,
ToggleScreen, Function, Play, Notify, If, Language, ShowMenu, OpenURL ...
- Crop/LiveCrop/Solid/Frame/Transform/DynamicDisplayable values, crop property,
globals(), renpy.variant/change_language/seen_image, transition stubs
Tests: screen parser and runtime (linkedom DOM), screens fixture compiled from
tests/fixtures/screens.rpy. Docs: docs/SCREENS.md.
Twisted-Thoughts now passes the strict compatibility check.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- games.main_version_id, version visibility (draft/published) and kind
(original/translation/mod), version_history; migration 0002
- permissions translations.edit and versions.publish
- "Translate" creates a translation draft; publishing merges the changed
languages into the main version in place (players keep their saves), the
previous manifest goes to the history and can be restored
- translation API: overview with per-state stats, entries with filters,
search and scene context, save, glossary, locale font, PO/CSV export/import
- translation editor page: languages and progress, lines and strings,
filters, autosave, approve/outdated/fuzzy actions, glossary highlighting,
"play from here" with the scene restored
- player: ?lang= and ?at= from the editor, per-locale font
(models.py and permissions.py for this landed earlier in 9ee3e1f)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tokens and components in styles.css, shared Ui (dialogs, toasts), icons,
bottom tab bar on mobile, keyboard/gamepad navigation, library shelf with
filters and conversion badges, hero game page, restyled player screens.
Games list now returns per-game version counts. Adds docs/UI_DESIGN.md.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- every script node gets a permanent id, say nodes a Ren'Py-style tid
- all languages from the game's tl/ are imported at conversion: dialogue blocks
matched by their source-location comment or by label + original text,
`translate strings` become a per-language strings table
- source language from config.language or detected from the dialogue
- manifest stores locales/<lang>/<module>.json and strings.json; game.json
lists sourceLang and available locales with progress
- player: language picker in main menu and settings, fallback to source,
__(), [var!t] and _preferences.language
- parser: `default` with init priority, and one bad statement no longer drops
the whole file
- docs/LOCALIZATION.md: design for translations, publishing and mods
Line endings of several converter/player files go back to LF: an earlier
commit accidentally stored them as CRLF.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- FastAPI backend: admin bootstrap with forced credential change, admin-created
users with one-time generated passwords, roles as permission sets
- Games and versions with content-addressed blob storage; forks copy only the
manifest, so mods share assets with the original
- Worker: archive/RPA extraction, unrpyc for .rpyc, own .rpy parser, Python
subset translated via ast, media converted to WebP/Opus/WebM
- React panel (library search, game page, upload, users, roles) and a
framework-free web player with saves, history, skip/auto, transitions
- docker compose: postgres, backend, worker, nginx on a single HTTP port
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>