Compare commits

..

4 Commits

Author SHA1 Message Date
Gouvernathor db1a4367ae Use the docstring instead of a rst entry
no need to edit the doc generation script actually
2023-07-19 15:00:38 +02:00
Gouvernathor fe8dc69cde Tweak doc entry 2023-07-16 18:38:55 +02:00
Gouvernathor 3ebd020841 Document addition
Unless I add deco or decorator as a :doc: option, decorators can't be documented by docstring
2023-07-16 18:24:10 +02:00
Gouvernathor 1bc5e25ed3 Add register_decorator 2023-07-16 18:13:48 +02:00
156 changed files with 2751 additions and 13149 deletions
-6
View File
@@ -103,9 +103,6 @@ cache/
# Pygame_sdl2.
/pygame_sdl2
# Updater.
update.pem
# Steam.
steam_appid.txt
/steamapi.py
@@ -130,6 +127,3 @@ CubismSdkForNative-4-*.zip
# Works in progress, throwaway scripts, etc.
/scratch
/*-dists
/c
/cg
/.vscode/launch.json
-18
View File
@@ -190,24 +190,6 @@
"foreground": "#dc3545",
"fontStyle": "bold"
}
},
{
"scope": "renpy.meta.color.#fff",
"settings": {
"foreground": "#fff"
}
},
{
"scope": "renpy.meta.color.#fcc",
"settings": {
"foreground": "#fcc"
}
},
{
"scope": "renpy.meta.color.#cfc",
"settings": {
"foreground": "#cfc"
}
}
]
},
+1 -1
View File
@@ -50,7 +50,7 @@ your system. On Ubuntu and Debian, these dependencies can be installed with
the command::
sudo apt install virtualenvwrapper python3-dev libavcodec-dev libavformat-dev \
libswresample-dev libswscale-dev libharfbuzz-dev libfreetype6-dev libfribidi-dev libsdl2-dev \
libswresample-dev libswscale-dev libfreetype6-dev libfribidi-dev libsdl2-dev \
libsdl2-image-dev libsdl2-gfx-dev libsdl2-mixer-dev libsdl2-ttf-dev libjpeg-dev
Ren'Py requires SDL_image 2.6 or greater. If your distribution doesn't include
+1 -1
View File
@@ -252,7 +252,7 @@ def main():
"-q",
"egg_info",
"--tag-build",
"+renpy" + args.version,
"-for-renpy-" + args.version,
"sdist",
"-d",
os.path.abspath(destination)
+1 -1
View File
@@ -42,7 +42,7 @@ class Editor(renpy.editor.Editor):
elif renpy.arch == "armv7l":
arch = "arm"
else:
arch = "x64"
arch = "x86_64"
code = os.path.join(RENPY_VSCODE, "VSCode-linux-" + arch, "bin", "code")
else:
+1 -1
View File
@@ -42,7 +42,7 @@ class Editor(renpy.editor.Editor):
elif renpy.arch == "armv7l":
arch = "arm"
else:
arch = "x64"
arch = "x86_64"
code = os.path.join(RENPY_VSCODE, "VSCode-linux-" + arch, "bin", "code")
else:
+34 -123
View File
@@ -528,11 +528,6 @@ change_renpy_executable()
self.reporter.info(_("Scanning project files..."))
project.update_dump(force=True, gui=False, compile=project.data['force_recompile'])
if project.data['tutorial']:
self.reporter.info(_("Building distributions failed:\n\nThe project is the Ren'Py Tutorial, which can't be distributed outside of Ren'Py. Consider using The Question as a test project."), pause=True)
self.log.close()
return
if project.data['force_recompile']:
import compileall
@@ -584,9 +579,6 @@ change_renpy_executable()
self.include_update = build['include_update']
self.build_update = self.include_update and build_update
if self.include_update:
self.make_key_pem()
# The various executables, which change names based on self.executable_name.
self.app = self.executable_name + ".app"
self.exe = self.executable_name + ".exe"
@@ -684,12 +676,11 @@ change_renpy_executable()
dlc=p["dlc"])
if self.build_update and p["update"]:
for update_format in self.list_update_formats():
self.make_package(
p["name"],
update_format,
p["file_lists"],
dlc=p["dlc"])
self.make_package(
p["name"],
"update",
p["file_lists"],
dlc=p["dlc"])
wait_parallel_threads()
@@ -708,23 +699,6 @@ change_renpy_executable()
if open_directory:
renpy.run(store.OpenDirectory(self.destination, absolute=True))
def list_update_formats(self):
"""
Returns a list of update formats to build.
"""
rv = [ ]
for update_format in self.build["update_formats"]:
if update_format == "rpu":
rv.append("rpu")
elif update_format == "zsync":
rv.append("update")
else:
raise Exception("Unknown update format: " + update_format)
return rv
def scan_and_classify(self, directory, patterns):
"""
Walks through the `directory`, finds files and directories that
@@ -749,37 +723,29 @@ change_renpy_executable()
is_dir = os.path.isdir(path)
if is_dir:
match_names = [ name + "/", name ]
match_name = name + "/"
else:
match_names = [ name ]
match_name = name
for pattern, file_list in patterns:
matched = False
if match(match_name, pattern):
for match_name in match_names:
# When we have ('test/**', None), avoid excluding test.
if (not file_list) and is_dir:
new_pattern = pattern.rstrip("*")
if (pattern != new_pattern) and match(match_name, new_pattern):
continue
if match(match_name, pattern):
# When we have ('test/**', None), avoid excluding test.
if (not file_list) and is_dir:
new_pattern = pattern.rstrip("*")
if (pattern != new_pattern) and match(match_name, new_pattern):
continue
matched = True
break
if matched:
break
else:
print(str(match_names[0]), "doesn't match anything.", file=self.log)
print(str(match_name), "doesn't match anything.", file=self.log)
pattern = None
file_list = None
print(str(match_names[0]), "matches", str(pattern), "(" + str(file_list) + ").", file=self.log)
print(str(match_name), "matches", str(pattern), "(" + str(file_list) + ").", file=self.log)
if file_list is None:
return
@@ -1446,7 +1412,6 @@ change_renpy_executable()
FORMATS = {
"update" : (".update", False, False, False),
"rpu" : ("", False, False, False),
"tar.bz2" : (".tar.bz2", False, False, True),
"zip" : (".zip", False, False, True),
@@ -1496,7 +1461,7 @@ change_renpy_executable()
update_fn = os.path.join(self.destination, filename + ".update.json")
if self.include_update and not format.startswith("app-"):
if self.include_update and (variant not in [ 'ios', 'android', 'source']) and (not format.startswith("app-")):
with open(update_fn, "wb" if PY2 else "w") as f:
json.dump(update, f, indent=2)
@@ -1505,9 +1470,6 @@ change_renpy_executable()
fl.append(File("update", None, True, False))
fl.append(File("update/current.json", update_fn, False, False))
if not dlc:
fl.append(File("update/key.pem", self.temp_filename("key.pem"), False, False))
# If we're not an update file, prepend the directory.
if (not dlc) and prepend:
fl.prepend_directory(filename)
@@ -1518,10 +1480,6 @@ change_renpy_executable()
full_filename = filename + ext
path += ext
if format == "rpu":
full_filename = "rpu/" + variant + ".files.rpu"
path = self.destination + "/" + full_filename
if self.build['renpy']:
fl_hash = fl.hash(self)
else:
@@ -1558,8 +1516,6 @@ change_renpy_executable()
pkg = TarPackage(path, "w:bz2")
elif format == "update":
pkg = UpdatePackage(path, filename, self.destination)
elif format == "rpu":
pkg = RPUPackage(self.destination, variant)
elif format == "zip" or format == "app-zip" or format == "bare-zip":
if self.build['renpy']:
pkg = ExternalZipPackage(path)
@@ -1596,13 +1552,13 @@ change_renpy_executable()
self.reporter.progress_done()
if format == "update":
# Build the zsync file.
self.reporter.info(_("Making the [variant] update zsync file."), variant=variant)
def close_progress(done, total):
self.reporter.progress(_("Finishing the [variant] [format] package."), done, total, variant=variant, format=format)
pkg.close(close_progress)
pkg.close()
if done is not None:
done()
@@ -1623,78 +1579,33 @@ change_renpy_executable()
def add_variant(variant):
digest = self.build_cache[self.base_name + "-" + variant + ".update"][0]
sums_size = os.path.getsize(self.destination + "/" + self.base_name + "-" + variant + ".sums")
index[variant] = {
"version" : self.update_versions[variant],
"pretty_version" : self.pretty_version,
"renpy_version" : renpy.version_only,
}
"digest" : digest,
"zsync_url" : self.base_name + "-" + variant + ".zsync",
"sums_url" : self.base_name + "-" + variant + ".sums",
"sums_size" : sums_size,
"json_url" : self.base_name + "-" + variant + ".update.json",
}
if "update" in self.build["update_formats"]:
fn = renpy.fsencode(os.path.join(self.destination, self.base_name + "-" + variant + ".update"))
digest = self.build_cache[self.base_name + "-" + variant + ".update"][0]
sums_size = os.path.getsize(self.destination + "/" + self.base_name + "-" + variant + ".sums")
index[variant].update({
"digest" : digest,
"zsync_url" : self.base_name + "-" + variant + ".zsync",
"sums_url" : self.base_name + "-" + variant + ".sums",
"sums_size" : sums_size,
"json_url" : self.base_name + "-" + variant + ".update.json",
})
fn = renpy.fsencode(os.path.join(self.destination, self.base_name + "-" + variant + ".update"))
if os.path.exists(fn):
os.unlink(fn)
if "rpu" in self.build["update_formats"]:
rpu_size = 0
rpu_dir = os.path.join(self.destination, "rpu")
for i in os.listdir(rpu_dir):
rpu_size += os.path.getsize(os.path.join(rpu_dir, i))
index[variant]["rpu_url"] = "rpu/" + variant + ".files.rpu"
index[variant]["rpu_digest"] = self.build_cache["rpu/" + variant + ".files.rpu"][0]
index[variant]["rpu_size"] = rpu_size
if os.path.exists(fn):
os.unlink(fn)
for p in packages:
if p["update"]:
add_variant(p["name"])
update_data = json.dumps(index, indent=2)
fn = renpy.fsencode(os.path.join(self.destination, "updates.json"))
with open(fn, "wb" if PY2 else "w") as f:
f.write(update_data)
json.dump(index, f, indent=2)
# Write the signed file.
import ecdsa
with open(self.find_update_pem(), "rb") as f:
signing_key = ecdsa.SigningKey.from_pem(f.read())
fn = renpy.fsencode(os.path.join(self.destination, "updates.ecdsa"))
with open(fn, "wb") as f:
f.write(signing_key.sign(update_data.encode("utf-8")))
def find_update_pem(self):
if self.build['renpy']:
return os.path.join(config.renpy_base, "update.pem")
else:
return os.path.join(self.project.path, "update.pem")
def make_key_pem(self):
import ecdsa
with open(self.find_update_pem(), "rb") as f:
signing_key = ecdsa.SigningKey.from_pem(f.read())
key_pem = self.temp_filename("key.pem")
with open(key_pem, "wb") as f:
f.write(signing_key.verifying_key.to_pem())
def save_build_cache(self):
if not self.build['renpy']:
-16
View File
@@ -267,23 +267,7 @@ label start_update_old_game:
call update_old_game
jump build_distributions
label add_update_pem:
python hide:
interface.info("You're trying to build an update, but an update.pem file doesn't exist.\n\nThis file is used to sign updates, and will be automatically created in your projects's base directory.\n\nYou'll need to back up update.pem and keep it safe.", cancel=Jump("build_distributions"))
import ecdsa
key = ecdsa.SigningKey.generate(curve=ecdsa.NIST256p).to_pem()
with open(os.path.join(project.current.path, "update.pem"), "wb") as f:
f.write(key)
return
label start_distribute:
if project.current.dump["build"]["include_update"] and not os.path.exists(os.path.join(project.current.path, "update.pem")):
call add_update_pem
if project.current.data["add_from"]:
call add_from_common
+1 -1
View File
@@ -162,7 +162,7 @@ init 1 python in editor:
elif renpy.arch == "armv7l":
arch = "arm"
else:
arch = "x64"
arch = "x86_64"
installed = os.path.exists(os.path.join(config.renpy_base, "vscode/VSCode-linux-" + arch))
+1 -1
View File
@@ -259,7 +259,7 @@ label lint:
interface.processing(_("Checking script for potential problems..."))
lint_fn = project.current.temp_filename("lint.txt")
project.current.launch([ 'lint', lint_fn, ] + list(persistent.lint_options), wait=True)
project.current.launch([ 'lint', lint_fn ], wait=True)
e = renpy.editor.editor
e.begin(True)
+1 -5
View File
@@ -81,6 +81,7 @@ init python:
label install_live2d:
python hide:
if PY2:
_prefix = r"lib/py2-"
else:
@@ -97,11 +98,6 @@ label install_live2d:
(r".*/Core/dll/android/(x86_64/libLive2DCubismCore.so)", r"rapt/prototype/renpyandroid/src/main/jniLibs/\1"),
]
if PY2:
patterns.extend([
(r".*/Core/dll/windows/x86/(Live2DCubismCore.dll)", _prefix + r"windows-i686/\1"),
])
install_from_zip("Live2D Cubism SDK for Native", "CubismSdkForNative-4-*.zip", patterns)
jump front_page
+1 -1
View File
@@ -492,7 +492,7 @@ init python in interface:
The amount of time to pause for after showing the message.
"""
common(title, store.INTERACTION_COLOR, message, submessage=submessage, pause=pause, show_screen=True, **kwargs)
common(title, store.INTERACTION_COLOR, message, submessage=None, pause=pause, show_screen=True, **kwargs)
renpy.pause(pause)
def processing(message, submessage=None, complete=None, total=None, **kwargs):
-3
View File
@@ -62,9 +62,6 @@ label new_project:
if persistent.projects_directory is None:
$ interface.error(_("The projects directory could not be set. Giving up."))
if PY2:
$ interface.info(_("Warning : you are using Ren'Py 7. It is recommended to start new projects using Ren'Py 8 instead."))
python:
if persistent.legacy:
+1 -3
View File
@@ -242,12 +242,10 @@ init python:
# allows the updater to run.
build.include_update = True
# Build both kinds of updates.
build.update_formats = [ "zsync", "rpu" ]
# Allow empty directories, so we can distribute the images directory.
build.exclude_empty_directories = False
# Mac signing options.
import os
build.mac_identity = os.environ.get("RENPY_MAC_IDENTITY", None)
+11 -34
View File
@@ -33,6 +33,7 @@ init python in distribute:
from zipfile import crc32
# Since the long type doesn't exist on py3, define it here
if not PY2:
long = int
@@ -194,7 +195,7 @@ init python in distribute:
self.zipfile.write_with_info(zi, path)
def close(self, progress=None):
def close(self):
self.zipfile.close()
@@ -206,7 +207,7 @@ init python in distribute:
If true, times will be forced to the epoch.
"""
self.tarfile = tarfile.open(filename, mode, format=tarfile.PAX_FORMAT)
self.tarfile = tarfile.open(filename, mode)
self.tarfile.dereference = True
self.notime = notime
@@ -242,10 +243,9 @@ init python in distribute:
def add_directory(self, name, path):
self.add_file(name, path, True)
def close(self, progress=None):
def close(self):
self.tarfile.close()
class UpdatePackage(TarPackage):
def __init__(self, filename, basename, destination):
@@ -255,7 +255,7 @@ init python in distribute:
TarPackage.__init__(self, filename, "w", notime=True)
def close(self, progress=None):
def close(self):
TarPackage.close(self)
cmd = [
@@ -283,6 +283,7 @@ init python in distribute:
sums.write(struct.pack("<I", zlib.adler32(data) & 0xffffffff))
class DirectoryPackage(object):
def mkdir(self, path):
@@ -314,10 +315,9 @@ init python in distribute:
fn = os.path.join(self.path, name)
self.mkdir(fn)
def close(self, progress=None):
def close(self):
return
class ExternalZipPackage(object):
def __init__(self, path):
@@ -331,7 +331,7 @@ init python in distribute:
def add_directory(self, name, path):
self.dp.add_directory(name, path)
def close(self, progress=None):
def close(self):
self.dp.close()
if os.path.exists(self.path):
@@ -350,40 +350,17 @@ init python in distribute:
shutil.rmtree(self.directory)
class DMGPackage(DirectoryPackage):
def __init__(self, path, make_dmg):
self.make_dmg = make_dmg
DirectoryPackage.__init__(self, path)
def close(self, progress=None):
def close(self):
DirectoryPackage.close(self)
self.make_dmg()
class RPUPackage(object):
def __init__(self, directory, variant):
import renpy.update.common
self.directory = directory + "/rpu"
self.variant = variant
self.file_list = renpy.update.common.FileList()
if not os.path.exists(self.directory):
os.mkdir(self.directory)
def add_file(self, name, path, xbit):
self.file_list.add_file(name, path, xbit)
def add_directory(self, name, _path):
self.file_list.add_directory(name)
def close(self, progress=None):
import renpy.update.generate
renpy.update.generate.BlockGenerator(self.variant, self.file_list, self.directory, progress)
parallel_threads = [ ]
class ParallelPackage(object):
@@ -401,7 +378,7 @@ init python in distribute:
def add_directory(self, name, path):
self.worklist.append((True, name, path, True))
def close(self, progress=None):
def close(self):
t = threading.Thread(target=self.run)
t.start()
+20 -68
View File
@@ -19,15 +19,15 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
default persistent.show_edit_funcs = True
default persistent.windows_console = False
default persistent.lint_options = { # the ones which should be enabled by default
"--orphan-tl",
}
init python:
from math import ceil
if persistent.show_edit_funcs is None:
persistent.show_edit_funcs = True
if persistent.windows_console is None:
persistent.windows_console = False
def scan_translations(piglatin=True):
languages = renpy.known_languages()
@@ -64,9 +64,6 @@ default persistent.daily_update_check_once = False
# Keep the default update check from triggering until tomorrow.
default persistent.last_update_check = datetime.date.today()
# Should we try to skip the splashscreen?
default persistent.skip_splashscreen = False
init python:
if not persistent.daily_update_check_once:
persistent.daily_update_check_once = True
@@ -74,14 +71,6 @@ init python:
default preference_tab = "general"
define preference_tabs = {
"general" : _("General"),
"options" : _("Options"),
"theme" : _("Theme"),
"install" : _("Install Libraries"),
"actions" : _("Actions"),
"lint" : _("Lint Options"),
}
screen preferences():
@@ -109,12 +98,17 @@ screen preferences():
has vbox
# Projects directory selection.
add SEPARATOR2
add HALF_SPACER
for i, l in preference_tabs.items():
textbutton l action SetVariable("preference_tab", i) style "l_list"
textbutton _("General") action SetVariable("preference_tab", "general") style "l_list"
textbutton _("Options") action SetVariable("preference_tab", "options") style "l_list"
textbutton _("Theme") action SetVariable("preference_tab", "theme") style "l_list"
textbutton _("Install Libraries") action SetVariable("preference_tab", "install") style "l_list"
textbutton _("Actions") action SetVariable("preference_tab", "actions") style "l_list"
if preference_tab == "general":
@@ -125,6 +119,7 @@ screen preferences():
has vbox
# Projects directory selection.
add SEPARATOR2
@@ -212,22 +207,6 @@ screen preferences():
add SPACER
add SEPARATOR2
frame:
style "l_indent"
has vbox
text _("Game Options:")
add HALF_SPACER
if renpy.windows:
textbutton _("Console output") style "l_checkbox" action ToggleField(persistent, "windows_console")
textbutton _("Skip splashscreen") style "l_checkbox" action ToggleField(persistent, "skip_splashscreen")
add SPACER
add SEPARATOR2
frame:
style "l_indent"
has vbox
@@ -239,11 +218,15 @@ screen preferences():
textbutton _("Show edit file section") style "l_checkbox" action ToggleField(persistent, "show_edit_funcs")
textbutton _("Large fonts") style "l_checkbox" action [ ToggleField(persistent, "large_print"), renpy.utter_restart ]
if renpy.windows:
textbutton _("Console output") style "l_checkbox" action ToggleField(persistent, "windows_console")
textbutton _("Sponsor message") style "l_checkbox" action ToggleField(persistent, "sponsor_message")
if ability.can_update:
textbutton _("Daily check for update") style "l_checkbox" action [ToggleField(persistent, "daily_update_check"), SetField(persistent, "last_update_check", None)] selected persistent.daily_update_check
elif preference_tab == "theme":
frame:
@@ -253,6 +236,7 @@ screen preferences():
has vbox
# Projects directory selection.
add SEPARATOR2
frame:
@@ -292,6 +276,7 @@ screen preferences():
use install_preferences
elif preference_tab == "actions":
frame:
@@ -315,39 +300,6 @@ screen preferences():
textbutton _("Reset window size") style "l_nonbox" action Preference("display", 1.0)
textbutton _("Clean temporary files") style "l_nonbox" action Jump("clean_tmp")
elif preference_tab == "lint":
frame:
style "l_indent"
xmaximum TWOTHIRDS
xfill True
has vbox
add SEPARATOR2
frame:
style "l_indent"
has vbox
text _("Lint toggles:")
add HALF_SPACER
textbutton _("Orphan translations"):
style "l_checkbox"
action ToggleSetMembership(persistent.lint_options, "--orphan-tl")
textbutton _("Parameters overriding builtin names"):
style "l_checkbox"
action ToggleSetMembership(persistent.lint_options, "--builtins-parameters")
textbutton _("Word count and character count for speaking characters"):
style "l_checkbox"
action ToggleSetMembership(persistent.lint_options, "--words-char-count")
add SPACER
textbutton _("Check Script (Lint)") action Jump("lint")
textbutton _("Return") action Jump("front_page") style "l_left_button"
+4 -18
View File
@@ -55,12 +55,12 @@ init python in project:
def __init__(self, path, name=None):
while path.endswith("/"):
path = path[:-1]
if name is None:
name = os.path.basename(path)
while path.endswith("/"):
path = path[:-1]
if not os.path.exists(path):
raise Exception("{} does not exist.".format(path))
@@ -128,7 +128,6 @@ init python in project:
data.setdefault("add_from", True)
data.setdefault("force_recompile", True)
data.setdefault("android_build", "Release")
data.setdefault("tutorial", False)
if "renamed_all" not in data:
dp = data["packages"]
@@ -260,16 +259,8 @@ init python in project:
environ = dict(os.environ)
environ["RENPY_LAUNCHER_LANGUAGE"] = _preferences.language or "english"
if persistent.skip_splashscreen:
environ["RENPY_SKIP_SPLASHSCREEN"] = "1"
environ.update(env)
# Filter out system PYTHON* environment variables.
if hasattr(sys, "renpy_executable"):
environ = { k : v for k, v in environ.items() if not k.startswith("PYTHON") }
encoded_environ = { }
for k, v in environ.items():
@@ -770,12 +761,7 @@ init python in project:
blurb = LAUNCH_BLURBS[persistent.blurb % len(LAUNCH_BLURBS)]
persistent.blurb += 1
if persistent.skip_splashscreen:
submessage = _("Splashscreen skipped in launcher preferences.")
else:
submessage = None
interface.interaction(_("Launching"), blurb, submessage=submessage, pause=2.5)
interface.interaction(_("Launching"), blurb, pause=2.5)
def __call__(self):
+1 -1
View File
@@ -731,7 +731,7 @@
# renpy/common/_developer/developer.rpym:57
old "Show Image Load Log (F4)"
new "Afficher le log de chargement des images (F4)"
new "Montrer le log ne chargement des images (F4)"
# renpy/common/_developer/developer.rpym:60
old "Hide Image Load Log (F4)"
+1 -21
View File
@@ -1590,7 +1590,7 @@
# game/gui7.rpy:339
old "Custom. The GUI is optimized for a 16:9 aspect ratio."
new "Personnalisé. Le GUI est optimisé pour un ratio de cadre 16:9."
new "Personnalisé. Le GUI est optimié pour un ratio de cadre 16:9."
# game/gui7.rpy:355
old "WIDTH"
@@ -2147,23 +2147,3 @@
# game/androidstrings.rpy:69
old "I found a bundle.keystore file in the rapt directory. Do you want to use this file?"
new "J'ai trouvé un fichier bundle.keystore dans le dossier \"rapt\". Voulez-vous l'utiliser ?"
# game/choose_directory.rpy:72
old "No directory was selected, but one is required."
new "Un chemin d'accès est nécessaire, mais aucun n'a été fourni."
# game/choose_directory.rpy:80
old "The selected directory does not exist."
new "Le dossier sélectionné n'existe pas."
# game/choose_directory.rpy:82
old "The selected directory is not writable."
new "Le dossier sélectionné n'est pas ouvert en écriture."
# game/distribute.rpy:554
old "This may be derived from build.name and config.version or build.version."
new "Cette variable peut être dérivée de build.name, et de config.version ou build.version."
# game/new_project.rpy:66
old "Warning : you are using Ren'Py 7. It is recommended to start new projects using Ren'Py 8 instead."
new "Attention : vous utilisez Ren'Py 7. Il est recommandé d'utiliser Ren'Py 8 pour de nouveaux projets."
-15
View File
@@ -228,18 +228,3 @@
old "Speech Bubble Editor (Shift+B)"
new "Editor de burbujas de diálogo (Mayús+B)"
# renpy/common/_developer/developer.rpym:69
old "Show Translation Identifiers"
new "Mostrar identificadores de traducción"
# renpy/common/_developer/developer.rpym:72
old "Hide Translation Identifiers"
new "Ocultar identificadores de traducción"
# renpy/common/_developer/developer.rpym:582
old "\n{color=#fff}Copied to clipboard.{/color}"
new "\n{color=#fff}Copiado en el portapapeles.{/color}"
# renpy/common/_developer/developer.rpym:588
old "\n{color=#fff}Click to copy.\nDrag to move.{/color}"
new "\n{color=#fff}Haz clic para copiar.\nArrastre para mover.{/color}"
-3
View File
@@ -2147,6 +2147,3 @@
old "This may be derived from build.name and config.version or build.version."
new "Puede derivarse de build.name y config.version o build.version."
# game/distribute.rpy:532
old "Building distributions failed:\n\nThe project is the Ren'Py Tutorial, which can't be distributed outside of Ren'Py. Consider using The Question as a test project."
new "La compilación de distribuciones falló:\n\nEl proyecto es el Tutorial de Ren'Py, que no puede ser distribuido fuera de Ren'Py. Considera usar The Question como proyecto de prueba."
+145 -146
View File
@@ -19,7 +19,7 @@ translate ukrainian strings:
# 00action_file.rpy:26
old "{#weekday}Friday"
new "{#weekday}Пятниця"
new "{#weekday}П'ятниця"
# 00action_file.rpy:26
old "{#weekday}Saturday"
@@ -59,51 +59,51 @@ translate ukrainian strings:
# 00action_file.rpy:47
old "{#month}January"
new "{#month}січня"
new "{#month}Січня"
# 00action_file.rpy:47
old "{#month}February"
new "{#month}лютого"
new "{#month}Лютого"
# 00action_file.rpy:47
old "{#month}March"
new "{#month}березня"
new "{#month}Березня"
# 00action_file.rpy:47
old "{#month}April"
new "{#month}квітня"
new "{#month}Квітня"
# 00action_file.rpy:47
old "{#month}May"
new "{#month}травня"
new "{#month}Травня"
# 00action_file.rpy:47
old "{#month}June"
new "{#month}червня"
new "{#month}Червня"
# 00action_file.rpy:47
old "{#month}July"
new "{#month}липня"
new "{#month}Липня"
# 00action_file.rpy:47
old "{#month}August"
new "{#month}серпня"
new "{#month}Серпня"
# 00action_file.rpy:47
old "{#month}September"
new "{#month}вересня"
new "{#month}Вересня"
# 00action_file.rpy:47
old "{#month}October"
new "{#month}жовтня"
new "{#month}Жовтня"
# 00action_file.rpy:47
old "{#month}November"
new "{#month}листопада"
new "{#month}Листопада"
# 00action_file.rpy:47
old "{#month}December"
new "{#month}грудня"
new "{#month}Грудня"
# 00action_file.rpy:63
old "{#month_short}Jan"
@@ -155,19 +155,19 @@ translate ukrainian strings:
# 00action_file.rpy:240
old "%b %d, %H:%M"
new "%b %d, %H:%M"
new "%d %b, %H:%M"
# 00action_file.rpy:353
old "Save slot %s: [text]"
new "Зберегти комірку %s: [text]"
new "Слот збереження %s: [text]"
# 00action_file.rpy:434
old "Load slot %s: [text]"
new "Завантажити комірку %s: [text]"
new "Слот завантаження %s: [text]"
# 00action_file.rpy:487
old "Delete slot [text]"
new "Видалити комірку [text]"
new "Видалити слот [text]"
# 00action_file.rpy:569
old "File page auto"
@@ -179,19 +179,19 @@ translate ukrainian strings:
# 00action_file.rpy:573
old "File page [text]"
new "Сторінка [text]"
new "Сторінка збережень [text]"
# 00action_file.rpy:763
old "Next file page."
new "Наступна сторінка."
new "Наступна сторінка збережень."
# 00action_file.rpy:827
old "Previous file page."
new "Попередня сторінка"
new "Минула сторінка збережень"
# 00action_file.rpy:888
old "Quick save complete."
new "Швидке збереження виконано."
new "Швидке збереження завершено."
# 00action_file.rpy:906
old "Quick save."
@@ -207,7 +207,7 @@ translate ukrainian strings:
# 00director.rpy:708
old "The interactive director is not enabled here."
new "Інтерактивний режисер тут не ввімкнений."
new "Інтерактивний директор недоступний."
# 00director.rpy:1481
old "⬆"
@@ -219,7 +219,7 @@ translate ukrainian strings:
# 00director.rpy:1551
old "Done"
new "Готово"
new "Прийняти"
# 00director.rpy:1561
old "(statement)"
@@ -231,7 +231,7 @@ translate ukrainian strings:
# 00director.rpy:1563
old "(attributes)"
new "(атрибути)"
new "(атрибут)"
# 00director.rpy:1564
old "(transform)"
@@ -247,7 +247,7 @@ translate ukrainian strings:
# 00director.rpy:1602
old "(filename)"
new "(імя файла)"
new "(ім'я файла)"
# 00director.rpy:1631
old "Change"
@@ -263,19 +263,19 @@ translate ukrainian strings:
# 00director.rpy:1639
old "Remove"
new "Вилучити"
new "Видалити"
# 00director.rpy:1674
old "Statement:"
new "Функція:"
new "Функції:"
# 00director.rpy:1695
old "Tag:"
new "Тег:"
new "Теги:"
# 00director.rpy:1711
old "Attributes:"
new "Атрибути:"
new "Атрибут:"
# 00director.rpy:1729
old "Transforms:"
@@ -287,15 +287,15 @@ translate ukrainian strings:
# 00director.rpy:1767
old "Transition:"
new "Перехід:"
new "Переходи:"
# 00director.rpy:1785
old "Channel:"
new "Канал:"
new "Канали:"
# 00director.rpy:1803
old "Audio Filename:"
new "Імя аудіофайлу::"
new "Ім'я файла:"
# 00gui.rpy:370
old "Are you sure?"
@@ -303,63 +303,63 @@ translate ukrainian strings:
# 00gui.rpy:371
old "Are you sure you want to delete this save?"
new "Ви дійсно бажаєте видалити збереження?"
new "Ви впевнені що хочете видалити це збереження?"
# 00gui.rpy:372
old "Are you sure you want to overwrite your save?"
new "Ви дійсно бажаєте перезаписати збереження?"
new "Ви впевнені що хочете перезаписати ваше збереження?"
# 00gui.rpy:373
old "Loading will lose unsaved progress.\nAre you sure you want to do this?"
new "Завантаження гри приведе до втрати незбереженого прогресу.\nВи дійсно бажаєте це зробити?"
new "Завантаження гри приведе до втрати незбереженого прогресу.\nВи впевнені що хочете це зробити?"
# 00gui.rpy:374
old "Are you sure you want to quit?"
new "Ви дійсно бажаєте вийти?"
new "Ви впевнені що бажаєте вийти?"
# 00gui.rpy:375
old "Are you sure you want to return to the main menu?\nThis will lose unsaved progress."
new "Ви дійсно бажаєте повернутися до головного меню?\nЦе призведе до втрати незбереженого прогресу."
new "Ви впевнені, що хочете повернутися до головного меню?\nЦе призведе до втрати незбереженого прогресу."
# 00gui.rpy:376
old "Are you sure you want to end the replay?"
new "Ви дійсно бажаєте закінчити повтор?"
new "Ви впевнені, що хочете завершити повтор?"
# 00gui.rpy:377
old "Are you sure you want to begin skipping?"
new "Ви дійсно бажаєте почати пропускати?"
new "Ви впевнені, що бажаєте пропустити?"
# 00gui.rpy:378
old "Are you sure you want to skip to the next choice?"
new "Ви дійсно бажаєте пропустити все до наступного вибору?"
new "Ви точно хочете пропустити все до наступного вибору?"
# 00gui.rpy:379
old "Are you sure you want to skip unseen dialogue to the next choice?"
new "Ви дійсно бажаєте пропустити непрочитані діалоги до наступного вибору?"
new "Ви впевнені, що хочете пропустити непрочитані діалоги до наступного вибору?"
# 00keymap.rpy:258
old "Failed to save screenshot as %s."
new "Не вдалося зберегти знімок екрана як %s."
new "Провалено спробу зберегти скріншот як %s."
# 00keymap.rpy:270
old "Saved screenshot as %s."
new "Знімок екрана збережено як %s."
new "Скріншот збережений як %s."
# 00library.rpy:146
old "Self-voicing disabled."
new "Синтез мовлення вимкнено."
new "Синтезатор мови вимкнено."
# 00library.rpy:147
old "Clipboard voicing enabled. "
new "Озвучення буфера обміну увімкнено."
new "Озвучення буфера обміну включено."
# 00library.rpy:148
old "Self-voicing enabled. "
new "Синтез мовлення увімкнено."
new "Синтезатор мови включений."
# 00library.rpy:150
old "bar"
new "смуга"
new "Смуга налаштування"
# 00library.rpy:151
old "selected"
@@ -371,39 +371,39 @@ translate ukrainian strings:
# 00library.rpy:153
old "horizontal scroll"
new "горизонтальне прокручування"
new "горизонтальна смуга прокручування"
# 00library.rpy:154
old "vertical scroll"
new "вертикальне прокручування"
new "вертикальна смуга прокручування"
# 00library.rpy:155
old "activate"
new "увімкнути"
new "елемент активовано"
# 00library.rpy:156
old "deactivate"
new "вимкнути"
new "елемент деактивовано"
# 00library.rpy:157
old "increase"
new "збільшити"
new "більше"
# 00library.rpy:158
old "decrease"
new "зменшити"
new "менше"
# 00library.rpy:193
old "Skip Mode"
new "Режим пропуску"
new "Режим Пропуску"
# 00library.rpy:279
old "This program contains free software under a number of licenses, including the MIT License and GNU Lesser General Public License. A complete list of software, including links to full source code, can be found {a=https://www.renpy.org/l/license}here{/a}."
new "Ця програма містить вільне програмне забезпечення під низкою ліцензій, зокрема ліцензією MIT та GNU Lesser General Public License. Повний список ПЗ, включно з посиланнями на повний вихідний код, можна знайти {a=https://www.renpy.org/l/license}тут{/a}."
new "Ця програма містить вільне та відкрите програмне забезпечення під кількома ліцензіями, включаючи ліцензію MIT та GNU Lesser General Public. Повний список ліцензій, враховуючи посилання на повний вихідний код, можна знайти {a=https://www.renpy.org/l/license}тут{/a}."
# 00preferences.rpy:207
old "display"
new "режим показу"
new "режим екрана"
# 00preferences.rpy:219
old "transitions"
@@ -415,11 +415,11 @@ translate ukrainian strings:
# 00preferences.rpy:230
old "video sprites"
new "відео спрайти"
new "відео-спрайти"
# 00preferences.rpy:239
old "show empty window"
new "показати порожнє вікно"
new "показувати порожнє вікно діалогу"
# 00preferences.rpy:248
old "text speed"
@@ -443,11 +443,11 @@ translate ukrainian strings:
# 00preferences.rpy:271
old "skip unseen text"
new "пропускати увесь текст"
new "пропускати весь текст"
# 00preferences.rpy:273
old "begin skipping"
new "почати пропускати"
new "почати пропуск"
# 00preferences.rpy:277
old "after choices"
@@ -459,23 +459,23 @@ translate ukrainian strings:
# 00preferences.rpy:286
old "auto-forward time"
new "швидкість перемотки"
new "швидкість авточитання"
# 00preferences.rpy:300
old "auto-forward"
new "перемотка"
new "авточитання"
# 00preferences.rpy:307
old "Auto forward"
new "Перемотка"
new "Авточитання"
# 00preferences.rpy:310
old "auto-forward after click"
new "перемотка після дотику"
new "продовжувати авточитання після кліку"
# 00preferences.rpy:319
old "automatic move"
new "автоматичний рух"
new "автоматично пересувати мишу до кнопки" ###
# 00preferences.rpy:328
old "wait for voice"
@@ -483,23 +483,23 @@ translate ukrainian strings:
# 00preferences.rpy:337
old "voice sustain"
new "підтримка голосу"
new "не зупиняти голос"
# 00preferences.rpy:346
old "self voicing"
new "синтез мовлення"
new "озвучка через синтезатор мови"
# 00preferences.rpy:355
old "clipboard voicing"
new "озвучення буфера обміну"
new "синтез мови з буфера обміну"
# 00preferences.rpy:364
old "debug voicing"
new "режим налагодження синтезу мови"
new "режим дебагу синтезу мови"
# 00preferences.rpy:373
old "emphasize audio"
new "виділити звук"
new "посилити гучність заздалегідь заданих звукових каналів рахунок приглушення інших каналів"
# 00preferences.rpy:382
old "rollback side"
@@ -507,15 +507,15 @@ translate ukrainian strings:
# 00preferences.rpy:392
old "gl powersave"
new "економія енергії"
new "налаштування графіки. Економія енергії"
# 00preferences.rpy:398
old "gl framerate"
new "частота кадрів"
new "налаштування графіки. Частота кадрів"
# 00preferences.rpy:401
old "gl tearing"
new "розрив кадрів"
new "налаштування графіки. Розривання кадрів"
# 00preferences.rpy:413
old "music volume"
@@ -523,11 +523,11 @@ translate ukrainian strings:
# 00preferences.rpy:414
old "sound volume"
new "гучність звук. ефектів"
new "гучність звуків"
# 00preferences.rpy:415
old "voice volume"
new "гучність озвучення"
new "гучність голосу"
# 00preferences.rpy:416
old "mute music"
@@ -535,39 +535,39 @@ translate ukrainian strings:
# 00preferences.rpy:417
old "mute sound"
new "без звук. ефектів"
new "без звуків"
# 00preferences.rpy:418
old "mute voice"
new "без озвучення"
new "без голосу"
# 00preferences.rpy:419
old "mute all"
new "без звуку"
new "режим без звуку"
# 00preferences.rpy:500
old "Clipboard voicing enabled. Press 'shift+C' to disable."
new "Озвучення буфера обміну увімкнено. Натисніть 'shift+C', щоб вимкнути."
new "Озвучення буфера обміну увімкнено. Натисніть 'shift+C', щоб вимкнути це."
# 00preferences.rpy:502
old "Self-voicing would say \"[renpy.display.tts.last]\". Press 'alt+shift+V' to disable."
new "Синтез мовлення має сказати \"[renpy.display.tts.last]\". Натисніть 'alt+shift+V', щоб вимкнути."
new "Синтезатор мови повинен сказати \"[renpy.display.tts.last]\". Натисніть 'alt+shift+V', щоб відключити його."
# 00preferences.rpy:504
old "Self-voicing enabled. Press 'v' to disable."
new "Синтез мовлення увімкнено. Натисніть 'v', щоб вимкнути його."
new "Синтезатор мови включено. Натисніть 'v', щоб вимкнути його."
# _compat\gamemenu.rpym:198
old "Empty Slot."
new "Порожній комірка."
new "Порожній слот"
# _compat\gamemenu.rpym:355
old "Previous"
new "Попередня"
new "Назад"
# _compat\gamemenu.rpym:362
old "Next"
new "Наступна"
new "Далі"
# _compat\preferences.rpym:428
old "Joystick Mapping"
@@ -579,7 +579,7 @@ translate ukrainian strings:
# _developer\developer.rpym:43
old "Interactive Director (D)"
new "Інтерактивний режисер (D)"
new "Інтерактивний Директор (D)"
# _developer\developer.rpym:45
old "Reload Game (Shift+R)"
@@ -591,11 +591,11 @@ translate ukrainian strings:
# _developer\developer.rpym:49
old "Variable Viewer"
new "Переглядач змінних"
new "Перегляд змінних"
# _developer\developer.rpym:51
old "Image Location Picker"
new "Вибір розташування зображень"
new "Інструмент позиціонування на зображеннях"
# _developer\developer.rpym:53
old "Filename List"
@@ -603,11 +603,11 @@ translate ukrainian strings:
# _developer\developer.rpym:57
old "Show Image Load Log (F4)"
new "Показати журнал завант. зображень (F4)"
new "Показати лог завантаження зображень (F4)"
# _developer\developer.rpym:60
old "Hide Image Load Log (F4)"
new "Сховати журнал завант. зображень (F4)"
new "Скрыть лог загрузки изображений (F4)"
# _developer\developer.rpym:63
old "Image Attributes"
@@ -623,7 +623,7 @@ translate ukrainian strings:
# _developer\developer.rpym:137
old "Nothing to inspect."
new "Нічого оглядати."
new "Змінні не задано."
# _developer\developer.rpym:265
old "Return to the developer menu"
@@ -639,27 +639,27 @@ translate ukrainian strings:
# _developer\developer.rpym:435
old "Right-click or escape to quit."
new "Натисніть ПКМ або Escape, щоб вийти."
new "Натисніть праву кнопку миші або ESC, щоб вийти."
# _developer\developer.rpym:467
old "Rectangle copied to clipboard."
new "Координати прямокутника скопійовано в буфер обміну."
new "Координати прямокутника скопійовані в буфер обміну."
# _developer\developer.rpym:470
old "Position copied to clipboard."
new "Координати позиції скопійовано у буфер обміну."
new "Координати позиції скопійовані у буфер обміну."
# _developer\developer.rpym:489
old "Type to filter: "
new "Фільтр: "
new "Поточний фільтр: "
# _developer\developer.rpym:617
old "Textures: [tex_count] ([tex_size_mb:.1f] MB)"
new "Текстури: [tex_count] ([tex_size_mb:.1f] Мб)"
new "Текстур: [tex_count] ([tex_size_mb:.1f] МБ)"
# _developer\developer.rpym:621
old "Image cache: [cache_pct:.1f]% ([cache_size_mb:.1f] MB)"
new "Кеш зображень: [cache_pct:.1f]% ([cache_size_mb:.1f] Мб)"
new "Кеш зображень: [cache_pct:.1f]% ([cache_size_mb:.1f] МБ)"
# _developer\developer.rpym:631
old "✔ "
@@ -671,11 +671,11 @@ translate ukrainian strings:
# _developer\developer.rpym:639
old "\n{color=#cfc}✔ predicted image (good){/color}\n{color=#fcc}✘ unpredicted image (bad){/color}\n{color=#fff}Drag to move.{/color}"
new "\n{color=#cfc}✔ передбачене зображення (добре){/color}\n{color=#fcc}✘ раптове зображення (погано){/color}\n{color=#fff}Перетягніть, щоб перемістити.{/color}"
new "\n{color=#cfc}✔ передбачене зображення (добре){/color}\n{color=#fcc}✘ раптове зображення (погано){/color}\n{color=#fff}Натисніть, щоб пересунути.{/color}"
# _developer\inspector.rpym:38
old "Displayable Inspector"
new "Оглядач обєктів"
new "Диспетчер об'єктів"
# _developer\inspector.rpym:61
old "Size"
@@ -691,23 +691,23 @@ translate ukrainian strings:
# _developer\inspector.rpym:122
old "Inspecting Styles of [displayable_name!q]"
new "Оглядання стилів [displayable_name!q]"
new "Інспектую стилі [displayable_name!q]"
# _developer\inspector.rpym:139
old "displayable:"
new "обєкт:"
new "об'єкт:"
# _developer\inspector.rpym:145
old " (no properties affect the displayable)"
new " (на обєкт не впливають жодні параметри)"
new " (на об'єкт не впливають жодні параметри)"
# _developer\inspector.rpym:147
old " (default properties omitted)"
new " (стандартні властивості опущено)"
new " (налаштування за замовчуванням опущено)"
# _developer\inspector.rpym:185
old "<repr() failed>"
new "<repr() failed>"
new "<repr() провалений>"
# _layout\classic_load_save.rpym:170
old "a"
@@ -719,11 +719,11 @@ translate ukrainian strings:
# 00iap.rpy:217
old "Contacting App Store\nPlease Wait..."
new "Звязок з App Store\nБудь ласка, зачекайте..."
new "Зв'язуюсь з App Store\nБудь ласка, чекайте..."
# 00updater.rpy:375
old "The Ren'Py Updater is not supported on mobile devices."
new "Оновлювач Ren'Py не підтримується на мобільних пристроях."
new "Ren'Py Updater не підтримується на мобільних пристроях."
# 00updater.rpy:494
old "An error is being simulated."
@@ -731,35 +731,35 @@ translate ukrainian strings:
# 00updater.rpy:678
old "Either this project does not support updating, or the update status file was deleted."
new "Або цей проєкт не підтримує оновлення, або файл стану оновлення було видалено."
new "Або цей проект не підтримує оновлення, або видалено файл статусу оновлення."
# 00updater.rpy:692
old "This account does not have permission to perform an update."
new "Цей обліковий запис не має дозволу на виконання оновлення."
new "Цей обліковий запис не має права проводити оновлення."
# 00updater.rpy:695
old "This account does not have permission to write the update log."
new "Цей обліковий запис не має дозволу на запис журналу оновлень."
new "Цей обліковий запис не має права писати лог оновлення."
# 00updater.rpy:722
old "Could not verify update signature."
new "Не вдалося перевірити підпис оновлення."
new "Не можу верифікувати підпис оновлення."
# 00updater.rpy:997
old "The update file was not downloaded."
new "Файл оновлення не було завантажено."
new "Файл поновлення не був завантажений."
# 00updater.rpy:1015
old "The update file does not have the correct digest - it may have been corrupted."
new "Файл оновлення не має правильного дайджесту — можливо, він був пошкоджений."
new "Файл поновлення не містить коректного дайджесту — він може бути пошкоджений."
# 00updater.rpy:1071
old "While unpacking {}, unknown type {}."
new "Під час розпакування {}, невідомий тип {}."
new "При розпаковуванні {} виявлено невідомий тип {}."
# 00updater.rpy:1439
old "Updater"
new "Оновлювач"
new "Оновлення"
# 00updater.rpy:1450
old "This program is up to date."
@@ -767,7 +767,7 @@ translate ukrainian strings:
# 00updater.rpy:1452
old "[u.version] is available. Do you want to install it?"
new "[u.version] доступно. Бажаєте її встановити?"
new "[u.version] доступна. Ви бажаєте її встановити?"
# 00updater.rpy:1454
old "Preparing to download the updates."
@@ -799,11 +799,11 @@ translate ukrainian strings:
# 00gallery.rpy:605
old "prev"
new "попер."
new "мин"
# 00gallery.rpy:606
old "next"
new "наст."
new "наст"
# 00gallery.rpy:607
old "slideshow"
@@ -823,11 +823,11 @@ translate ukrainian strings:
# 00accessibility.rpy:76
old "Font Override"
new "Замінити шрифт"
new "Перевизначення шрифту"
# 00accessibility.rpy:80
old "Default"
new "Стандратний"
new "За замовчуванням"
# 00accessibility.rpy:84
old "DejaVu Sans"
@@ -839,7 +839,7 @@ translate ukrainian strings:
# 00accessibility.rpy:94
old "Text Size Scaling"
new "Масштаб розміру тексту"
new "Масштабування розміру тексту"
# 00accessibility.rpy:100
old "Reset"
@@ -847,11 +847,11 @@ translate ukrainian strings:
# 00accessibility.rpy:105
old "Line Spacing Scaling"
new "Масштаб міжрядкового інтервалу"
new "Масштабування міжрядкового інтервалу"
# 00accessibility.rpy:117
old "Self-Voicing"
new "Синтез мовлення"
new "Озвучка через синтезатор мовлення"
# 00accessibility.rpy:121
old "Off"
@@ -859,11 +859,11 @@ translate ukrainian strings:
# 00accessibility.rpy:125
old "Text-to-speech"
new "Перетворення тексту в мовлення"
new "Перетворення тексту на мовлення"
# 00accessibility.rpy:129
old "Clipboard"
new "Буфер обміну"
new "З буфера обміну"
# 00preferences.rpy:430
old "font transform"
@@ -875,23 +875,22 @@ translate ukrainian strings:
# 00preferences.rpy:441
old "font line spacing"
new "міжрядковий інтервал шрифту"
new "міжрядковий інтервал"
# renpy/common/00accessibility.rpy:191
old "The options on this menu are intended to improve accessibility. They may not work with all games, and some combinations of options may render the game unplayable. This is not an issue with the game or engine. For the best results when changing fonts, try to keep the text size the same as it originally was."
new "Параметри цього меню призначені для покращення доступності. Вони можуть працювати не з усіма іграми, а деякі комбінації параметрів можуть зробити гру неможливою для гри. Це не є проблемою гри або рушія. Для досягнення найкращих результатів при зміні шрифтів намагайтеся зберігати розмір тексту таким, яким він був спочатку."
new "Установки цього меню призначені для збільшення доступності. Вони можуть працювати не з усіма іграми, а деякі комбінації опцій можуть зробити гру, що відображається некоректно. Це не проблема з грою чи движком. Для досягнення найкращих результатів при зміні шрифтів намагайтеся зберігати розмір тексту таким, яким він був спочатку."
# renpy/common/00accessibility.rpy:193
old "Self-Voicing Volume Drop"
new "Зменшити гучність синтезу мовлення"
new "Зменшення гучності при озвученні через синтезатор мовлення"
# renpy/common/00preferences.rpy:384
old "self voicing volume drop"
new "зменшити гучність синтезу мовлення"
new "зменшення гучності при озвученні через синтезатор мовлення"
# renpy/common/00preferences.rpy:464
old "system cursor"
new "системний вказівник"
new "системний курсор"
# renpy/common/00accessibility.rpy:180
old "High Contrast Text"
@@ -899,11 +898,11 @@ translate ukrainian strings:
# renpy/common/00preferences.rpy:487
old "renderer menu"
new "меню візуалізації"
new "меню рендерера"
# renpy/common/00preferences.rpy:490
old "accessibility menu"
new "меню доступності"
new "меню спеціальних можливостей"
# renpy/common/00preferences.rpy:493
old "high contrast text"
@@ -911,15 +910,15 @@ translate ukrainian strings:
# renpy/common/00preferences.rpy:511
old "audio when minimized"
new "звук при згортанні"
new "звук при згортанні вікна"
# renpy/common/00preferences.rpy:531
old "main volume"
new "основна гучність"
new "загальна гучність"
# renpy/common/00preferences.rpy:535
old "mute main"
new "без основи"
new "режим без звуку"
translate ukrainian strings:
@@ -937,31 +936,31 @@ translate ukrainian strings:
# renpy/common/00director.rpy:1745
old "Click to toggle attribute, right click to toggle negative attribute."
new "Натисніть, аби увімкнути атрибут, натисніть ПКМ, аби увімкнути від’ємний атрибут."
new "Клацніть, аби увімкнути атрибут, клацніть ПКМ, аби увімкнути від’ємний атрибут."
# renpy/common/00director.rpy:1768
old "Click to set transform, right click to add to transform list."
new "Натисніть, щоб встановити трансформацію, натисніть ПКМ, щоб додати до списку трансформацій."
new "Клацніть, щоб встановити трансформацію, клацніть ПКМ, щоб додати до списку трансформацій."
# renpy/common/00director.rpy:1789
old "Click to set, right click to add to behind list."
new "Натисніть, щоб встановити, натисніть ПКМ, щоб додати до списку."
new "Клацніть, щоб встановити, клацніть ПКМ, щоб додати до списку."
# renpy/common/00gui.rpy:456
old "This save was created on a different device. Maliciously constructed save files can harm your computer. Do you trust this save's creator and everyone who could have changed the file?"
new "Це збереження було створено на іншому пристрої. Зловмисно створені файли збережень можуть завдати шкоди вашому пристрою. Чи довіряєте ви творцю цього збереження і всім, хто міг змінити файл?"
new "Це збереження було створено на іншому пристрої. Зловмисно створені файли збережень можуть завдати шкоди вашому комп'ютеру. Чи довіряєте ви творцю цього збереження і всім, хто міг змінити файл?"
# renpy/common/00gui.rpy:457
old "Do you trust the device the save was created on? You should only choose yes if you are the device's sole user."
new "Чи довіряєте ви пристрою, на якому було створено збереження? Ви повинні вибрати «Так», тільки якщо ви є єдиним користувачем пристрою."
new "Чи довіряєте ви пристрою, на якому було створено збереження? Ви повинні вибрати \"Так\", тільки якщо ви є єдиним користувачем пристрою."
# renpy/common/00preferences.rpy:528
old "audio when unfocused"
new "звук при згортанні вікна"
new "звук при розфокусуванні"
# renpy/common/00preferences.rpy:537
old "web cache preload"
new "попер. завантаження вебкешу"
new "попереднє завантаження веб-кешу"
# renpy/common/00preferences.rpy:552
old "voice after game menu"
@@ -981,7 +980,7 @@ translate ukrainian strings:
# renpy/common/00sync.rpy:190
old "Could not connect to the Ren'Py Sync server."
new "Не вдалося під’єднатися до сервера Ren'Py Sync."
new "Не вдалося підключитися до сервера Ren'Py Sync."
# renpy/common/00sync.rpy:192
old "The Ren'Py Sync server timed out."
@@ -989,7 +988,7 @@ translate ukrainian strings:
# renpy/common/00sync.rpy:194
old "An unknown error occurred while connecting to the Ren'Py Sync server."
new "Під час з’єднання до сервера Ren'Py Sync виникла невідома помилка."
new "Під час підключення до сервера Ren'Py Sync виникла невідома помилка."
# renpy/common/00sync.rpy:267
old "The Ren'Py Sync server does not have a copy of this sync. The sync ID may be invalid, or it may have timed out."
@@ -1017,7 +1016,7 @@ translate ukrainian strings:
# renpy/common/00sync.rpy:529
old "This will upload your saves to the {a=https://sync.renpy.org}Ren'Py Sync Server{/a}.\nDo you want to continue?"
new "Це завантажить ваші збереження на {a=https://sync.renpy.org}сервер Ren'Py Sync{/a}.\nБажаєте продовжити?"
new "Це завантажить ваші збереження на сервер синхронізації {a=https://sync.renpy.org}Ren'Py Sync{/a}.\nБажаєте продовжити?"
# renpy/common/00sync.rpy:558
old "Enter Sync ID"
@@ -1025,7 +1024,7 @@ translate ukrainian strings:
# renpy/common/00sync.rpy:569
old "This will contact the {a=https://sync.renpy.org}Ren'Py Sync Server{/a}."
new "Зв’язок зі {a=https://sync.renpy.org}сервером Ren'Py Sync{/a}."
new "Зв’язок зі сервером синхронізації {a=https://sync.renpy.org}Ren'Py Sync{/a}."
# renpy/common/00sync.rpy:596
old "Sync Success"
@@ -1037,7 +1036,7 @@ translate ukrainian strings:
# renpy/common/00sync.rpy:605
old "You can use this ID to download your save on another device.\nThis sync will expire in an hour.\nRen'Py Sync is supported by {a=https://www.renpy.org/sponsors.html}Ren'Py's Sponsors{/a}."
new "Ви можете використати цей ідентифікатор, щоб завантажити збереження на інший пристрій.\nЦя синхронізація завершиться за годину.\nRen'Py Sync підтримується за підтримки {a=https://www.renpy.org/sponsors.html}спонсорів Ren'Py{/a}."
new "Ви можете використовувати цей ідентифікатор, щоб завантажити збереження на інший пристрій.\nЦя синхронізація закінчиться через годину.\nRen'Py Sync підтримується {a=https://www.renpy.org/sponsors.html}спонсорами Ren'Py{/a}."
# renpy/common/00sync.rpy:631
old "Sync Error"
+23 -23
View File
@@ -3,19 +3,19 @@ translate ukrainian strings:
# 00console.rpy:255
old "Press <esc> to exit console. Type help for help.\n"
new "Натисніть <esc>, щоб вийти із консолі. Введіть help для довідки.\n"
new "Натисніть <esc>, щоб вийти із консолі. Введіть <help> для допомоги.\n"
# 00console.rpy:259
old "Ren'Py script enabled."
new "Скрипт Ren'Py увімкено."
new "Ren'Py script активовано."
# 00console.rpy:261
old "Ren'Py script disabled."
new "Скрипт Ren'Py вимкнуто."
new "Ren'Py script деактивовано."
# 00console.rpy:496
old "help: show this help"
new "help: показує цю довідку"
new "help: показує допомогу"
# 00console.rpy:501
old "commands:\n"
@@ -23,27 +23,27 @@ translate ukrainian strings:
# 00console.rpy:511
old " <renpy script statement>: run the statement\n"
new " <функція скрипту renpy>: запуск функції\n"
new " <оператор renpy script>: запуск оператора\n"
# 00console.rpy:513
old " <python expression or statement>: run the expression or statement"
new " <вираз або функція python>: запуск виразу або функції"
new " <вираз або оператор python>: запустити вираз або оператор"
# 00console.rpy:521
old "clear: clear the console history"
new "clear: очистити історію консолі"
new "clear: очищення історії консолі"
# 00console.rpy:525
old "exit: exit the console"
new "exit: вийти із консолі"
new "exit: вихід із консолі"
# 00console.rpy:533
old "load <slot>: loads the game from slot"
new "load <комірка>: завантажує гру з вибраної комірки"
new "load <слот>: завантажує гру з вибраного слота"
# 00console.rpy:546
old "save <slot>: saves the game in slot"
new "save <комірка>: зберігає гру у вибраній комірці"
new "save <слот>: зберігає гру у вибраний слот"
# 00console.rpy:557
old "reload: reloads the game, refreshing the scripts"
@@ -51,49 +51,49 @@ translate ukrainian strings:
# 00console.rpy:565
old "watch <expression>: watch a python expression"
new "watch <вираз>: дивитися вираз python"
new "watch <вираз>: спостерігати за виразом python"
# 00console.rpy:591
old "unwatch <expression>: stop watching an expression"
new "unwatch <вираз>: припинити дивитися вираз"
new "unwatch <вираз>: припинити спостерігати за виразом"
# 00console.rpy:622
old "unwatchall: stop watching all expressions"
new "unwatchall: припинити дивитися за всіма виразами"
new "unwatchall: глобальне припинення спостереження"
# 00console.rpy:639
old "jump <label>: jumps to label"
new "jump <мітка>: перейти на мітку"
new "jump <мітка>: стрибок на мітку"
# 00console.rpy:685
old "short: Shorten the representation of objects on the console (default)."
new "short: Скоротити представлення обєктів на консолі (за стандартом)."
new "short: Скорочене представлення об'єктів (repr) у консолі (за замовчуванням)."
# 00console.rpy:690
old "long: Print the full representation of objects on the console."
new "long: Вивести повне представлення обєктів на консолі."
new "long: Виводити повне уявлення об'єктів (repr) у консолі."
# renpy/common/00console.rpy:814
old "watch <expression>: watch a python expression\n watch short: makes the representation of traced expressions short (default)\n watch long: makes the representation of traced expressions as is"
new "watch <вираз>: дивитися вираз python\n watch short: робить представлення відстежених виразів коротким (за стандартом)\n watch long: робить представлення відстежених виразів як є"
new "watch <вираз>: спостерігати за виразом python\n watch short: вкорочує відображення виразів, що відстежуються (за замовчуванням)\n watch long: робить відображення відстежуваних виразів як є"
# renpy/common/00console.rpy:925
old "escape: Enables escaping of unicode symbols in unicode strings."
new "escape: Вмикає екранування символів Unicode в рядках Unicode."
new "escape: Включає екранування Unicode символів у рядках unicode."
# renpy/common/00console.rpy:929
old "unescape: Disables escaping of unicode symbols in unicode strings and print it as is (default)."
new "unescape: Вимикає екранування символів Unicode в рядках Unicode і друкує їх як є (за стандартом)."
new "unescape: Вимикає екранування Unicode символів у рядках unicode та виводить їх як є (за замовчуванням)."
# renpy/common/00console.rpy:784
old "stack: print the return stack"
new "stack: вивести стек повернення"
new "stack: виводить стек повернення (return stack)"
translate ukrainian strings:
# renpy/common/_developer/developer.rpym:51
old "Persistent Viewer"
new "Переглядач даних"
new "Переглядач постійних даних"
# renpy/common/_developer/developer.rpym:70
old "Speech Bubble Editor (Shift+B)"
@@ -101,9 +101,9 @@ translate ukrainian strings:
# renpy/common/00console.rpy:789
old "help: show this help\n help <expr>: show signature and documentation of <expr>"
new "help: показує цю довідку\n help <вираз>: показує підпис та документацію <виразу>"
new "help: показати цю довідку\n help <expr>: показати підпис та документацію <expr>"
# renpy/common/00console.rpy:813
old "Help may display undocumented functions. Please check that the function or\nclass you want to use is documented.\n\n"
new "У довідці можуть відображатися недокументовані функції. Будь ласка, перевірте, чи функцію або\nклас, який бажаєте використати, задокументовано.\n\n"
new "У довідці можуть відображатися недокументовані функції. Будь ласка, перевірте, чи функцію або\nклас, який ви хочете використати, задокументовано.\n\n"
+27 -27
View File
@@ -7,23 +7,23 @@ translate ukrainian strings:
# 00gltest.rpy:74
old "Automatically Choose"
new "Обрати автоматично"
new "Вибрати автоматично"
# 00gltest.rpy:79
old "Force Angle/DirectX Renderer"
new "Візуалізація Angle/DirectX"
new "Примусова візуалізація Angle/DirectX"
# 00gltest.rpy:83
old "Force OpenGL Renderer"
new "Візуалізація OpenGL"
new "Примусова візуалізація OpenGL"
# 00gltest.rpy:87
old "Force Software Renderer"
new "Програмна візуалізація"
new "Примусова програмна візуалізація"
# 00gltest.rpy:93
old "NPOT"
new "NPOT"
new "NPOT (OpenGL 2+)"
# 00gltest.rpy:97
old "Enable"
@@ -51,7 +51,7 @@ translate ukrainian strings:
# 00gltest.rpy:163
old "Tearing"
new "Розрив кадрів"
new "Розривання кадрів"
# 00gltest.rpy:179
old "Changes will take effect the next time this program is run."
@@ -79,11 +79,11 @@ translate ukrainian strings:
# 00gltest.rpy:229
old "Its graphics drivers may be out of date or not operating correctly. This can lead to slow or incorrect graphics display. Updating DirectX could fix this problem."
new "Графічні драйвери можуть бути застарілими або працювати неправильно. Це може призвести до повільного або некоректного відображення графіки. Оновлення DirectX може вирішити цю проблему."
new "Графічні драйвера застаріли чи працюють неправильно. Це може призвести до повільного або неправильного відображення графіки. Оновлення DirectX може вирішити цю проблему."
# 00gltest.rpy:231
old "Its graphics drivers may be out of date or not operating correctly. This can lead to slow or incorrect graphics display."
new "Графічні драйвери можуть бути застарілими або працювати неправильно. Це може призвести до повільного або некоректного відображення графіки."
new "Графічні драйвера застаріли чи працюють неправильно. Це може призвести до повільного або неправильного відображення графіки."
# 00gltest.rpy:236
old "Update DirectX"
@@ -91,7 +91,7 @@ translate ukrainian strings:
# 00gltest.rpy:242
old "Continue, Show this warning again"
new "Продовжити, показувати знову"
new "Продовжити, показати знову"
# 00gltest.rpy:246
old "Continue, Don't show warning again"
@@ -103,11 +103,11 @@ translate ukrainian strings:
# 00gltest.rpy:268
old "DirectX web setup has been started. It may start minimized in the taskbar. Please follow the prompts to install DirectX."
new "Запущено вебналаштування DirectX. Вона може згорнутися на панелі завдань. Будь ласка, дотримуйтесь підказок для встановлення DirectX."
new "Запущено веб-налаштування DirectX. Вона може згорнутися на панелі завдань. Будь ласка, дотримуйтесь підказок для встановлення DirectX."
# 00gltest.rpy:272
old "{b}Note:{/b} Microsoft's DirectX web setup program will, by default, install the Bing toolbar. If you do not want this toolbar, uncheck the appropriate box."
new "{b}Нотатка:{/b} Програма вебналаштування DirectX від Microsoft за стандартом встановлює панель інструментів Bing. Якщо вам не потрібна ця панель, зніміть позначку у відповідному полі."
new "{b}Примітка:{/b} Інсталятор DirectX за замовчуванням намагається встановити панель інструментів Bing. Якщо ви цього не хочете, зніміть відповідну галочку."
# 00gltest.rpy:276
old "When setup finishes, please click below to restart this program."
@@ -119,11 +119,11 @@ translate ukrainian strings:
# 00gamepad.rpy:32
old "Select Gamepad to Calibrate"
new "Оберіть ґеймпад для калібрування"
new "Виберіть геймпад для калібрування"
# 00gamepad.rpy:35
old "No Gamepads Available"
new "Ґеймпадів не виявлено"
new "Геймпадів не виявлено"
# 00gamepad.rpy:54
old "Calibrating [name] ([i]/[total])"
@@ -131,7 +131,7 @@ translate ukrainian strings:
# 00gamepad.rpy:58
old "Press or move the [control!s] [kind]."
new "Натисніть або рухніть [control!s] [kind]."
new "Натисніть або перемістіть [control!s] [kind]."
# 00gamepad.rpy:66
old "Skip (A)"
@@ -167,7 +167,7 @@ translate ukrainian strings:
# _errorhandling.rpym:584
old "Attempts a roll back to a prior time, allowing you to save or choose a different choice."
new "Спроба відкату до попереднього часу, що дозволяє зберегти або вибрати інший варіант."
new "Спроба відкоту до попереднього часу, що дозволяє зберегти або вибрати інший варіант."
# _errorhandling.rpym:587
old "Ignore"
@@ -203,7 +203,7 @@ translate ukrainian strings:
# _errorhandling.rpym:638
old "Parsing the script failed."
new "Розбір скрипта не вдався."
new "Опрацювання скрипту завершилося невдало."
# _errorhandling.rpym:664
old "Opens the errors.txt file in a text editor."
@@ -219,7 +219,7 @@ translate ukrainian strings:
# _errorhandling.rpym:544
old "Copies the traceback.txt file to the clipboard as BBcode for forums like https://lemmasoft.renai.us/."
new "Копіює файл traceback.txt у буфер обміну як BBCode для форумів як https://lemmasoft.renai.us/."
new "Копіює файл traceback.txt у буфер обміну як BBCode для форумів типу https://lemmasoft.renai.us/."
# _errorhandling.rpym:546
old "Copy Markdown"
@@ -231,35 +231,35 @@ translate ukrainian strings:
# _errorhandling.rpym:683
old "Copies the errors.txt file to the clipboard as BBcode for forums like https://lemmasoft.renai.us/."
new "Копіює файл errors.txt у буфер обміну як BBCode для форумів як https://lemmasoft.renai.us/."
new "Копіює файл errors.txt у буфер обміну як BBCode для форумів типу https://lemmasoft.renai.us/."
# _errorhandling.rpym:687
old "Copies the errors.txt file to the clipboard as Markdown for Discord."
new "Копіює файл errors.txt у буфер обміну як Markdown для Discord."
new "Копіює файл errors.txt у буфер обміну як Markdown для Дискорду."
# renpy/common/00gltest.rpy:100
old "Force GL Renderer"
new "Візуалізація GL"
new "Примусова візуалізація GL"
# renpy/common/00gltest.rpy:105
old "Force ANGLE Renderer"
new "Візуалізація ANGLE"
new "Примусова візуалізація ANGLE"
# renpy/common/00gltest.rpy:110
old "Force GLES Renderer"
new "Візуалізація GLES"
new "Примусова візуалізація GLES"
# renpy/common/00gltest.rpy:116
old "Force GL2 Renderer"
new "Візуалізація GL2"
new "Примусова візуалізація GL2"
# renpy/common/00gltest.rpy:121
old "Force ANGLE2 Renderer"
new "Візуалізація ANGLE2"
new "Примусова візуалізація ANGLE2"
# renpy/common/00gltest.rpy:126
old "Force GLES2 Renderer"
new "Візуалізація GLES2"
new "Примусова візуалізація GLES2"
# renpy/common/00gltest.rpy:245
old "This game requires use of GL2 that can't be initialised."
@@ -271,11 +271,11 @@ translate ukrainian strings:
# renpy/common/00gltest.rpy:273
old "Change render options"
new "Змінити параметри візцалізації"
new "Змінити налаштування рендерингу"
# renpy/common/00gamepad.rpy:58
old "Press or move the '[control!s]' [kind]."
new "Натисніть або рухніть '[control!s]' [kind]."
new "Натисніть або посуньте [kind] '[control!s]'"
# renpy/common/00gltest.rpy:136
old "Enable (No Blocklist)"
File diff suppressed because it is too large Load Diff
+44 -40
View File
@@ -11,15 +11,15 @@ translate ukrainian strings:
# gui/game/screens.rpy:85
old "## Say screen"
new "## Екран Say"
new "## Екран розмови"
# gui/game/screens.rpy:87
old "## The say screen is used to display dialogue to the player. It takes two parameters, who and what, which are the name of the speaking character and the text to be displayed, respectively. (The who parameter can be None if no name is given.)"
new "## Екран Say використовується для відображення діалогу гравцеві. Він приймає два параметри, who і what, імя персонажа, що говорить, і текст, який буде відображатися відповідно. (Параметр who може мати значення None, якщо ім’я не вказано.)"
new "## Екран say використовується для відображення діалогу гравцеві. Він приймає два параметри, хто і що, ім'я персонажа, що говорить, і текст, який буде відображатися відповідно. (Параметр who може мати значення None, якщо ім’я не вказано.)"
# gui/game/screens.rpy:92
old "## This screen must create a text displayable with id \"what\", as Ren'Py uses this to manage text display. It can also create displayables with id \"who\" and id \"window\" to apply style properties."
new "## Цей екран має створювати текст, який можна відображати з ідентифікатором «what», оскільки Ren'Py використовує це для керування відображенням тексту. Він також може створювати відображувані елементи з ідентифікаторами «who» та «window» для застосування властивостей стилю."
new "## Цей екран має створювати текст, який можна відображати з ідентифікатором \"what\", оскільки Ren'Py використовує це для керування відображенням тексту. Він також може створювати відображувані елементи з ідентифікаторами \"who\" та ідентифікаторами \"window\" для застосування властивостей стилю."
# gui/game/screens.rpy:96
old "## https://www.renpy.org/doc/html/screen_special.html#say"
@@ -27,11 +27,11 @@ translate ukrainian strings:
# gui/game/screens.rpy:114
old "## If there's a side image, display it above the text. Do not display on the phone variant - there's no room."
new "Якщо є бічне зображення, показуйте його над текстом. Не виводьте на мобільний варіант немає місця."
new "## Якщо є зображення збоку, відобразіть його над текстом. Не відображати на телефоні як варіант - немає місця."
# gui/game/screens.rpy:120
old "## Make the namebox available for styling through the Character object."
new "## Робить поле імені доступним для стилізації за допомогою обєкта Character."
new "## Робить поле імен доступним для стилізації через об'єкт Character."
# gui/game/screens.rpy:165
old "## Input screen"
@@ -43,7 +43,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:170
old "## This screen must create an input displayable with id \"input\" to accept the various input parameters."
new "## Цей екран має створити відображуваний вхід з ідентифікатором «input» для прийняття різних вхідних параметрів."
new "## Цей екран має створити відображуваний вхід з ідентифікатором \"input\" для прийняття різних вхідних параметрів."
# gui/game/screens.rpy:173
old "## https://www.renpy.org/doc/html/screen_special.html#input"
@@ -55,7 +55,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:202
old "## This screen is used to display the in-game choices presented by the menu statement. The one parameter, items, is a list of objects, each with caption and action fields."
new "## Цей екран використовується для відображення вибору в грі, представленого командою 'menu'. Один параметр, 'items' — це список об’єктів, кожен із полями заголовка та дії."
new "## Цей екран використовується для відображення вибору в грі, представленого командою 'menu'. Один параметр, 'items', — це список об’єктів, кожен із полями заголовка та дії."
# gui/game/screens.rpy:206
old "## https://www.renpy.org/doc/html/screen_special.html#choice"
@@ -135,7 +135,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:314
old "End Replay"
new "Закінчити повтор"
new "Закінчити повтору"
# gui/game/screens.rpy:318
old "Main Menu"
@@ -155,7 +155,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:329
old "## The quit button is banned on iOS and unnecessary on Android and Web."
new "## Кнопка виходу заборонена на iOS і непотрібна на Android і в Web."
new "## Кнопка виходу заборонена на iOS і непотрібна на Android і в Веб."
# gui/game/screens.rpy:330
old "Quit"
@@ -191,11 +191,11 @@ translate ukrainian strings:
# gui/game/screens.rpy:408
old "## This lays out the basic common structure of a game menu screen. It's called with the screen title, and displays the background, title, and navigation."
new "## Це описує базову структуру екрана меню гри. Він викликається за допомогою заголовка екрана та відображає тло, заголовок і навігацію."
new "## Це описує базову структуру екрана меню гри. Він викликається за допомогою заголовка екрана та відображає фон, заголовок і навігацію."
# gui/game/screens.rpy:411
old "## The scroll parameter can be None, or one of \"viewport\" or \"vpgrid\". This screen is intended to be used with one or more children, which are transcluded (placed) inside it."
new "## Параметр прокручування може бути None або одним із «viewport» чи «vpgrid». Цей екран призначений для використання з одним або декількома обєктами, які включені (розміщені) всередині нього."
new "## Параметр прокручування може бути None або одним із \"viewport\" чи \"vpgrid\". Цей екран призначений для використання з одним або декількома об'єктами, які включені (розміщені) всередині нього."
# gui/game/screens.rpy:429
old "## Reserve space for the navigation section."
@@ -219,7 +219,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:546
old "## This use statement includes the game_menu screen inside this one. The vbox child is then included inside the viewport inside the game_menu screen."
new "## Цей оператор використання включає екран «game_menu» всередині цього. Потім дочірній елемент «vbox» включається в область перегляду всередині екрана «game_menu»."
new "## Цей оператор використання включає екран game_menu всередині цього. Потім дочірній елемент vbox включається в область перегляду всередині екрана game_menu."
# gui/game/screens.rpy:556
old "Version [config.version!t]\n"
@@ -227,7 +227,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:558
old "## gui.about is usually set in options.rpy."
new "## «gui.about» зазвичай встановлюється в «options.rpy»."
new "## gui.about зазвичай встановлюється в options.rpy."
# gui/game/screens.rpy:562
old "Made with {a=https://www.renpy.org/}Ren'Py{/a} [renpy.version_only].\n\n[renpy.license!t]"
@@ -239,7 +239,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:575
old "## These screens are responsible for letting the player save the game and load it again. Since they share nearly everything in common, both are implemented in terms of a third screen, file_slots."
new "## Ці екрани дозволяють гравцеві зберегти гру та завантажити її знову. Оскільки вони мають майже все спільне, обидва реалізовані в термінах третього екрана, «file_slots»."
new "## Ці екрани дозволяють гравцеві зберегти гру та завантажити її знову. Оскільки вони мають майже все спільне, обидва реалізовані в термінах третього екрана, file_slots."
# gui/game/screens.rpy:579
old "## https://www.renpy.org/doc/html/screen_special.html#save https://www.renpy.org/doc/html/screen_special.html#load"
@@ -251,7 +251,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:598
old "Automatic saves"
new "Автоматичні збереження"
new "Автозбереження"
# gui/game/screens.rpy:598
old "Quick saves"
@@ -259,7 +259,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:604
old "## This ensures the input will get the enter event before any of the buttons do."
new "## Це гарантує, що введення отримає подію «enter» раніше, ніж будь-яка кнопка."
new "## Це гарантує, що введення отримає подію enter раніше, ніж будь-яка кнопка."
# gui/game/screens.rpy:608
old "## The page name, which can be edited by clicking on a button."
@@ -287,7 +287,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:660
old "{#auto_page}A"
new "{#auto_page}А"
new "{#auto_page}A"
# gui/game/screens.rpy:663
old "{#quick_page}Q"
@@ -295,7 +295,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:665
old "## range(1, 10) gives the numbers from 1 to 9."
new "## range(1, 10) дає числа від 1 до 9."
new "## діапазон(1, 10) дає числа від 1 до 9."
# gui/game/screens.rpy:669
old ">"
@@ -339,7 +339,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:737
old "## Additional vboxes of type \"radio_pref\" or \"check_pref\" can be added here, to add additional creator-defined preferences."
new "## Сюди можна долучити додаткові вікна vbox типу «radio_pref» або «check_pref», щоб долучити додаткові параметри, визначені творцем."
new "## Сюди можна долучити додаткові вікна vbox типу \"radio_pref\" або \"check_pref\", щоб долучити додаткові параметри, визначені творцем."
# gui/game/screens.rpy:748
old "Text Speed"
@@ -347,7 +347,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:752
old "Auto-Forward Time"
new "Швидкість перемотки"
new "Швидкість авточитання"
# gui/game/screens.rpy:759
old "Music Volume"
@@ -355,7 +355,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:766
old "Sound Volume"
new "Гучність звук. ефектів"
new "Гучність ефектів"
# gui/game/screens.rpy:772
old "Test"
@@ -363,7 +363,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:776
old "Voice Volume"
new "Гучність озвучення"
new "Гучність голосу"
# gui/game/screens.rpy:787
old "Mute All"
@@ -383,15 +383,15 @@ translate ukrainian strings:
# gui/game/screens.rpy:875
old "## Avoid predicting this screen, as it can be very large."
new "## Не намагайтеся передбачити цей екран, оскільки він може бути дуже великим."
new "## Уникайте прогнозування цього екрана, оскільки він може бути дуже великим."
# gui/game/screens.rpy:886
old "## This lays things out properly if history_height is None."
new "## Тут все буде правильно, якщо history_height дорівнює None."
new "## Це правильно розкладає речі, якщо history_height дорівнює None."
# gui/game/screens.rpy:896
old "## Take the color of the who text from the Character, if set."
new "## Бере колір тексту who з Character, якщо він заданий."
new "## Взяти колір тексту що від персонажа, якщо встановлено."
# gui/game/screens.rpy:905
old "The dialogue history is empty."
@@ -419,7 +419,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:978
old "Gamepad"
new "Ґеймпад"
new "Геймпад"
# gui/game/screens.rpy:991
old "Enter"
@@ -427,7 +427,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:992
old "Advances dialogue and activates the interface."
new "Просуває діалог і вмикає інтерфейс."
new "Розвиває діалог і активує інтерфейс."
# gui/game/screens.rpy:995
old "Space"
@@ -439,7 +439,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:999
old "Arrow Keys"
new "Клавіші зі стрілками"
new "Стрілки"
# gui/game/screens.rpy:1000
old "Navigate the interface."
@@ -495,11 +495,11 @@ translate ukrainian strings:
# gui/game/screens.rpy:1032
old "Toggles assistive {a=https://www.renpy.org/l/voicing}self-voicing{/a}."
new "Вмикає допоміжний {a=https://www.renpy.org/l/voicing}синтез мовлення{/a}."
new "Вмикає допоміжне {a=https://www.renpy.org/l/voicing}самоозвучення{/a}."
# gui/game/screens.rpy:1036
old "Opens the accessibility menu."
new "Відкриває меню доступності."
new "Відкриває меню спеціальних можливостей."
# gui/game/screens.rpy:1042
old "Left Click"
@@ -515,11 +515,11 @@ translate ukrainian strings:
# gui/game/screens.rpy:1054
old "Mouse Wheel Up\nClick Rollback Side"
new "Коліщатко миші вгору\nСторона відкату"
new "Колесо миші вгору\nбік повернення назад"
# gui/game/screens.rpy:1058
old "Mouse Wheel Down"
new "Коліщатко миші вниз"
new "Колесо миші вниз"
# gui/game/screens.rpy:1065
old "Right Trigger\nA/Bottom Button"
@@ -535,15 +535,15 @@ translate ukrainian strings:
# gui/game/screens.rpy:1078
old "D-Pad, Sticks"
new "Хрестовина, Стики"
new "D-Pad, Стіки"
# gui/game/screens.rpy:1082
old "Start, Guide"
new "Start, Guide"
new "Старт, Гайд"
# gui/game/screens.rpy:1086
old "Y/Top Button"
new "Y/Верхня кнопка"
new "Кнопка Y/Top"
# gui/game/screens.rpy:1089
old "Calibrate"
@@ -559,7 +559,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:1123
old "## The confirm screen is called when Ren'Py wants to ask the player a yes or no question."
new "## Екран підтвердження викликається, коли Ren'Py хоче поставити гравцеві запитання «Так» або «Ні»."
new "## Екран підтвердження викликається, коли Ren'Py хоче поставити гравцеві запитання «так» або «ні»."
# gui/game/screens.rpy:1126
old "## https://www.renpy.org/doc/html/screen_special.html#confirm"
@@ -579,7 +579,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:1157
old "## Right-click and escape answer \"no\"."
new "## Клацніть ПКМ та вийдіть із відповіді «Ні»."
new "## Клацніть правою кнопкою миші та вийдіть із відповіді \"no\"."
# gui/game/screens.rpy:1184
old "## Skip indicator screen"
@@ -623,7 +623,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:1281
old "## This screen is used for NVL-mode dialogue and menus."
new "## Цей екран використовується для діалогу та меню режиму NVL."
new "## Відображає діалог у vpgrid або vbox."
# gui/game/screens.rpy:1283
old "## https://www.renpy.org/doc/html/screen_special.html#nvl"
@@ -635,7 +635,7 @@ translate ukrainian strings:
# gui/game/screens.rpy:1307
old "## Displays the menu, if given. The menu may be displayed incorrectly if config.narrator_menu is set to True."
new "## Відображає меню, якщо є. Меню може відображатися неправильно, якщо для config.narrator_menu задано значення True."
new "## Відображає меню, якщо є. Меню може відображатися неправильно, якщо для config.narrator_menu встановлено значення True."
# gui/game/screens.rpy:1337
old "## This controls the maximum number of NVL-mode entries that can be displayed at once."
@@ -658,19 +658,23 @@ translate ukrainian strings:
# gui/game/screens.rpy:676
old "Upload Sync"
new "Вивантажити синхронізацію"
# gui/game/screens.rpy:680
old "Download Sync"
new "Завантажити синхронізацію"
# gui/game/screens.rpy:1410
old "## Bubble screen"
new "## Екран з бульбашками"
# gui/game/screens.rpy:1412
old "## The bubble screen is used to display dialogue to the player when using speech bubbles. The bubble screen takes the same parameters as the say screen, must create a displayable with the id of \"what\", and can create displayables with the \"namebox\", \"who\", and \"window\" ids."
new "## Екран бульбашок використовується для показу діалогу гравцеві під час використання мовних бульбашок. Екран бульбашок має ті самі параметри, що й екран слів, має створювати елемент відображення з ідентифікатором «what», а також може створювати елементи відображення з ідентифікаторами «namebox», «who» і «window»."
new "## Екран бульбашок використовується для показу діалогу гравцеві під час використання мовних бульбашок. Екран бульбашок має ті самі параметри, що й екран слів, має створювати елемент відображення з ідентифікатором \"what\", а також може створювати елементи відображення з ідентифікаторами \"namebox\", \"who\" і \"window\"."
# gui/game/screens.rpy:1417
old "## https://www.renpy.org/doc/html/bubble.html#bubble-screen"
+5 -5
View File
@@ -1,7 +1,7 @@
# У цьому файлі міститься скрипт гри.
# Оголошення персонажів, які використовуються в цій грі. Колірний аргумент розфарбовує
# імя персонажа.
# Оголошення символів, які використовуються в цій грі. Колірний аргумент розфарбовує
# ім'я персонажа.
define e = Character("Ейлін")
@@ -10,14 +10,14 @@ define e = Character("Ейлін")
label start:
# Це показує тло. За стандратом використовується заповнювач, але ви можете
# додати файл (з назвою «bg room.png» або «bg room.jpg») до
# Це показує фон. За замовчуванням використовується заповнювач, але ви можете
# додати файл (з назвою "bg room.png" або "bg room.jpg") до
# теки images, щоб показати його.
scene bg room
# Це показує спрайт персонажа. Використовується заповнювач, але ви можете
# замінити його, додавши до зображень файл із назвою «eileen happy.png».
# замінити його, додавши до зображень файл із назвою "eileen happy.png".
# до теки.
show eileen happy
+5 -4
View File
@@ -115,7 +115,7 @@ screen update_channel(channels):
for c in channels:
if c["split_version"] != list(renpy.version_tuple) or allow_repair_update:
$ action = [SetField(persistent, "has_update", None), SetField(persistent, "last_update_check", None), updater.Update(c["url"], simulate=UPDATE_SIMULATE, public_key=PUBLIC_KEY, confirm=False, force=allow_repair_update, prefer_rpu=c.get("prefer_rpu", False))]
$ action = [SetField(persistent, "has_update", None), SetField(persistent, "last_update_check", None), updater.Update(c["url"], simulate=UPDATE_SIMULATE, public_key=PUBLIC_KEY, confirm=False, force=allow_repair_update)]
if c["channel"].startswith("Release"):
$ current = _("• {a=https://www.renpy.org/doc/html/changelog.html}View change log{/a}")
@@ -132,14 +132,15 @@ screen update_channel(channels):
hbox:
spacing 7
textbutton c["channel"] action action
textbutton c["channel"] action action
add HALF_SPACER
$ date = _strftime(__("%B %d, %Y"), time.localtime(c["timestamp"]))
$ pretty = c["pretty_version"]
text "[date] • [pretty] [current!t]" style "l_small_text"
text "[date] • [c[pretty_version]] [current!t]" style "l_small_text"
add HALF_SPACER
+5 -5
View File
@@ -228,11 +228,11 @@ init python:
return f_rule
return False
def generate_web_icons(p, destination):
def generate_pwa_icons(p, destination):
"""
Checks if the web-icon.png file exists in the game folder and generates
Checks if the pwa_icon.png file exists in the game folder and generates
required icons for PWA in subdirectory icons/ in the destination folder.
If no web-icon.png is found, the default Ren'Py icon is used instead in
If no pwa_icon.png is found, the default Ren'Py icon is used instead in
the web folder, if exists.
"""
# Check if there's a custom icon in the game directory
@@ -430,7 +430,7 @@ init python:
# Copy the files from WEB_PATH to destination.
for fn in os.listdir(WEB_PATH):
if fn in { "game.zip", "hash.txt", "index.html", "web-icon.png" }:
if fn in { "game.zip", "hash.txt", "index.html", "pwa_icon.png" }:
continue
shutil.copy(os.path.join(WEB_PATH, fn), os.path.join(destination, fn))
@@ -466,7 +466,7 @@ init python:
f.write(html)
if not PY2:
generate_web_icons(p, destination)
generate_pwa_icons(p, destination)
prepare_pwa_files(p, destination)
# Zip up the game.
-1
View File
@@ -41,4 +41,3 @@ renpy.gl2.live2dmodel gen/renpy.gl2.live2dmodel.c
renpy.text.textsupport gen/renpy.text.textsupport.c
renpy.text.texwrap gen/renpy.text.texwrap.c
renpy.text.ftfont gen/renpy.text.ftfont.c ftsupport.c ttgsubtable.c
renpy.text.hbfont gen/renpy.text.hbfont.c
File diff suppressed because it is too large Load Diff
-214
View File
@@ -1,214 +0,0 @@
#!/usr/bin/env python3
import collections
import graphlib
from pathlib import Path
import pprint
class trie_node(dict):
name = None
def __missing__(self, key):
self[key] = trie_node()
self[key][''] = 0
return self[key]
@property
def key(self):
return tuple(sorted(self.items()))
def __hash__(self):
return hash(self.key)
def __eq__(self, other):
return self.key == other.key
emoji = trie_node()
LEVEL = {
"unqualified" : 1,
"component" : 2,
"minimally-qualified" : 3,
"fully-qualified" : 4,
}
def load():
"""
Load the emoji.
"""
emoji_test_text = Path(__file__).parent / "emoji-test.txt"
lines = emoji_test_text.read_text().splitlines()
for l in lines:
l = l.partition("#")[0].strip()
if not l:
continue
codepoints, qualifier = l.split(";")
level = LEVEL[qualifier.strip()]
d = emoji
for i in codepoints.strip().split():
i = chr(int(i, 16))
d = d[i]
d[''] = level
emoji[''] = 0
def merge(d):
"""
Merge tries with identical subtrees.
"""
unique_tries = { }
def pass1(d):
unique_tries[d] = d
for v in d.values():
if type(v) is not int:
pass1(v)
def pass2(d):
d = unique_tries[d]
for k, v in d.items():
if type(v) is not int:
d[k] = pass2(v)
return d
pass1(d)
return pass2(d)
dict_by_name = { }
def assign_names(d):
"""
Assigns a unique name to each trie node.
"""
serial = 0
def assign(d):
if type(d) is int or type(d) is str:
return
if d.name is not None:
return
nonlocal serial
d.name = f"e{serial}"
dict_by_name[d.name] = d
serial += 1
for v in d.values():
assign(v)
assign(d)
d.name = "emoji"
dict_by_name[d.name] = d
order = [ ]
def sort():
"""
Sorts the dicts into dependency order.
"""
graph = { }
def add(d):
graph[d.name] = {v.name for v in d.values() if type(v) is not int}
for v in d.values():
if type(v) is not int:
add(v)
add(emoji)
ts = graphlib.TopologicalSorter(graph)
global order
order = list(ts.static_order())
def generate(f, d):
"""
Generates Python code for a trie node.
"""
l = [ f"{d.name} = {{" ]
for k in sorted(d.keys()):
v = d[k]
if type(v) is int:
l.append(f"'{k}': {v},")
else:
l.append(f"'{k}': {v.name},")
l.append("}")
print(" ".join(l), file=f)
def main():
dest = Path(__file__).parent.parent.parent / "renpy"/ "text"/ "emoji_trie.py"
with open(dest, "w") as f:
print("""\
# coding: utf-8
#
# This file is generated by module/emoji/make_emoji_trie.py. Do not
# edit it directly.
#
# Emoji trie data.
#
#
# It contains a data structure that can be used to determine if a
# sequence of codepoints is an emoji and if it should be rendered
# in an emoji font.
#
# Each trie node is a dictionary mapping codepoints to other dictionaries.
# If a codepoint is not present in the dictionarly, look up the '' key
# in the dictionary to find what it maps to. (It will be one of the constants
# below.
#
# The top-level dictionary is named emoji.
# The codepoint is not an emoji.
NOT_EMOJI = 0
# The codepoints are an emoji, but it may or may not be rendered in an emoji font.
UNQUALIFIED = 1
# The codepoints are part of a larger emoji sequence, and should be rendered
# in an emoji font when standing alone.
COMPONENT = 2
# The codepoints are an emoji, and should be rendered in an emoji font, but
# aren't fully qualified.
MINIMALLY_QUALIFIED = 3
# The codepoints are an emoji, and should be rendered in an emoji font.
QUALIFIED = 4
""", file=f)
global emoji
load()
emoji = merge(emoji)
assign_names(emoji)
sort()
for name in order:
generate(f, dict_by_name[name])
if __name__ == "__main__":
main()
+1 -6
View File
@@ -134,7 +134,7 @@ style_properties = sorted_dict(
alt=None,
altruby_style=None,
antialias=None,
axis=None,
vertical=None,
background='renpy.easy.displayable_or_none',
bar_invert=None,
bar_resizing=None,
@@ -155,7 +155,6 @@ style_properties = sorted_dict(
debug=None,
drop_shadow=None,
drop_shadow_color='renpy.easy.color',
emoji_font=None,
first_indent=None,
first_spacing=None,
fit_first=None,
@@ -169,7 +168,6 @@ style_properties = sorted_dict(
hover_sound=None,
hyperlink_functions=None,
italic=None,
instance=None,
justify=None,
kerning=None,
key_events=None,
@@ -189,12 +187,10 @@ style_properties = sorted_dict(
order_reverse=None,
outlines='expand_outlines',
outline_scaling=None,
prefer_emoji=None,
rest_indent=None,
right_margin=None,
right_padding=None,
ruby_style=None,
shaper=None,
size=None,
size_group=None,
slow_abortable=None,
@@ -213,7 +209,6 @@ style_properties = sorted_dict(
top_margin=None,
top_padding=None,
underline=None,
vertical=None,
xanchor='expand_anchor',
xfill=None,
xfit=None,
+1 -48
View File
@@ -88,7 +88,6 @@ cdef extern from "pyfreetype.h":
FT_PIXEL_MODE_GRAY4,
FT_PIXEL_MODE_LCD,
FT_PIXEL_MODE_LCD_V,
FT_PIXEL_MODE_BGRA,
FT_PIXEL_MODE_MAX
@@ -395,13 +394,6 @@ cdef extern from "pyfreetype.h":
FT_LOAD_LINEAR_DESIGN
FT_LOAD_SBITS_ONLY
FT_LOAD_NO_AUTOHINT
FT_LOAD_COLOR
FT_LOAD_TARGET_NORMAL
FT_LOAD_TARGET_LIGHT
FT_LOAD_TARGET_MONO
FT_LOAD_TARGET_LCD
FT_LOAD_TARGET_LCD_V
FT_Error FT_Load_Glyph(FT_Face face, FT_UInt glyph_index, FT_Int32 flags)
FT_Error FT_Load_Char(FT_Face face, FT_ULong char_code, FT_Int32 flags)
@@ -523,7 +515,7 @@ cdef extern from "pyfreetype.h":
FT_Error FT_Bitmap_Embolden(FT_Library lib, FT_Bitmap *bitmap, FT_Pos xStrength, FT_Pos yStrength)
FT_Error FT_Bitmap_Convert(FT_Library lib, FT_Bitmap *source, FT_Bitmap *target, FT_Int alignment)
# Additions by Tom Rothamel.
# Additions by Tom Rothame.
cdef struct FT_MemoryRec_
ctypedef FT_MemoryRec_ *FT_Memory
@@ -613,42 +605,3 @@ cdef extern from "pyfreetype.h":
cdef FT_Long FT_CEIL(FT_Long)
cdef FT_Long FT_FLOOR(FT_Long)
cdef FT_Long FT_ROUND(FT_Long)
# Multiple Masters
cdef struct FT_Var_Axis_:
FT_String* name
FT_Fixed minimum
FT_Fixed default "def"
FT_Fixed maximum
FT_ULong tag
FT_UInt strid
ctypedef FT_Var_Axis_ FT_Var_Axis
cdef struct FT_Var_Named_Style_:
FT_Fixed* coords
FT_UInt strid
FT_UInt psid
ctypedef FT_Var_Named_Style_ FT_Var_Named_Style
cdef struct FT_MM_Var_:
FT_UInt num_axis
FT_UInt num_designs
FT_UInt num_namedstyles
FT_Var_Axis* axis
FT_Var_Named_Style* namedstyle
ctypedef FT_MM_Var_ FT_MM_Var
FT_Error FT_Get_MM_Var(FT_Face face, FT_MM_Var **amaster)
FT_Error FT_Done_MM_Var(FT_Library library, FT_MM_Var *amaster)
FT_Error FT_Set_Named_Instance(FT_Face, FT_UInt instance_index)
FT_Error FT_Set_Var_Design_Coordinates( FT_Face face,
FT_UInt num_coords,
FT_Fixed* coords )
-1
View File
@@ -7,7 +7,6 @@
#include FT_OUTLINE_H
#include FT_BITMAP_H
#include FT_STROKER_H
#include FT_MULTIPLE_MASTERS_H
#define FT_FLOOR(X) ((X & -64) >> 6)
#define FT_CEIL(X) (((X + 63) & -64) >> 6)
-8
View File
@@ -86,7 +86,6 @@ include("libavcodec/avcodec.h", directory="ffmpeg", optional=True) or include("l
include("libswscale/swscale.h", directory="ffmpeg", optional=True) or include("libswscale/swscale.h") # type: ignore
include("GL/glew.h")
include("pygame_sdl2/pygame_sdl2.h", directory="python{}.{}".format(sys.version_info.major, sys.version_info.minor))
include("hb.h", directory="harfbuzz")
library("SDL2")
library("png")
@@ -198,13 +197,6 @@ cython(
[ "ftsupport.c", "ttgsubtable.c" ],
libs=sdl + [ 'freetype', 'z', 'm' ])
if not (PY2 and emscripten):
cython(
"renpy.text.hbfont",
[ "ftsupport.c" ],
libs=sdl + [ 'harfbuzz', 'freetype', 'z', 'm' ])
generate_all_cython()
find_unnecessary_gen()
+1 -1
View File
@@ -207,7 +207,7 @@ def path_to_renpy_base():
Returns the absolute path to the Ren'Py base directory.
"""
renpy_base = os.path.dirname(os.path.abspath(__file__))
renpy_base = os.path.dirname(os.path.realpath(sys.argv[0]))
renpy_base = os.path.abspath(renpy_base)
return renpy_base
+7 -3
View File
@@ -278,6 +278,9 @@ class Backup(_object):
# A map from module to the set of names in that module.
self.names = { }
if mobile:
return
for m in sys.modules.values():
if m is None:
continue
@@ -568,7 +571,8 @@ def import_all():
global backup
backup = Backup()
if not mobile:
backup = Backup()
post_import()
@@ -614,8 +618,8 @@ def reload_all():
returned.
"""
# if mobile:
# raise Exception("Reloading is not supported on mobile platforms.")
if mobile:
raise Exception("Reloading is not supported on mobile platforms.")
import renpy
+131 -31
View File
@@ -242,11 +242,24 @@ def compile_all():
for i in compile_queue:
i.atl.find_loaded_variables()
if i.atl.constant == GLOBAL_CONST:
i.compile()
compile_queue = [ ]
def find_loaded_variables(expr):
"""
Returns the set of variables that are loaded by the given expression.
"""
if expr is None:
return set()
ast = renpy.pyanalysis.ccache.ast_eval(expr)
return renpy.python.find_loaded_variables(ast)
# Used to indicate that a variable is not in the context.
NotInContext = renpy.object.Sentinel("NotInContext")
@@ -272,6 +285,28 @@ class Context(object):
def __ne__(self, other):
return not (self == other)
def variables_equal(self, other, variables):
"""
Returns true if the variables in `variables` are equal in
this context and `other`. False if they are not equal.
Returns True if any variable cannot be compared.
"""
try:
if renpy.config.at_transform_compare_full_context:
if self.context != other.context:
return False
for i in variables:
if self.context.get(i, NotInContext) != other.context.get(i, NotInContext):
return False
return True
except Exception:
return True
# This is intended to be subclassed by ATLTransform. It takes care of
@@ -412,12 +447,7 @@ class ATLTransformBase(renpy.object.Object):
# a way that would affect the execution of the ATL.
if t.atl.constant != GLOBAL_CONST:
block = self.get_block()
if block is None:
block = self.compile()
if not deep_compare(self.block, t.block):
if not self.context.variables_equal(t.context, t.atl.find_loaded_variables()):
return
self.done = t.done
@@ -567,6 +597,9 @@ class ATLTransformBase(renpy.object.Object):
def execute(self, trans, st, at):
if self.state.debug:
print("A", st)
if self.done:
return None
@@ -671,6 +704,12 @@ class RawStatement(object):
self.constant = NOT_CONST
def find_loaded_variables(self):
"""
Returns the set of variables that are loaded by this statement.
"""
raise Exception("find_loaded_variables not implemented.")
# The base class for compiled ATL Statements.
@@ -729,6 +768,10 @@ class RawBlock(RawStatement):
# and use this value for all ATLTransform that use us as an atl.
compiled_block = None
# If this is the outermost ATL of a parse, a set giving the variables
# that are loaded by this block.
loaded_variable_cache = None
def __init__(self, loc, statements, animation):
@@ -791,6 +834,20 @@ class RawBlock(RawStatement):
self.constant = constant
def find_loaded_variables(self):
if self.loaded_variable_cache is not None:
return self.loaded_variable_cache
variables = set()
for i in self.statements:
variables.update(i.find_loaded_variables())
self.loaded_variable_cache = variables
return variables
# A compiled ATL block.
class Block(Statement):
@@ -1129,6 +1186,26 @@ class RawMultipurpose(RawStatement):
self.constant = constant
def find_loaded_variables(self):
rv = set()
rv.update(find_loaded_variables(self.warp_function))
rv.update(find_loaded_variables(self.duration))
rv.update(find_loaded_variables(self.circles))
for _name, expr in self.properties:
rv.update(find_loaded_variables(expr))
for _name, exprs in self.splines:
for expr in exprs:
rv.update(find_loaded_variables(expr))
for expr, withexpr in self.expressions:
rv.update(find_loaded_variables(expr))
rv.update(find_loaded_variables(withexpr))
return rv
def predict(self, ctx):
for i, _j in self.expressions:
@@ -1164,6 +1241,9 @@ class RawContainsExpr(RawStatement):
def mark_constant(self, analysis):
self.constant = analysis.is_constant_expr(self.expression)
def find_loaded_variables(self):
return find_loaded_variables(self.expression)
# This allows us to have multiple ATL transforms as children.
class RawChild(RawStatement):
@@ -1198,6 +1278,14 @@ class RawChild(RawStatement):
self.constant = constant
def find_loaded_variables(self):
rv = set()
for i in self.children:
rv.update(i.find_loaded_variables())
return rv
# This changes the child of this statement, optionally with a transition.
class Child(Statement):
@@ -1507,6 +1595,9 @@ class RawRepeat(RawStatement):
def mark_constant(self, analysis):
self.constant = analysis.is_constant_expr(self.repeats)
def find_loaded_variables(self):
return find_loaded_variables(self.repeats)
class Repeat(Statement):
def __init__(self, loc, repeats):
@@ -1544,6 +1635,13 @@ class RawParallel(RawStatement):
self.constant = constant
def find_loaded_variables(self):
rv = set()
for i in self.blocks:
rv.update(i.find_loaded_variables())
return rv
class Parallel(Statement):
@@ -1625,6 +1723,15 @@ class RawChoice(RawStatement):
self.constant = constant
def find_loaded_variables(self):
rv = set()
for chance, block in self.choices:
rv.update(find_loaded_variables(chance))
rv.update(block.find_loaded_variables())
return rv
class Choice(Statement):
@@ -1694,6 +1801,9 @@ class RawTime(RawStatement):
def mark_constant(self, analysis):
self.constant = analysis.is_constant_expr(self.time)
def find_loaded_variables(self):
return find_loaded_variables(self.time)
class Time(Statement):
@@ -1741,6 +1851,15 @@ class RawOn(RawStatement):
self.constant = constant
def find_loaded_variables(self):
rv = set()
for block in self.handlers.values():
rv.update(block.find_loaded_variables())
return rv
class On(Statement):
def __init__(self, loc, handlers):
@@ -1849,6 +1968,9 @@ class RawEvent(RawStatement):
def mark_constant(self, analysis):
self.constant = GLOBAL_CONST
def find_loaded_variables(self):
return set()
class Event(Statement):
@@ -1875,6 +1997,9 @@ class RawFunction(RawStatement):
def mark_constant(self, analysis):
self.constant = analysis.is_constant_expr(self.expr)
def find_loaded_variables(self):
return find_loaded_variables(self.expr)
class Function(Statement):
@@ -2186,28 +2311,3 @@ def parse_atl(l):
old = new
return RawBlock(block_loc, merged, animation)
def deep_compare(a, b):
"""
Compares two trees of ATL statements for equality.
"""
if type(a) != type(b):
return False
if isinstance(a, (list, tuple)):
return all(deep_compare(i, j) for i, j in zip(a, b))
if isinstance(a, dict):
if len(a) != len(b):
return False
return all((k in b) and deep_compare(a[k], b[k]) for k in a)
if isinstance(a, Statement):
return deep_compare(a.__dict__, b.__dict__)
try:
return a == b
except Exception:
return True
+14 -16
View File
@@ -668,7 +668,7 @@ class Channel(object):
renpysound.dequeue(self.number, True)
renpysound.stop(self.number)
def enqueue(self, filenames, loop=True, synchro_start=False, fadein=0, tight=None, loop_only=False, relative_volume=1.0):
def enqueue(self, filenames, loop=True, synchro_start=False, fadein=0, tight=None, relative_volume=1.0):
with lock:
@@ -676,22 +676,20 @@ class Channel(object):
filename, _, _ = self.split_filename(filename, False)
renpy.game.persistent._seen_audio[str(filename)] = True # type: ignore
if not loop_only:
if tight is None:
tight = self.tight
if tight is None:
tight = self.tight
self.keep_queue += 1
for filename in filenames:
qe = QueueEntry(filename, fadein, tight, False, relative_volume)
self.queue.append(qe)
# Only fade the first thing in.
fadein = 0
self.wait_stop = synchro_start
self.synchro_start = synchro_start
self.keep_queue += 1
for filename in filenames:
qe = QueueEntry(filename, fadein, tight, False, relative_volume)
self.queue.append(qe)
# Only fade the first thing in.
fadein = 0
self.wait_stop = synchro_start
self.synchro_start = synchro_start
if loop:
self.loop = list(filenames)
+1 -3
View File
@@ -116,13 +116,11 @@ def play(filenames, channel="music", loop=None, fadeout=None, synchro_start=Fals
if if_changed and c.get_playing() in filenames:
fadein = 0
loop_only = loop_is_filenames
if not loop_is_filenames:
c.dequeue()
else:
c.dequeue()
c.fadeout(fadeout)
loop_only = False
if renpy.config.skip_sounds and renpy.config.skipping and (not loop):
enqueue = False
@@ -130,7 +128,7 @@ def play(filenames, channel="music", loop=None, fadeout=None, synchro_start=Fals
enqueue = True
if enqueue:
c.enqueue(filenames, loop=loop, synchro_start=synchro_start, fadein=fadein, tight=tight, loop_only=loop_only, relative_volume=relative_volume)
c.enqueue(filenames, loop=loop, synchro_start=synchro_start, fadein=fadein, tight=tight, relative_volume=relative_volume)
t = get_serial()
ctx.last_changed = t
+1 -79
View File
@@ -121,82 +121,11 @@ def mac_start(fn):
def popen_del(self, *args, **kwargs):
"""
Fix an issue where the __del__ method of popen doesn't work.
Fix an issue where the __del__ method of popen doesn't wor,
"""
return
def get_alternate_base(basedir, always=False):
"""
:undocumented:
Tries to find an alternate base directory. This exists in a writable
location, and is intended for use by a game that downloads its assets
to the device (generally for ios or android, where the assets may be
too big for the app store).
"""
# Determine the alternate base directory location.
if renpy.android:
altbase = os.path.join(os.environ["ANDROID_PRIVATE"], "base")
elif renpy.ios:
from pyobjus import autoclass # type: ignore
from pyobjus.objc_py_types import enum # type: ignore
NSSearchPathDirectory = enum("NSSearchPathDirectory", NSApplicationSupportDirectory=14)
NSSearchPathDomainMask = enum("NSSearchPathDomainMask", NSUserDomainMask=1)
NSFileManager = autoclass('NSFileManager')
manager = NSFileManager.defaultManager()
url = manager.URLsForDirectory_inDomains_(
NSSearchPathDirectory.NSApplicationSupportDirectory,
NSSearchPathDomainMask.NSUserDomainMask,
).lastObject()
# url.path seems to change type based on iOS version, for some reason.
try:
altbase = url.path().UTF8String()
except Exception:
altbase = url.path.UTF8String()
if isinstance(altbase, bytes):
altbase = altbase.decode("utf-8")
else:
altbase = os.path.join(basedir, "base")
if always:
return altbase
# Check to see if there's a game in there created with the
# current version of Ren'Py.
def ver(s):
"""
Returns the first three components of a version string.
"""
return tuple(int(i) for i in s.split(".")[:3])
import json
version_json = os.path.join(altbase, "update", "version.json")
if not os.path.exists(version_json):
return basedir
with open(version_json, "r") as f:
modules = json.load(f)
for v in modules.values():
if ver(v["renpy_version"]) != ver(renpy.version_only):
return basedir
return altbase
def bootstrap(renpy_base):
global renpy
@@ -260,13 +189,6 @@ def bootstrap(renpy_base):
sys.stderr.write("Base directory %r does not exist. Giving up.\n" % (basedir,))
sys.exit(1)
# Potentially use an alternate base directory.
try:
basedir = get_alternate_base(basedir)
except Exception:
import traceback
traceback.print_exc()
# Make game/ on Android.
if renpy.android:
if not os.path.exists(basedir + "/game"):
-3
View File
@@ -492,9 +492,6 @@ def display_say(
# Clears out transients.
renpy.exports.with_statement(None)
renpy.exports.checkpoint(True, hard=checkpoint)
return
# If we're not interacting, call the noniteractive_callbacks.
+1 -1
View File
@@ -196,7 +196,7 @@ init -1500 python:
:func:`renpy.show_screen` function.
"""
return Show(screen, transition, *args, _transient=True, **kwargs)
return Show(screen, transition, _transient=True, *args, **kwargs)
@renpy.pure
class Hide(Action, DictEquality):
+305 -253
View File
@@ -21,7 +21,10 @@
init -1600 python:
__Sentinel = object()
##########################################################################
# Functions that set variables or fields.
__FieldNotFound = object()
def _get_field(obj, name, kind):
@@ -31,100 +34,333 @@ init -1600 python:
rv = obj
for i in name.split("."):
rv = getattr(rv, i, __Sentinel)
if rv is __Sentinel:
raise Exception("The {!r} {} does not exist.".format(name, kind))
rv = getattr(rv, i, __FieldNotFound)
if rv is __FieldNotFound:
raise NameError("The {} {} does not exist.".format(kind, name))
return rv
def _set_field(obj, name, value, kind):
fields, _, attr = name.rpartition(".")
obj = _get_field(obj, fields, kind)
try:
obj = _get_field(obj, fields, kind)
setattr(obj, attr, value)
except Exception:
raise Exception("The {!r} {} cannot be set.".format(name, kind))
raise NameError("The {} {} does not exist.".format(kind, name))
init -1600 python hide:
Accessor = Manager = python_object
# clarify the role of each class without adding to the mro
# and without having ancestors placed between RevertableObject and python_object in the mro
# Accessor mixins : manage how the data is accessed
# defines a __call__ which gets the value to set from the value_to_set() method
# defines a current_value() method which returns the current value of the accessed data or raise an exception
# may define the `kind` class attribute, which is used in error messages
class Field(Accessor):
@renpy.pure
class SetField(Action, FieldEquality):
"""
An Accessor mixin class for Actions setting a field on an object.
:doc: data_action
:args: (object, field, value)
Causes the a field on an object to be set to a given value.
`object` is the object, `field` is a string giving the name of the
field to set, and `value` is the value to set it to.
"""
identity_fields = ("object",)
equality_fields = ("field",)
identity_fields = [ "object" ]
equality_fields = [ "field", "value" ]
kind = "field"
def __init__(self, object, field, *args, **kwargs):
super(Field, self).__init__(*args, **kwargs)
def __init__(self, object, field, value, kind="field"):
self.object = object
self.field = field
self.value = value
self.kind = kind
def __call__(self):
_set_field(self.object, self.field, self.value_to_set(), self.kind)
_set_field(self.object, self.field, self.value, self.kind)
renpy.restart_interaction()
def current_value(self):
return _get_field(self.object, self.field, self.kind)
def get_selected(self):
return _get_field(self.object, self.field, self.kind) == self.value
class Variable(Field):
@renpy.pure
def SetVariable(name, value):
"""
An Accessor mixin class for Actions setting a global variable.
:doc: data_action
Causes the variable called `name` to be set to `value`.
The `name` argument must be a string, and can be a simple name like "strength", or
one with dots separating the variable from fields, like "hero.strength"
or "persistent.show_cutscenes".
"""
kind = "global variable"
return SetField(store, name, value, kind="variable")
def __init__(self, name, *args, **kwargs):
super(Variable, self).__init__(store, name, *args, **kwargs)
class Dict(Accessor):
@renpy.pure
class SetDict(Action, FieldEquality):
"""
An Accessor mixin class for Actions setting an index on a sequence or a key on a mapping.
:doc: data_action
Causes the value of `key` in `dict` to be set to `value`.
This also works with lists, where `key` is the index at which
the value will be set.
"""
identity_fields = ("dict",)
equality_fields = ("key",)
kind = "key or index"
identity_fields = [ "dict" ]
equality_fields = [ "key", "value" ]
def __init__(self, dict, key, *args, **kwargs):
super(Dict, self).__init__(*args, **kwargs)
def __init__(self, dict, key, value):
self.dict = dict
self.key = key
self.value = value
def __call__(self):
self.dict[self.key] = self.value_to_set()
self.dict[self.key] = self.value
renpy.restart_interaction()
def current_value(self):
key = self.key
try:
return self.dict[self.key]
except LookupError as e:
raise Exception("The {!r} {} does not exist.".format(key, self.kind)) # from e # PY3 only
def get_selected(self):
if self.key not in self.dict:
return False
class ScreenVariable(Accessor):
return self.dict[self.key] == self.value
@renpy.pure
class SetScreenVariable(Action, FieldEquality):
"""
An Accessor mixin class for Actions setting a screen variable.
:doc: data_action
Causes the variable called `name` associated with the current screen
to be set to `value`.
In a ``use``\ d screen, this action sets the variable in the context
of the screen containing the ``use``\ d one(s).
To set variables within a ``use``\ d screen, and only in that
case, use :func:`SetLocalVariable` instead.
"""
identity_fields = ()
equality_fields = ("name",)
kind = "screen variable"
identity_fields = [ "value" ]
equality_fields = [ "name" ]
def __init__(self, name, *args, **kwargs):
super(ScreenVariable, self).__init__(*args, **kwargs)
def __init__(self, name, value):
self.name = name
self.value = value
def __call__(self):
cs = renpy.current_screen()
if cs is None:
return
cs.scope[self.name] = self.value
renpy.restart_interaction()
def get_selected(self):
cs = renpy.current_screen()
if cs is None:
return False
if self.name not in cs.scope:
return False
return cs.scope[self.name] == self.value
# Not pure.
def SetLocalVariable(name, value):
"""
:doc: data_action
Causes the variable called `name` to be set to `value` in the current
local context.
This function is only useful in a screen that has been ``use``\ d by
another screen, as it provides a way of setting the value of a
variable inside the used screen. In all other cases,
:func:`SetScreenVariable` should be preferred, as it allows more
of the screen to be cached.
For more information, see :ref:`sl-use`.
This must be created in the context that the variable is set
in - it can't be passed in from somewhere else.
"""
return SetDict(sys._getframe(1).f_locals, name, value)
@renpy.pure
class ToggleField(Action, FieldEquality):
"""
:doc: data_action
:args: (object, field, true_value=None, false_value=None)
Toggles `field` on `object`. Toggling means to invert the boolean
value of that field when the action is performed.
`true_value`
If not None, then this is the true value we use.
`false_value`
If not None, then this is the false value we use.
"""
identity_fields = [ "object"]
equality_fields = [ "field", "true_value", "false_value" ]
kind = "field"
def __init__(self, object, field, true_value=None, false_value=None, kind="field"):
self.object = object
self.field = field
self.true_value = true_value
self.false_value = false_value
self.kind = kind
def __call__(self):
value = _get_field(self.object, self.field, self.kind)
if self.true_value is not None:
value = (value == self.true_value)
value = not value
if self.true_value is not None:
if value:
value = self.true_value
else:
value = self.false_value
_set_field(self.object, self.field, value, self.kind)
renpy.restart_interaction()
def get_selected(self):
rv = _get_field(self.object, self.field, self.kind)
if self.true_value is not None:
rv = (rv == self.true_value)
return rv
@renpy.pure
def ToggleVariable(variable, true_value=None, false_value=None):
"""
:doc: data_action
Toggles the variable whose name is given in `variable`.
The `variable` argument must be a string, and can be a simple name like "strength", or
one with dots separating the variable from fields, like "hero.strength"
or "persistent.show_cutscenes".
`true_value`
If not None, then this is the true value used.
`false_value`
If not None, then this is the false value used.
"""
return ToggleField(store, variable, true_value=true_value, false_value=false_value, kind="variable")
@renpy.pure
class ToggleDict(Action, FieldEquality):
"""
:doc: data_action
Toggles the value of `key` in `dict`. It also works on
lists, in which case `key` is the index of the value to toggle.
Toggling means to invert the value when the action is performed.
`true_value`
If not None, then this is the true value used.
`false_value`
If not None, then this is the false value used.
"""
identity_fields = [ "dict", ]
equality_fields = [ "key", "true_value", "false_value" ]
def __init__(self, dict, key, true_value=None, false_value=None):
self.dict = dict
self.key = key
self.true_value = true_value
self.false_value = false_value
def __call__(self):
value = self.dict[self.key]
if self.true_value is not None:
value = (value == self.true_value)
value = not value
if self.true_value is not None:
if value:
value = self.true_value
else:
value = self.false_value
self.dict[self.key] = value
renpy.restart_interaction()
def get_selected(self):
try:
rv = self.dict[self.key]
except (KeyError, IndexError):
return False
if self.true_value is not None:
rv = (rv == self.true_value)
return rv
# Not pure.
def ToggleLocalVariable(name, true_value=None, false_value=None):
"""
:doc: data_action
Toggles the value of the variable called `name` in the current local context.
This function is only useful in a screen that has been ``use``\ d by
another screen, as it provides a way of setting the value of a
variable inside the used screen. In all other cases,
:func:`ToggleScreenVariable` should be preferred, as it allows more
of the screen to be cached.
For more information, see :ref:`sl-use`.
This must be created in the context that the variable is set
in - it can't be passed in from somewhere else.
`true_value`
If not None, then this is the true value used.
`false_value`
If not None, then this is the false value used.
"""
return ToggleDict(sys._getframe(1).f_locals, name, true_value=true_value, false_value=false_value)
@renpy.pure
class ToggleScreenVariable(Action, FieldEquality):
"""
:doc: data_action
Toggles the value of the variable called `name` in the current screen.
In a ``use``\ d screen, this action accesses and sets the given variable
in the context of the screen containing the ``use``\ d one(s).
To access and set variables within a ``use``\ d screen, and only in that
case, use :func:`ToggleLocalVariable` instead.
`true_value`
If not None, then this is the true value used.
`false_value`
If not None, then this is the false value used.
"""
equality_fields = [ "name", "true_value", "false_value" ]
def __init__(self, name, true_value=None, false_value=None):
self.name = name
self.true_value = true_value
self.false_value = false_value
def __call__(self):
cs = renpy.current_screen()
@@ -132,222 +368,38 @@ init -1600 python hide:
if cs is None:
return
cs.scope[self.name] = self.value_to_set()
renpy.restart_interaction()
value = cs.scope[self.name]
def current_value(self):
cs = renpy.current_screen()
if cs is None:
raise Exception("No current screen.")
rv = cs.scope.get(self.name, __Sentinel)
if rv is __Sentinel:
raise Exception("The {!r} {} does not exist.".format(self.name, self.kind))
return rv
class LocalVariable(Dict):
"""
An Accessor mixin class for Actions setting a local variable.
"""
kind = "local variable"
def __init__(self, name, *args, **kwargs):
super(LocalVariable, self).__init__(sys._getframe(1).f_locals,
name,
*args, **kwargs)
# Manager mixins : manage what value gets written
# defines a value_to_set() method which returns the value to be set
# may define get_selected and such methods
# both of these may call the current_value() method
class Set(Manager):
"""
A Manager mixin class for Actions setting a single value
"""
identity_fields = ()
equality_fields = ("value",)
def __init__(self, value, *args, **kwargs):
super(Set, self).__init__(*args, **kwargs)
self.value = value
def value_to_set(self):
return self.value
def get_selected(self):
try:
val = self.current_value()
except Exception:
return False
return val == self.value
class Toggle(Manager):
"""
A Manager mixin class for Actions toggling between two values.
"""
identity_fields = ()
equality_fields = ("true_value", "false_value")
def __init__(self, true_value=None, false_value=None, *args, **kwargs):
super(Toggle, self).__init__(*args, **kwargs)
self.true_value = true_value
self.false_value = false_value
def value_to_set(self):
value = self.current_value()
tv = self.true_value
if tv is not None:
value = (value == tv)
if self.true_value is not None:
value = (value == self.true_value)
value = not value
if tv is not None:
if self.true_value is not None:
if value:
return tv
value = self.true_value
else:
return self.false_value
value = self.false_value
return value
cs.scope[self.name] = value
renpy.restart_interaction()
def get_selected(self):
try:
val = self.current_value()
except Exception:
cs = renpy.current_screen()
if cs is None:
return False
tv = self.true_value
if tv is not None:
return val == tv
return bool(val)
class Cycle(Manager):
"""
A Manager mixin class for Actions cycling through a list of values.
Warning : the provided values must not be duplicated, otherwise some values can never be set.
"""
identity_fields = ()
equality_fields = ("values", "loop")
def __init__(self, values, *args, **kwargs):
if kwargs.pop("reverse", False):
values = values[::-1]
# the reversed builtin returns a non-indexable iterator
# and we only support sequences anyway - iterators are not picklable
self.loop = kwargs.pop("loop", True)
super(Cycle, self).__init__(*args, **kwargs)
self.values = renpy.easy.to_tuple(values)
def value_to_set(self):
values = self.values
value = self.current_value()
if value in values:
idx = values.index(value) + 1
lnv = len(values)
if self.loop and idx >= lnv:
idx -= lnv
else:
idx = 0
return values[idx]
def get_sensitive(self):
values = self.values
if not values:
if self.name not in cs.scope:
return False
if self.loop:
return True
rv = cs.scope[self.name]
try:
value = self.current_value()
except Exception:
return False
if value in values:
idx = values.index(value)+1
if idx >= len(values):
return False
return True
class Increment(Manager):
"""
A Manager mixin class for Actions incrementing a value by a set amount.
"""
identity_fields = ()
equality_fields = ("amount",)
def __init__(self, amount=1, *args, **kwargs):
super(Increment, self).__init__(*args, **kwargs)
self.amount = amount
def value_to_set(self):
return self.current_value() + self.amount
def get_sensitive(self):
try:
value = self.current_value()
except Exception:
return False
try:
value + self.amount
except Exception:
return False
return True
if self.true_value is not None:
rv = (rv == self.true_value)
for accessor in (Field, Variable, Dict, ScreenVariable, LocalVariable):
for manager in (Set, Toggle, Cycle, Increment):
name = manager.__name__ + accessor.__name__
doc = ":doc: generated_data_action"
if config.generating_documentation:
from inspect import signature, Signature, Parameter
params = []
for bas in (accessor, manager):
sig = signature(bas.__init__)
for k, param in enumerate(sig.parameters.values()):
if k and (param.kind not in (param.VAR_POSITIONAL, param.VAR_KEYWORD)):
params.append(param)
if manager is Cycle:
params.append(Parameter("reverse", Parameter.KEYWORD_ONLY, default=False))
params.append(Parameter("loop", Parameter.KEYWORD_ONLY, default=True))
doc += "\n:args: " + str(Signature(parameters=params)) + "\n\nSee :ref:`data-actions`."
clsdict = python_dict(
identity_fields=manager.identity_fields+accessor.identity_fields,
equality_fields=manager.equality_fields+accessor.equality_fields,
__doc__=doc,
)
cls = type(name, (accessor, manager, Action, FieldEquality), clsdict)
if accessor is not LocalVariable:
renpy.pure(name)
setattr(store, name, cls)
init -1600 python:
return rv
@renpy.pure
class AddToSet(Action, FieldEquality):
-17
View File
@@ -291,23 +291,6 @@ init -1500 python:
Such Json data is added to a save slot by callbacks registered using
:var:`config.save_json_callbacks`.
By default, the following keys are defined:
`_save_name`
The value of :var:`save_name` when the game was saved.
`_renpy_version`
The version of Ren'Py the save was created with.
`_version`
The value of :var:`config.version` when the save was created.
`_game_runtime`
The result of calling :func:`renpy.get_game_runtime`.
`_ctime`
The time the save was created, in seconds since January 1, 1970, UTC.
"""
json = renpy.slot_json(__slotname(name, page, slot))
+4 -6
View File
@@ -36,15 +36,13 @@ init -1500 python:
class ShowMenu(Action, DictEquality):
"""
:doc: menu_action
:args: (screen=_game_menu_screen, *args, _transition=config.intra_transition, **kwargs)
Causes us to enter the game menu, if we're not there already. If we
are in the game menu, then this shows a screen or jumps to a label.
`screen` is usually the name of a screen, which is shown using
the screen mechanism with the ``*args`` and ``**kwargs`` passed to
the screen. If the screen doesn't exist, then "_screen" is appended
to it, and that label is jumped to, ignoring `args` and `kwargs`.
the screen mechanism. If the screen doesn't exist, then "_screen"
is appended to it, and that label is jumped to.
If the optional keyword argument `_transition` is given, the
menu will change screens using the provided transition.
@@ -62,7 +60,7 @@ init -1500 python:
* ShowMenu("stats")
ShowMenu without an argument will enter the game menu at the
default screen, taken from :var:`_game_menu_screen`.
default screen, taken from _game_menu_screen.
Extra arguments and keyword arguments are passed on to the screen
"""
@@ -99,7 +97,7 @@ init -1500 python:
if renpy.has_screen(screen):
renpy.transition(transition)
renpy.show_screen(screen, *self.args, _transient=True, **self.kwargs)
renpy.show_screen(screen, _transient=True, *self.args, **self.kwargs)
renpy.restart_interaction()
elif renpy.has_label(screen):
+1 -53
View File
@@ -73,7 +73,6 @@ init -1500 python:
class SelectedIf(Action, DictEquality):
"""
:doc: other_action
:args: (action, /)
This indicates that one action in a list of actions should be used
to determine if a button is selected. This only makes sense
@@ -107,7 +106,6 @@ init -1500 python:
class SensitiveIf(Action, DictEquality):
"""
:doc: other_action
:args: (action, /)
This indicates that one action in a list of actions should be used
to determine if a button is sensitive. This only makes sense
@@ -609,7 +607,7 @@ init -1500 python:
The sensitivity and selectedness of this action match those
of the `yes` action.
See :func:`renpy.confirm` for a function version of this action.
See :func:`layout.yesno_screen` for a function version of this action.
"""
@@ -876,56 +874,6 @@ init -1500 python:
return current.screen_name[0]
@renpy.pure
class CopyToClipboard(Action):
"""
:doc: other_action
Copies the string `s` to the system clipboard, if possible. This
should work on desktop and mobile platforms, but will not work
on the web.
"""
def __init__(self, s):
self.s = s
def __call__(self):
import pygame.scrap
pygame.scrap.put(pygame.SCRAP_TEXT, self.s.encode("utf-8"))
@renpy.pure
class EditFile(Action):
"""
:doc: other_action
Requests Ren'Py to open the given file in a text editor, if possible.
This will work on some platforms but not others.
`filename`
If given, the filename to open. If None, the current filename
and line number are used, with `line` being ignored.
`line`
The line number to open the file at.
"""
def __init__(self, filename=None, line=1):
self.filename = filename
self.line = line
def __call__(self):
filename = self.filename
line = self.line
if filename is None:
filename, line = renpy.get_filename_line()
try:
renpy.launch_editor([ filename ], line)
except Exception:
pass
init -1500:
transform _notify_transform:
+244 -180
View File
@@ -112,26 +112,59 @@ init -1500 python:
self.old_value = other.value
self.start_time = None
class __GenericValue(BarValue, FieldEquality):
# pickle defaults
@renpy.pure
class DictValue(BarValue, FieldEquality):
"""
:doc: value
A value that allows the user to adjust the value of a key
in a dict.
`dict`
The dict.
`key`
The key.
`range`
The range to adjust over.
`max_is_zero`
If True, then when the value of a key is zero, the value of the
bar will be range, and all other values will be shifted down by 1.
This works both ways - when the bar is set to the maximum, the
value of a key is set to 0.
`style`
The styles of the bar created.
`offset`
An offset to add to the value.
`step`
The amount to change the bar by. If None, defaults to 1/10th of
the bar.
`action`
If not None, an action to call when the field has changed.
"""
offset = 0
action = None
force_step = False
identity_fields = ()
equality_fields = ('range', 'max_is_zero', 'style', 'offset', 'step', 'action', 'force_step')
identity_fields = [ 'dict' ]
equality_fields = [ 'key', 'range', 'max_is_zero', 'style', 'offset', 'step', 'action', 'force_step' ]
def __init__(self, range, max_is_zero=False, style="bar", offset=0, step=None, action=None, force_step=False):
def __init__(self, dict, key, range, max_is_zero=False, style="bar", offset=0, step=None, action=None, force_step=False):
self.dict = dict
self.key = key
self.range = range
self.max_is_zero = max_is_zero
self.style = style
self.offset = offset
self.force_step = force_step
if step is None:
if isinstance(range, float):
step = range / 10.0
else:
step = max(range // 10, 1)
step = max(range / 10, 1)
self.step = step
self.action = action
@@ -145,13 +178,14 @@ init -1500 python:
value += self.offset
self.set_value(value)
self.dict[self.key] = value
renpy.restart_interaction()
return renpy.run(self.action)
def get_adjustment(self):
value = self.get_value()
value = self.dict[self.key]
value -= self.offset
@@ -173,46 +207,10 @@ init -1500 python:
return self.style, "v" + self.style
@renpy.pure
class DictValue(__GenericValue):
class FieldValue(BarValue, FieldEquality):
"""
:doc: value
:args: {args}
A bar value that allows the user to adjust the value of a key in
a dict, or of an element at a particular index in a list.
`dict`
The dict, or the list.
`key`
The key, or the index when using a list.
"""
kind = "key or index"
identity_fields = ('dict',)
equality_fields = __GenericValue.equality_fields + ('key',)
def __init__(self, dict, key, *args, **kwargs):
self.dict = dict
self.key = key
super(DictValue, self).__init__(*args, **kwargs)
def get_value(self):
value = self.dict[self.key]
return value
def set_value(self, value):
try:
self.dict[self.key] = value
except LookupError as e:
raise Exception("The {!r} {} does not exist".format(self.key, self.kind)) # from e # PY3 only
@renpy.pure
class FieldValue(__GenericValue):
"""
:doc: value
:args: {args}
:args: (self, object, field, range, max_is_zero=False, style="bar", offset=0, step=None, action=None, force_step=False)
A bar value that allows the user to adjust the value of a field
on an object.
@@ -220,157 +218,223 @@ init -1500 python:
`object`
The object.
`field`
The field name, a string.
"""
kind = "field"
identity_fields = ('object',)
equality_fields = __GenericValue.equality_fields + ("field",)
def __init__(self, object, field, *args, **kwargs):
self.object = object
self.field = field
super(FieldValue, self).__init__(*args, **kwargs)
def get_value(self):
value = _get_field(self.object, self.field, self.kind)
return value
def set_value(self, value):
_set_field(self.object, self.field, value, self.kind)
@renpy.pure
class VariableValue(FieldValue):
"""
:doc: value
:args: {args}
A bar value that allows the user to adjust the value of a variable in
the default store.
`variable`
The `variable` parameter must be a string, and can be a simple
name like "strength", or one with dots separating the variable
from fields, like "hero.strength" or "persistent.show_cutscenes".
"""
kind = "variable"
def __init__(self, variable, *args, **kwargs):
super(VariableValue, self).__init__(store, variable, *args, **kwargs)
@renpy.pure
class ScreenVariableValue(__GenericValue):
"""
:doc: value
:args: {args}
A bar value that adjusts the value of a variable in a screen.
In a ``use``\ d screen, this targets a variable in the context of
the screen containing the ``use``\ d one(s). To target variables
within a ``use``\ d screen, and only in that case, use
:func:`LocalVariableValue` instead.
`variable`
A string giving the name of the variable to adjust.
"""
kind = "screen variable" # not used in error messages, only in doc-gen
identity_fields = ()
equality_fields = __GenericValue.equality_fields + ('variable',)
def __init__(self, variable, *args, **kwargs):
self.variable = variable
super(ScreenVariableValue, self).__init__(*args, **kwargs)
def get_value(self):
cs = renpy.current_screen()
if cs is None:
raise Exception("No current screen.")
if self.variable not in cs.scope:
raise Exception("The {!r} variable is not defined in the {} screen.".format(self.variable, cs.screen_name))
value = cs.scope[self.variable]
return value
def set_value(self, value):
cs = renpy.current_screen()
cs.scope[self.variable] = value
# unpure
class LocalVariableValue(DictValue):
"""
:doc: value
:args: {args}
A bar value that adjusts the value of a variable in a ``use``\ d
screen.
To target a variable in a top-level screen, prefer using
:func:`ScreenVariableValue`.
For more information, see :ref:`sl-use`.
This must be created in the context that the variable is set in
- it can't be passed in from somewhere else.
`variable`
A string giving the name of the variable to adjust.
"""
kind = "local variable"
def __init__(self, variable, *args, **kwargs):
super(LocalVariableValue, self).__init__(sys._getframe(1).f_locals, variable, *args, **kwargs)
init -1500 python hide:
if config.generating_documentation:
import inspect
import itertools
generic_params = tuple(inspect.signature(__GenericValue.__init__).parameters.values())[1:]
suffix = inspect.cleandoc("""
The field, a string.
`range`
The range to adjust over.
`max_is_zero`
If True, then when the {kind}'s value is zero, the value of the
bar will be `range`, and all other values will be shifted down
by 1. This works both ways - when the bar is set to the maximum,
the value of the {kind} is set to 0.
If True, then when the field is zero, the value of the
bar will be range, and all other values will be shifted
down by 1. This works both ways - when the bar is set to
the maximum, the field is set to 0.
This is used internally, for some preferences.
`style`
The styles of the created bar.
The styles of the bar created.
`offset`
An offset to add to the value.
`step`
The amount to change the bar by. If None, defaults to 1/10th of
the bar.
`action`
If not None, an action to call when the {kind}'s value is changed.
""")
If not None, an action to call when the field has changed.
"""
for value in (DictValue, FieldValue, VariableValue, ScreenVariableValue, LocalVariableValue):
docstr = inspect.cleandoc(value.__doc__)
offset = 0
action = None
force_step = False
kind = "field"
params = []
for param in itertools.islice(inspect.signature(value.__init__).parameters.values(), 1, None):
if param.kind not in (param.VAR_POSITIONAL, param.VAR_KEYWORD):
params.append(param)
identity_fields = [ 'object', ]
equality_fields = [ 'range', 'max_is_zero', 'style', 'offset', 'step', 'action', 'force_step', 'field' ]
params.extend(generic_params)
def __init__(self, object, field, range, max_is_zero=False, style="bar", offset=0, step=None, action=None, force_step=False, kind="field"):
self.object = object
self.field = field
self.range = range
self.max_is_zero = max_is_zero
self.style = style
self.offset = offset
self.force_step = force_step
self.kind = kind
value.__doc__ = (docstr+"\n"+suffix).format(
args=inspect.Signature(parameters=params),
kind=value.kind)
if step is None:
if isinstance(range, float):
step = range / 10.0
else:
step = max(range / 10, 1)
init -1500 python:
self.step = step
self.action = action
def changed(self, value):
if self.max_is_zero:
if value == self.range:
value = 0
else:
value = value + 1
value += self.offset
_set_field(self.object, self.field, value, "field")
renpy.restart_interaction()
return renpy.run(self.action)
def get_adjustment(self):
value = _get_field(self.object, self.field, "field")
value -= self.offset
if self.max_is_zero:
if value == 0:
value = self.range
else:
value = value - 1
return ui.adjustment(
range=self.range,
value=value,
changed=self.changed,
step=self.step,
force_step=self.force_step,
)
def get_style(self):
return self.style, "v" + self.style
@renpy.pure
def VariableValue(variable, range, max_is_zero=False, style="bar", offset=0, step=None, action=None, force_step=False):
"""
:doc: value
A bar value that allows the user to adjust the value of a variable
in the default store.
`variable`
The `variable` parameter must be a string, and can be a simple name like "strength", or
one with dots separating the variable from fields, like "hero.strength"
or "persistent.show_cutscenes".
`range`
The range to adjust over.
`max_is_zero`
If True, then when the field is zero, the value of the
bar will be range, and all other values will be shifted
down by 1. This works both ways - when the bar is set to
the maximum, the field is set to 0.
This is used internally, for some preferences.
`style`
The styles of the bar created.
`offset`
An offset to add to the value.
`step`
The amount to change the bar by. If None, defaults to 1/10th of
the bar.
`action`
If not None, an action to call when the field has changed.
"""
return FieldValue(store, variable, range, max_is_zero=max_is_zero, style=style, offset=offset, step=step, action=action, force_step=force_step, kind="variable")
@renpy.pure
class ScreenVariableValue(BarValue, FieldEquality):
"""
:doc: value
A bar value that adjusts the value of a variable in a screen.
`variable`
A string giving the name of the variable to adjust.
`range`
The range to adjust over.
`max_is_zero`
If True, then when the field is zero, the value of the
bar will be range, and all other values will be shifted
down by 1. This works both ways - when the bar is set to
the maximum, the field is set to 0.
This is used internally, for some preferences.
`style`
The styles of the bar created.
`offset`
An offset to add to the value.
`step`
The amount to change the bar by. If None, defaults to 1/10th of
the bar.
`action`
If not None, an action to call when the field has changed.
"""
action = None
offset = 0
force_step = False
identity_fields = [ ]
equality_fields = [ 'variable', 'max_is_zero', 'style', 'offset', 'step', 'action', 'force_step' ]
def __init__(self, variable, range, max_is_zero=False, style="bar", offset=0, step=None, action=None, force_step=False):
self.variable = variable
self.range = range
self.max_is_zero = max_is_zero
self.style = style
self.offset = offset
self.force_step = force_step
if step is None:
if isinstance(range, float):
step = range / 10.0
else:
step = max(range / 10, 1)
self.step = step
self.action = action
def changed(self, value):
cs = renpy.current_screen()
if self.max_is_zero:
if value == self.range:
value = 0
else:
value = value + 1
value += self.offset
cs.scope[self.variable] = value
renpy.restart_interaction()
return renpy.run(self.action)
def get_adjustment(self):
cs = renpy.current_screen()
if (cs is None) or (self.variable not in cs.scope):
raise Exception("{} is not defined in the {} screen.".format(self.variable, cs.screen_name))
value = cs.scope[self.variable]
value -= self.offset
if self.max_is_zero:
if value == 0:
value = self.range
else:
value = value - 1
return ui.adjustment(
range=self.range,
value=value,
changed=self.changed,
step=self.step,
force_step=self.force_step,
)
def get_style(self):
return self.style, "v" + self.style
@renpy.pure
class MixerValue(BarValue, DictEquality):
+1 -8
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2023 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2023 Tom Rothamel <pytom@bishoujo.us>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
@@ -159,7 +159,6 @@ init -1500 python in build:
("*.dll", None),
("*.manifest", None),
("*.keystore", None),
("update.pem", None),
("lib/", None),
("renpy/", None),
@@ -208,7 +207,6 @@ init -1500 python in build:
("web-presplash.png", "web"),
("web-presplash.jpg", "web"),
("web-presplash.webp", "web"),
("web-icon.png", "web"),
("progressive_download.txt", "web"),
("steam_appid.txt", None),
@@ -508,9 +506,6 @@ init -1500 python in build:
# Should the sdk-fonts directory be renamed to game?
_sdk_fonts = False
# Which update formats should be built?
update_formats = [ "rpu" ]
# This function is called by the json_dump command to dump the build data
# into the json file.
def dump():
@@ -594,8 +589,6 @@ init -1500 python in build:
rv["_sdk_fonts"] = _sdk_fonts
rv["update_formats"] = update_formats
return rv
init 1500 python in build:
+2 -6
View File
@@ -232,6 +232,7 @@ init -1100 python:
if version <= (7, 4, 8):
config.relative_transform_size = False
config.tts_front_to_back = False
if version <= (7, 4, 10):
config.always_unfocus = False
@@ -291,14 +292,9 @@ init -1100 python:
store.layeredimage._constant = True
store.updater._constant = True
if _compat_versions(version, (7, 6, 1), (8, 1, 1)):
config.tts_front_to_back = False
_greedy_rollback = False
if _compat_versions(version, (7, 6, 99), (8, 1, 99)):
config.simple_box_reverse = True
build.itch_channels = list(build.itch_channels.items())
style.default.shaper = "freetype"
# The version of Ren'Py this script is intended for, or
# None if it's intended for the current version.
@@ -410,7 +406,7 @@ init 1100 python hide:
config.layers.append('screens')
if "Fullscreen" in config.translations:
fs = __("Fullscreen")
fs = _("Fullscreen")
config.translations.setdefault("Fullscreen 4:3", fs + " 4:3")
config.translations.setdefault("Fullscreen 16:9", fs + " 16:9")
config.translations.setdefault("Fullscreen 16:10", fs + " 16:10")
+5 -9
View File
@@ -593,7 +593,7 @@ init -1500 python in _console:
else:
break
if s.partition("#")[0].rstrip().endswith(":"):
if s.rstrip().endswith(":"):
rv += " "
if not s.rstrip():
@@ -612,7 +612,7 @@ init -1500 python in _console:
self.lines.append(line)
indent = get_indent(line)
if indent or line.startswith("@") or line.endswith("\\"):
if indent:
self.lines.append(indent)
return
@@ -717,7 +717,7 @@ init -1500 python in _console:
pass
else:
result = renpy.python.py_eval(code)
if persistent._console_short and not getattr(result, "_console_always_long", False):
if persistent._console_short:
he.result = aRepr.repr(result)
else:
he.result = repr(result)
@@ -1103,25 +1103,21 @@ screen _console:
has vbox
$ last_line = ""
for line in lines:
hbox:
spacing 4
if (line[:1] != " ") and (last_line[:1] != "@") and (last_line[-1:] != "\\"):
if line[:1] != " ":
text "> " style "_console_prompt"
else:
text "... " style "_console_prompt"
text "[line!q]" style "_console_input_text"
$ last_line = line
hbox:
spacing 4
if (default[:1] != " ") and (last_line[:1] != "@") and (last_line[-1:] != "\\"):
if default[:1] != " ":
text "> " style "_console_prompt"
else:
text "... " style "_console_prompt"
-17
View File
@@ -280,10 +280,6 @@ init python in director:
return show_director
def after_load():
state.mode = "lines"
config.after_load_callbacks.append(after_load)
def interact():
"""
@@ -1770,7 +1766,6 @@ screen director_transform(state):
null height 14
text _("Click to set transform, right click to add to transform list.")
text _("Customize director.transforms to add more transforms.")
use director_footer(state)
@@ -1791,8 +1786,6 @@ screen director_behind(state):
style "director_button"
ypadding 0
null height 14
text _("Click to set, right click to add to behind list.")
use director_footer(state)
@@ -1813,11 +1806,6 @@ screen director_with(state):
style "director_button"
ypadding 0
null height 14
text _("Click to set.")
text _("Customize director.transitions to add more transitions.")
use director_footer(state)
@@ -1836,11 +1824,6 @@ screen director_channel(state):
style "director_button"
ypadding 0
null height 14
text _("Click to set.")
text _("Customize director.audio_channels to add more channels.")
use director_footer(state)
-4
View File
@@ -99,15 +99,11 @@ init -1700 python:
renpy.context_dynamic("_side_image_old")
renpy.context_dynamic("_side_image_raw")
renpy.context_dynamic("_side_image")
renpy.context_dynamic("_side_image_attributes")
renpy.context_dynamic("_side_image_attributes_reset")
store._window_subtitle = config.menu_window_subtitle
store._window = False
store._history = False
store._menu = True
store._side_image_attributes = None
store._side_image_attribute_reset = False
store.mouse_visible = True
store.suppress_overlay = True
-3
View File
@@ -257,9 +257,6 @@ init -1200 python:
is true.
"""
if developer and config.developer:
return True
return renpy.display.controller.exists()
-2
View File
@@ -568,8 +568,6 @@ init -1500 python in iap:
This is supported on Google Play and the Apple App Store, only.
"""
return backend.request_review()
def missing_products():
"""
Determines if any products are missing from persistent._iap_purchases
+90 -143
View File
@@ -40,12 +40,12 @@ init -1510 python:
elif self.action == "disable":
if current == self.input_value:
if current is self.input_value:
renpy.set_editable_input_value(self.input_value, False)
elif self.action == "toggle":
if current == self.input_value and editable:
if current is self.input_value and editable:
renpy.set_editable_input_value(self.input_value, False)
else:
renpy.set_editable_input_value(self.input_value, True)
@@ -56,7 +56,7 @@ init -1510 python:
current, editable = renpy.get_editable_input_value()
rv = (current == self.input_value) and editable
rv = (current is self.input_value) and editable
if self.action == "disable":
rv = not rv
@@ -79,27 +79,22 @@ init -1510 python:
@renpy.pure
class InputValue(renpy.object.Object):
"""
Subclassable by creators, documented in Sphinx.
"""
default = True
editable = True
returnable = False
disable_on_enter = False
def get_text(self):
raise NotImplementedError
raise Exception("Not implemented.")
def set_text(self, s):
raise NotImplementedError
raise Exception("Not implemented.")
def enter(self):
if self.returnable:
return self.get_text()
elif self.disable_on_enter:
renpy.run(self.Disable())
raise renpy.IgnoreEvent
else:
return None
def Enable(self):
if self.editable:
@@ -119,41 +114,70 @@ init -1510 python:
else:
return None
class __GenericInputValue(InputValue, FieldEquality):
"""
Not subclassable by creators, not documented, meant to factorize
common features of the documented input value classes.
"""
equality_fields = ("default", "returnable", "disable_on_enter")
def __init__(self, default=True, returnable=False, disable_on_enter=False):
self.default = default
self.returnable = returnable
self.disable_on_enter = disable_on_enter
class ScreenVariableInputValue(__GenericInputValue):
@renpy.pure
class VariableInputValue(InputValue, FieldEquality):
"""
:doc: input_value
:args: {args}
An input value that updates a variable in a screen.
In a ``use``\ d screen, this targets a variable in the context of the
screen containing the ``use``\ d one(s). To target variables within a
``use``\ d screen, and only in that case, use
:func:`LocalVariableInputValue` instead.
An input value that updates `variable`.
`variable`
A string giving the name of the variable to update.
The `variable` parameter must be a string, and can be a simple name like "strength", or
one with dots separating the variable from fields, like "hero.strength"
or "persistent.show_cutscenes".
`default`
If true, this input can be editable by default.
`returnable`
If true, the value of this input will be returned when the
user presses enter.
"""
identity_fields = ("screen",)
equality_fields = __GenericInputValue.equality_fields+("variable",)
identity_fields = [ ]
equality_fields = [ "variable", "returnable" ]
def __init__(self, variable, *args, **kwargs):
super(ScreenVariableInputValue, self).__init__(*args, **kwargs)
def __init__(self, variable, default=True, returnable=False):
self.variable = variable
self.default = default
self.returnable = returnable
def get_text(self):
return _get_field(store, self.variable, "variable")
def set_text(self, s):
_set_field(store, self.variable, s, "variable")
renpy.restart_interaction()
class ScreenVariableInputValue(InputValue, FieldEquality):
"""
:doc: input_value
An input value that updates variable.
`variable`
A string giving the name of the variable to update.
`default`
If true, this input can be editable by default.
`returnable`
If true, the value of this input will be returned when the
user presses enter.
"""
identity_fields = [ 'screen' ]
equality_fields = [ "variable", "returnable" ]
def __init__(self, variable, default=True, returnable=False):
self.variable = variable
self.default = default
self.returnable = returnable
self.screen = renpy.current_screen()
def get_text(self):
@@ -166,145 +190,68 @@ init -1510 python:
renpy.restart_interaction()
@renpy.pure
class FieldInputValue(__GenericInputValue):
class FieldInputValue(InputValue, FieldEquality):
"""
:doc: input_value
:args: {args}
An input value that updates `field` on `object`.
`field`
A string giving the name of the field.
`default`
If true, this input can be editable by default.
`returnable`
If true, the value of this input will be returned when the
user presses enter.
"""
identity_fields = ("object",)
equality_fields = __GenericInputValue.equality_fields+("field",)
identity_fields = [ "object"]
equality_fields = [ "field", "returnable" ]
kind = "field"
def __init__(self, object, field, *args, **kwargs):
super(FieldInputValue, self).__init__(*args, **kwargs)
def __init__(self, object, field, default=True, returnable=False):
self.object = object
self.field = field
self.default = default
self.returnable = returnable
def get_text(self):
return _get_field(self.object, self.field, self.kind)
return _get_field(self.object, self.field, "field")
def set_text(self, s):
_set_field(self.object, self.field, s, self.kind)
_set_field(self.object, self.field, s, "field")
renpy.restart_interaction()
@renpy.pure
class VariableInputValue(FieldInputValue):
class DictInputValue(InputValue, FieldEquality):
"""
:doc: input_value
:args: {args}
An input value that updates `variable`.
An input value that updates `key` in `dict`.
`variable`
A string giving the name of the variable to update.
`default`
If true, this input can be editable by default.
The `variable` parameter must be a string, and can be a simple name like "strength", or
one with dots separating the variable from fields, like "hero.strength"
or "persistent.show_cutscenes".
`returnable`
If true, the value of this input will be returned when the
user presses enter.
"""
__version__ = 1
identity_fields = [ "dict", "key" ]
equality_fields = [ "returnable" ]
kind = "variable"
def after_upgrade(self, version):
if version < 1:
self.field = self.variable
self.object = store
del self.variable
def __init__(self, variable, *args, **kwargs):
super(VariableInputValue, self).__init__(store, variable, *args, **kwargs)
@renpy.pure
class DictInputValue(__GenericInputValue):
"""
:doc: input_value
:args: {args}
An input value that updates ``dict[key]``.
`dict` may be a dict object or a list.
"""
identity_fields = ("dict",)
equality_fields = __GenericInputValue.equality_fields+("key",)
def __init__(self, dict, key, *args, **kwargs):
super(DictInputValue, self).__init__(*args, **kwargs)
def __init__(self, dict, key, default=True, returnable=False):
self.dict = dict
self.key = key
self.default = default
self.returnable = returnable
def get_text(self):
return self.dict[self.key]
def set_text(self, s):
self.dict[self.key] = s
renpy.restart_interaction()
# not pure
class LocalVariableInputValue(DictInputValue):
"""
:doc: input_value
:args: {args}
An input value that updates a local variable in a ``use``\ d screen.
To target a variable in a top-level screen, prefer using
:func:`ScreenVariableInputValue`.
For more information, see :ref:`sl-use`.
This must be created in the context that the variable is set in - it
can't be passed in from somewhere else.
`variable`
A string giving the name of the variable to update.
"""
def __init__(self, variable, *args, **kwargs):
super(LocalVariableInputValue, self).__init__(sys._getframe(1).f_locals, variable, *args, **kwargs)
def get_text(self):
try:
return super(LocalVariableInputValue, self).get_text()
except LookupError:
raise Exception("The {!r} local variable does not exist.".format(self.key)) # from e # PY3 only
init -1510 python hide:
if config.generating_documentation:
import inspect
import itertools
generic_params = tuple(inspect.signature(__GenericInputValue.__init__).parameters.values())[1:]
suffix = inspect.cleandoc("""
`default`
If true, this input can be editable by default.
`returnable`
If true, the value of this input will be returned when the
user presses enter.
`disable_on_enter`
If true, and if `returnable` is not true, pressing enter
will disable this input.
""")
for ivalue in (ScreenVariableInputValue, FieldInputValue, VariableInputValue, DictInputValue, LocalVariableInputValue):
docstr = inspect.cleandoc(ivalue.__doc__)
params = []
for param in itertools.islice(inspect.signature(ivalue.__init__).parameters.values(), 1, None):
if param.kind not in (param.VAR_POSITIONAL, param.VAR_KEYWORD):
params.append(param)
params.extend(generic_params)
ivalue.__doc__ = (docstr+"\n"+suffix).format(
args=inspect.Signature(parameters=params),
)
+5 -30
View File
@@ -423,7 +423,7 @@ python early in layeredimage:
args.append(None)
args.append(Null())
return ConditionSwitch(*args, predict_all=predict_all)
return ConditionSwitch(predict_all=predict_all, *args)
class RawConditionGroup(object):
@@ -883,11 +883,6 @@ python early in layeredimage:
ll = l.subblock_lexer()
while ll.advance():
if ll.keyword("pass"):
ll.expect_eol()
ll.expect_noblock("pass")
continue
line(ll)
ll.expect_eol()
ll.expect_noblock('attribute')
@@ -922,23 +917,18 @@ python early in layeredimage:
if not l.match(':'):
l.expect_eol()
l.expect_noblock('always')
l.expect_noblock('attribute')
return
l.expect_block('always')
l.expect_block('attribute')
l.expect_eol()
ll = l.subblock_lexer()
while ll.advance():
if ll.keyword("pass"):
ll.expect_eol()
ll.expect_noblock("pass")
continue
line(ll)
ll.expect_eol()
ll.expect_noblock('always')
ll.expect_noblock('attribute')
if a.image is None:
l.error("The always statement must have a displayable.")
@@ -964,11 +954,6 @@ python early in layeredimage:
ll = l.subblock_lexer()
while ll.advance():
if ll.keyword("pass"):
ll.expect_eol()
ll.expect_noblock("pass")
continue
if ll.keyword("attribute"):
parse_attribute(ll, rv)
continue
@@ -1002,12 +987,7 @@ python early in layeredimage:
rv = RawCondition(condition)
while ll.advance():
# not necessary : the if/elif/else blocks require a displayable,
# so they can't be empty in the first place anyway
# if ll.keyword("pass"):
# ll.expect_eol()
# ll.expect_noblock("pass")
# continue
while True:
@@ -1098,11 +1078,6 @@ python early in layeredimage:
parse_always(ll, rv)
ll.advance()
elif ll.keyword("pass"):
ll.expect_noblock("pass")
ll.expect_eol()
ll.advance()
else:
while parse_property(ll, rv, [ "image_format", "format_function", "attribute_function", "offer_screen", "at" ] +
+15 -4
View File
@@ -478,12 +478,23 @@ init -1400 python hide:
@layout
def yesno_screen(message, yes=None, no=None):
"""
:undocumented:
:doc: other
renpy.confirm is a better alternative in a python context,
or the Confirm action in a screen context.
This causes the a yes/no prompt screen with the given message
to be displayed. The screen will be hidden when the user hits
yes or no.
`message`
The message that will be displayed.
`yes`
An action that is run when the user chooses yes.
`no`
An action that is run when the user chooses no.
See :func:`Confirm` for an equivalent Action.
"""
if config.confirm_screen and renpy.has_screen('confirm'):
screen = "confirm"
elif renpy.has_screen("yesno_prompt"):
+18
View File
@@ -303,6 +303,24 @@ init -1700 python:
config.expensive_predict_callbacks.append(_predict_screens)
##########################################################################
# Name-only say statements.
# This character is copied when a name-only say statement is called.
name_only = adv
def predict_say(who, what):
who = Character(who, kind=name_only)
try:
who.predict(what)
except Exception:
pass
def say(who, what, interact=True, *args, **kwargs):
who = Character(who, kind=name_only)
who(what, interact=interact, *args, **kwargs)
##########################################################################
# Constant stores.
#
+1 -1
View File
@@ -409,7 +409,7 @@ init -1500 python:
if filename is None:
return
if self.single_track or filename not in self.filenames:
if self.single_track:
self.play(None, offset=0, queue=True)
else:
self.play(None, offset=1, queue=True)
+2 -2
View File
@@ -363,14 +363,14 @@ init 1050 python hide:
raise Exception("bubble.properties[{!r}] contains a value that can't be serialized to JSON: {!r}".format(k, i))
if bubble.active:
config.always_shown_screens.append("_bubble_editor")
config.overlay_screens.append("_bubble_editor")
screen _bubble_editor():
zorder 1050
if bubble.shown.value and not _menu:
if bubble.shown.value:
drag:
draggable True
+6 -19
View File
@@ -560,7 +560,7 @@ init -1499 python in _renpysteam:
################################################################## Keyboard
# True to show the keyboard once, False otherwise.
keyboard_mode = "once"
keyboard_mode = "always"
# True if this is the start of a new interaction, and so the keyboard
# should be shown if a text box appears.
@@ -570,16 +570,12 @@ init -1499 python in _renpysteam:
keyboard_showing = None
# Should the layers be shifted so the baseline is in view?
keyboard_shift = False
keyboard_shift = True
# Where the baseline is shifted to on the screen. This is a floating point number,
# Where the basline is shifted to on the screen. This is a floating point number,
# with 0.0 being the top of the screen and 1.0 being the bottom.
keyboard_baseline = 0.5
# The textarea given to steam. This is scaled using the usual
# position rules.
keyboard_text_area = (0.0, 0.5, 1.0, 0.5)
def prime_keyboard():
global keyboard_primed
keyboard_primed = True
@@ -604,19 +600,10 @@ init -1499 python in _renpysteam:
_KeyboardShift.text_rect = keyboard_text_rect
if keyboard_primed and (keyboard_showing is None) and keyboard_text_rect:
x, y, w, h = (int(i) for i in keyboard_text_rect)
pw, ph = renpy.exports.get_physical_size()
def scale(n, available):
if type(n) == float:
n = n * available
return int(n)
x = scale(keyboard_text_area[0], pw)
y = scale(keyboard_text_area[1], ph)
w = scale(keyboard_text_area[2], pw)
h = scale(keyboard_text_area[3], ph)
if keyboard_shift:
y = int(renpy.exports.get_physical_size()[1] * keyboard_baseline) - h
steamapi.SteamUtils().ShowFloatingGamepadTextInput(
steamapi.k_EFloatingGamepadTextInputModeModeSingleLine,
+1 -4
View File
@@ -168,11 +168,8 @@ init -1800:
ruby_style style.ruby_text
altruby_style style.altruby_text
# hyperlink_functions is set in 00defaults.rpy
hinting True
hinting "auto"
adjust_spacing True
emoji_font "TwemojiCOLRv0.ttf"
prefer_emoji True
shaper ("harfbuzz" if (not PY2) else "freetype")
# Window properties
background None
+1 -5
View File
@@ -210,7 +210,7 @@ init -1100 python in _sync:
f.write(content)
fetch_id = emscripten.run_script_int(
"""fetchFile("PUT", "{url}", "/sync.data", null, "application/octet-string")""".format(url=url))
"""fetchFile("PUT", "{url}", "/sync.data", null)""".format(url=url))
status = "PENDING"
message = "Pending."
@@ -515,7 +515,6 @@ init -1100 python in _sync:
init -1100:
screen sync_confirm():
style_prefix "sync"
modal True
zorder 100
@@ -545,7 +544,6 @@ init -1100:
key "game_menu" action Return(False)
screen sync_prompt(prompt):
style_prefix "sync"
modal True
zorder 100
@@ -584,7 +582,6 @@ init -1100:
screen sync_success(sync_id):
style_prefix "sync"
modal True
zorder 100
@@ -620,7 +617,6 @@ init -1100:
key "game_menu" action Return(False)
screen sync_error(message):
style_prefix "sync"
modal True
zorder 100
+30 -286
View File
@@ -253,7 +253,7 @@ init -1500 python in updater:
# The update was cancelled.
CANCELLED = "CANCELLED"
def __init__(self, url, base=None, force=False, public_key=None, simulate=None, add=[], restart=True, check_only=False, confirm=True, patch=True, prefer_rpu=False, size_only=False, allow_empty=False):
def __init__(self, url, base=None, force=False, public_key=None, simulate=None, add=[], restart=True, check_only=False, confirm=True, patch=True):
"""
Takes the same arguments as update().
"""
@@ -312,12 +312,6 @@ init -1500 python in updater:
# Do we prompt for confirmation?
self.confirm = confirm
# Should rpu updates be preferred?
self.prefer_rpu = prefer_rpu
# Should the update be allowed even if current.json is empty?
self.allow_empty = allow_empty
# The base path of the game that we're updating, and the path to the update
# directory underneath it.
@@ -350,9 +344,6 @@ init -1500 python in updater:
# where each file is moved from <file>.new to <file>.
self.moves = [ ]
if self.allow_empty:
os.makedirs(self.updatedir, exist_ok=True)
if public_key is not None:
with renpy.open_file(public_key, False) as f:
self.public_key = rsa.PublicKey.load_pkcs1(f.read())
@@ -424,11 +415,14 @@ init -1500 python in updater:
Performs the update.
"""
if getattr(renpy, "mobile", False):
raise UpdateError(_("The Ren'Py Updater is not supported on mobile devices."))
self.load_state()
self.test_write()
self.check_updates()
self.pretty_version = self.check_versions()
pretty_version = self.check_versions()
if not self.modules:
self.can_cancel = False
@@ -438,61 +432,12 @@ init -1500 python in updater:
renpy.restart_interaction()
return
persistent._update_version[self.url] = self.pretty_version
persistent._update_version[self.url] = pretty_version
if self.check_only:
renpy.restart_interaction()
return
# Confirm goes here.
# Disable autoreload.
renpy.set_autoreload(False)
self.new_state = dict(self.current_state)
renpy.restart_interaction()
self.progress = 0.0
self.state = self.PREPARING
import os
has_rpu = False
has_zsync = False
prefer_rpu = self.prefer_rpu or "RPU_UPDATE" in os.environ
for i in self.modules:
for d in self.updates:
if "rpu_url" in self.updates[d]:
has_rpu = True
if "zsync_url" in self.updates[d]:
has_zsync = True
if has_rpu and has_zsync:
if prefer_rpu:
self.rpu_update()
else:
self.zsync_update()
elif has_rpu:
self.rpu_update()
elif has_zsync:
self.zsync_update()
else:
raise UpdateError(_("No update methods found."))
def prompt_confirm(self):
"""
Prompts the user to confirm the update. Returns if the update
should proceed, or raises UpdateCancelled if it should not.
"""
if self.confirm and (not self.add):
# Confirm with the user that the update is available.
@@ -500,7 +445,7 @@ init -1500 python in updater:
self.can_cancel = True
self.can_proceed = True
self.state = self.UPDATE_AVAILABLE
self.version = self.pretty_version
self.version = pretty_version
renpy.restart_interaction()
@@ -516,131 +461,15 @@ init -1500 python in updater:
self.can_cancel = True
self.can_proceed = False
def fetch_files_rpu(self, module):
"""
Fetches the rpu file list for the given module.
"""
import requests, zlib
url = urlparse.urljoin(self.url, self.updates[module]["rpu_url"])
try:
resp = requests.get(url)
resp.raise_for_status()
except Exception as e:
raise UpdateError(__("Could not download file list: ") + str(e))
if hashlib.sha256(resp.content).hexdigest() != self.updates[module]["rpu_digest"]:
raise UpdateError(__("File list digest does not match."))
data = zlib.decompress(resp.content)
from renpy.update.common import FileList
return FileList.from_json(json.loads(data))
def rpu_progress(self, state, progress):
"""
Called by the rpu code to update the progress.
"""
old_state = self.state
self.state = state
self.progress = progress
if state != old_state or progress == 1.0 or progress == 0.0:
renpy.restart_interaction()
def rpu_update(self):
"""
Perform an update using the .rpu files.
"""
from renpy.update.common import FileList
from renpy.update.update import Update
# 1. Load the current files.
target_file_lists = [ ]
for i in self.modules:
target_file_lists.append(FileList.from_current_json(self.current_state[i]))
# 2. Fetch the file lists.
source_file_lists = [ ]
module_lists = { }
for i in self.modules:
fl = self.fetch_files_rpu(i)
module_lists[i] = fl
source_file_lists.append(fl)
# 3. Compute the update, and confirm it.
u = Update(
urlparse.urljoin(self.url, "rpu"),
source_file_lists,
self.base,
target_file_lists,
progress_callback=self.rpu_progress,
logfile=self.log
)
self.prompt_confirm()
self.can_cancel = False
# 4. Remove the version.json file.
version_json = os.path.join(self.updatedir, "version.json")
if os.path.exists(version_json):
os.unlink(version_json)
# 5. Apply the update.
u.update()
# 6. Update the new state.
for i in self.modules:
d = module_lists[i].to_current_json()
d["version"] = self.updates[i]["version"]
d["renpy_version"] = self.updates[i]["renpy_version"]
d["pretty_version"] = self.updates[i]["pretty_version"]
self.new_state[i] = d
# 7. Finish up.
self.message = None
self.progress = None
self.can_proceed = True
self.can_cancel = False
persistent._update_version[self.url] = None
# 8. Write the version.json file.
version_state = { }
for i in self.modules:
version_state[i] = {
"version" : self.updates[i]["version"],
"renpy_version" : self.updates[i]["renpy_version"],
"pretty_version" : self.updates[i]["pretty_version"]
}
with open(os.path.join(self.updatedir, "version.json"), "w") as f:
json.dump(version_state, f)
if self.restart:
self.state = self.DONE
else:
self.state = self.DONE_NO_RESTART
# Disable autoreload.
renpy.set_autoreload(False)
# Perform the update.
self.new_state = dict(self.current_state)
renpy.restart_interaction()
def zsync_update(self):
self.prompt_confirm()
self.progress = 0.0
self.state = self.PREPARING
if self.patch:
for i in self.modules:
@@ -906,10 +735,6 @@ init -1500 python in updater:
fn = os.path.join(self.updatedir, "current.json")
if not os.path.exists(fn):
if self.allow_empty:
self.current_state = { }
return
raise UpdateError(_("Either this project does not support updating, or the update status file was deleted."))
with open(fn, "r") as f:
@@ -938,106 +763,36 @@ init -1500 python in updater:
fn = os.path.join(self.updatedir, "updates.json")
urlretrieve(self.url, fn)
with open(fn, "r") as f:
self.updates = json.load(f)
with open(fn, "rb") as f:
updates_json = f.read()
# Was updates.json verified?
verified = False
# Does updates.json need to be verified?
require_verified = False
# New-style ECDSA signature.
key = os.path.join(config.basedir, "update", "key.pem")
if not os.path.exists(key):
key = os.path.join(self.updatedir, "key.pem")
if os.path.exists(key):
require_verified = True
self.log.write("Verifying with ECDSA.\n")
try:
import ecdsa
verifying_key = ecdsa.VerifyingKey.from_pem(open(key, "rb").read())
url = urlparse.urljoin(self.url, "updates.ecdsa")
f = urlopen(url)
while True:
signature = f.read(64)
if not signature:
break
if verifying_key.verify(signature, updates_json):
verified = True
self.log.write("Verified with ECDSA.\n")
except Exception:
if self.log:
import traceback
traceback.print_exc(None, self.log)
# Old-style RSA signature.
if self.public_key is not None:
require_verified = True
fn = os.path.join(self.updatedir, "updates.json.sig")
urlretrieve(self.url + ".sig", fn)
self.log.write("Verifying with RSA.\n")
with open(fn, "rb") as f:
import codecs
signature = codecs.decode(f.read(), "base64")
try:
fn = os.path.join(self.updatedir, "updates.json.sig")
urlretrieve(self.url + ".sig", fn)
with open(fn, "rb") as f:
import codecs
signature = codecs.decode(f.read(), "base64")
rsa.verify(updates_json, signature, self.public_key)
verified = True
self.log.write("Verified with RSA.\n")
except Exception:
if self.log:
import traceback
traceback.print_exc(None, self.log)
raise UpdateError(_("Could not verify update signature."))
if require_verified and not verified:
raise UpdateError(_("Could not verify update signature."))
self.updates = json.loads(updates_json)
if verified and "monkeypatch" in self.updates:
future.utils.exec_(self.updates["monkeypatch"], globals(), globals())
if "monkeypatch" in self.updates:
future.utils.exec_(self.updates["monkeypatch"], globals(), globals())
def add_dlc_state(self, name):
has_rpu = "rpu_url" in self.updates[name]
has_zsync = "zsync_url" in self.updates[name]
prefer_rpu = self.prefer_rpu or "RPU_UPDATE" in os.environ
if has_rpu and has_zsync:
if prefer_rpu:
has_zsync = False
else:
has_rpu = False
if has_rpu:
fl = self.fetch_files_rpu(name)
d = { name : fl.to_current_json() }
else:
url = urlparse.urljoin(self.url, self.updates[name]["json_url"])
f = urlopen(url)
d = json.load(f)
url = urlparse.urljoin(self.url, self.updates[name]["json_url"])
f = urlopen(url)
d = json.load(f)
d[name]["version"] = 0
self.current_state.update(d)
def check_versions(self):
"""
Decides what modules need to be updated, if any.
@@ -1693,7 +1448,7 @@ init -1500 python in updater:
return not not get_installed_packages(base)
def update(url, base=None, force=False, public_key=None, simulate=None, add=[], restart=True, confirm=True, patch=True, prefer_rpu=False, allow_empty=False):
def update(url, base=None, force=False, public_key=None, simulate=None, add=[], restart=True, confirm=True, patch=True):
"""
:doc: updater
@@ -1739,23 +1494,12 @@ init -1500 python in updater:
changed data. If false, Ren'Py will download a complete copy of
the game, and update from that. This is set to false automatically
when the url does not begin with "http:".
This is ignored if the RPU update format is being used.
`prefer_rpu`
If True, Ren'Py will prefer the RPU format for updates, if both
zsync and RPU are available.
`allow_empty`
If True, Ren'Py will allow the update to proceed even if the
base directory does not contain update information. (`add` must
be provided in this case.)
"""
global installed_packages_cache
installed_packages_cache = None
u = Updater(url=url, base=base, force=force, public_key=public_key, simulate=simulate, add=add, restart=restart, confirm=confirm, patch=patch, prefer_rpu=prefer_rpu, allow_empty=allow_empty)
u = Updater(url=url, base=base, force=force, public_key=public_key, simulate=simulate, add=add, restart=restart, confirm=confirm, patch=patch)
ui.timer(.1, repeat=True, action=renpy.restart_interaction)
renpy.call_screen("updater", u=u)
Binary file not shown.
-11
View File
@@ -1,11 +0,0 @@
Twiemoji
--------
https://github.com/twitter/twemoji
https://github.com/Emoji-COLRv0/Emoji-COLRv0
Copyright 2019 Twitter, Inc and other contributors
Code licensed under the MIT License: http://opensource.org/licenses/MIT
Graphics licensed under CC-BY 4.0: https://creativecommons.org/licenses/by/4.0/
-110
View File
@@ -65,25 +65,11 @@ screen _developer:
textbutton _("Image Attributes"):
action ui.callsinnewcontext("_image_attributes")
if not renpy.get_screen("_translation_identifier"):
textbutton _("Show Translation Identifiers"):
action Show("_translation_identifier")
else:
textbutton _("Hide Translation Identifiers"):
action Hide("_translation_identifier")
if bubble.active:
textbutton _("Speech Bubble Editor (Shift+B)"):
action [ SetField(bubble.shown, "value", True), Hide("_developer") ]
if not renpy.get_screen("_filename_and_line"):
textbutton _("Show Filename and Line"):
action Show("_filename_and_line")
else:
textbutton _("Hide Filename and Line"):
action Hide("_filename_and_line")
hbox:
spacing gui._scale(25)
@@ -546,102 +532,6 @@ screen _image_load_log():
timer 10.0 action SetScreenVariable("show_help", False)
screen _translation_identifier():
zorder 1500
default show_copy = False
default show_help = True
style_prefix ""
python:
identifier = renpy.get_translation_identifier()
copy = None
if identifier:
copy = (SetScreenVariable("show_copy", True),
Function(pygame_sdl2.scrap.put,
pygame_sdl2.scrap.SCRAP_TEXT,
identifier.encode("utf8")))
drag:
draggable True
focus_mask None
xpos 0
ypos 0
frame:
style "empty"
background "#0004"
xpadding 5
ypadding 5
has vbox
textbutton "[identifier]":
action copy
padding (0, 0)
text_color "#fff"
text_hover_color "#bdf"
text_size 14
if show_copy:
text _("\n{color=#fff}Copied to clipboard.{/color}"):
size 14
timer 2.0 action SetScreenVariable("show_copy", False)
elif show_help:
text _("\n{color=#fff}Click to copy.\nDrag to move.{/color}"):
size 14
timer 10.0 action SetScreenVariable("show_help", False)
screen _filename_and_line():
zorder 1500
style_prefix ""
# Should help be shown?
default help = True
# The filename and line to show.
$ filename, line = renpy.get_filename_line()
timer 3 action SetScreenVariable("help", False)
drag:
draggable True
focus_mask None
xpos 0
ypos 0
frame:
style "empty"
background "#0004"
xpadding 5
ypadding 5
xminimum 200
has vbox
textbutton "[filename]:[line]":
padding (0, 0)
text_color "#fff"
text_hover_color "#bdf"
text_size 14
action EditFile(filename, line)
if help:
null height 10
text _("Click to open in editor."):
size 14
color "#fff"
init 1000 python hide:
if config.transparent_tile:
tile = im.Tile("_transparent_tile.png", (config.screen_width, config.screen_height))
+1 -4
View File
@@ -85,11 +85,8 @@ init label _errorhandling:
slow_cps None
slow_cps_multiplier 1.0
slow_abortable False
hinting True
hinting "auto"
adjust_spacing True
emoji_font "TwemojiCOLRv0.ttf"
prefer_emoji True
shaper ("harfbuzz" if (not PY2) else "freetype")
# Window properties
background None
+1 -7
View File
@@ -1210,7 +1210,7 @@ raise_image_exceptions = True
relative_transform_size = True
# Should tts of layers be from front to back?
tts_front_to_back = True
tts_front_to_back = False
# Should live2d loading be logged to log.txt
log_live2d_loading = False
@@ -1398,12 +1398,6 @@ ex_rollback_classes = [ ]
# Should we revert to the old behavior of box_reverse?
simple_box_reverse = False
# A map from font name to the hinting for the font.
font_hinting = { None : "auto" }
# Should we execute costly tasks which are
# avoidable when not generating the documentation ?
generating_documentation = False
del os
del collections
+5 -3
View File
@@ -402,8 +402,9 @@ adv = ADVCharacter(None,
kind=False)
# This character is copied when a name-only say statement is called.
name_only = adv
# predict_say and who are defined in 00library.rpy, but we add default
# versions here in case there is a problem with initialization. (And
# for pickling purposes.)
def predict_say(who, what):
@@ -416,7 +417,8 @@ def predict_say(who, what):
def say(who, what, interact=True, *args, **kwargs):
who = Character(who, kind=adv)
who(what, *args, interact=interact, **kwargs)
who(what, interact=interact, *args, **kwargs)
# Used by renpy.reshow_say and extend.
_last_say_who = None
+3 -3
View File
@@ -690,9 +690,6 @@ cdef class RenderTransform:
ypoi = math.degrees(ypoi)
zpoi = math.degrees(zpoi)
if xplacement or yplacement or state.zpos:
self.reverse = Matrix.offset(-xplacement, -yplacement, -state.zpos) * self.reverse
if poi or orientation or xyz_rotate:
m = Matrix.offset(-width / 2, -height / 2, -z11)
@@ -710,6 +707,9 @@ cdef class RenderTransform:
self.reverse = m * self.reverse
if xplacement or yplacement or state.zpos:
self.reverse = Matrix.offset(-xplacement, -yplacement, -state.zpos) * self.reverse
if state.rotate is not None:
m = Matrix.offset(-width / 2, -height / 2, 0.0)
m = Matrix.rotate(0, 0, -state.rotate) * m
+1 -1
View File
@@ -308,7 +308,7 @@ class SMAnimation(renpy.display.displayable.Displayable):
for edges in self.edges.values():
args.extend(edges)
return SMAnimation(self.initial, *args, delay=self.delay, **self.properties)
return SMAnimation(self.initial, delay=self.delay, *args, **self.properties)
def Animation(*args, **kwargs):
+1 -16
View File
@@ -1903,19 +1903,6 @@ class Adjustment(renpy.object.Object):
self.ranged = ranged
self.force_step = force_step
def viewport_replaces(self, replaces): # type: (Adjustment) -> None
if replaces is self:
return
self.range = replaces.range
self.value = replaces.value
self.animation_amplitude = replaces.animation_amplitude
self.animation_target = replaces.animation_target
self.animation_start = replaces.animation_start
self.animation_delay = replaces.animation_delay
self.animation_warper = replaces.animation_warper
def round_value(self, value, release):
# Prevent deadlock border points
if value <= 0:
@@ -2127,9 +2114,7 @@ class Bar(renpy.display.displayable.Displayable):
self.value = value
adjustment = value.get_adjustment()
if renpy.game.interface is not None:
renpy.game.interface.timeout(0)
renpy.game.interface.timeout(0)
tooltip = value.get_tooltip()
if tooltip is not None:
+6 -1
View File
@@ -1937,6 +1937,8 @@ class Interface(object):
pygame.event.set_blocked(i)
pygame.event.set_blocked(pygame.TEXTINPUT)
# Fix a problem with fullscreen and maximized.
if renpy.game.preferences.fullscreen:
renpy.game.preferences.maximized = False
@@ -2033,7 +2035,7 @@ class Interface(object):
if i in renderers:
gl2_renderers.append(i + "2")
if renpy.config.gl2 or renpy.macintosh:
if renpy.config.gl2:
renderers = gl2_renderers + renderers
# Prevent a performance warning if the renderer
@@ -2131,6 +2133,7 @@ class Interface(object):
# Stop the resizing.
pygame.key.stop_text_input() # @UndefinedVariable
pygame.key.set_text_input_rect(None) # @UndefinedVariable
pygame.event.set_blocked(pygame.TEXTINPUT)
self.text_rect = None
self.old_text_rect = None
self.display_reset = False
@@ -2916,6 +2919,7 @@ class Interface(object):
rect = (x0, y0, x1 - x0, y1 - y0)
pygame.key.set_text_input_rect(rect) # @UndefinedVariable
pygame.event.set_allowed(pygame.TEXTINPUT)
if not self.old_text_rect or not_shown:
pygame.key.start_text_input() # @UndefinedVariable
@@ -2932,6 +2936,7 @@ class Interface(object):
if self.old_text_rect:
pygame.key.stop_text_input() # @UndefinedVariable
pygame.key.set_text_input_rect(None) # @UndefinedVariable
pygame.event.set_blocked(pygame.TEXTINPUT)
if self.touch_keyboard:
renpy.exports.hide_screen('_touch_keyboard', layer='screens')
-2
View File
@@ -457,7 +457,6 @@ class Drag(renpy.display.displayable.Displayable, renpy.revertable.RevertableObj
raise Exception("Drag expects either zero or one children.")
self.child = renpy.easy.displayable(d)
renpy.display.render.invalidate(self)
def _clear(self):
self.child = None
@@ -937,7 +936,6 @@ class DragGroup(renpy.display.layout.MultiBox):
super(DragGroup, self).add(child)
self.sorted = False
renpy.display.render.invalidate(self)
def remove(self, child):
"""
+1 -1
View File
@@ -422,7 +422,7 @@ def before_interact(roots):
grab = replaced_by.get(id(grab), None)
if override is not None:
d = renpy.exports.get_displayable(*override, base=True) # type: ignore
d = renpy.exports.get_displayable(base=True, *override) # type: ignore
if (d is not None) and (current is not d) and not grab:
current = d
+43 -78
View File
@@ -712,9 +712,8 @@ class Image(ImageBase):
"""
is_svg = False
dpi = 96
def __init__(self, filename, dpi=96, **properties):
def __init__(self, filename, **properties):
"""
@param filename: The filename that the image will be loaded from.
"""
@@ -732,10 +731,7 @@ class Image(ImageBase):
super(Image, self).__init__(filename, **properties)
self.filename = filename
self.dpi = dpi
self.is_svg = filename.lower().endswith(".svg")
self.pixel_perfect = self.is_svg
def _repr_info(self):
return repr(self.filename)
@@ -775,23 +771,19 @@ class Image(ImageBase):
# avoid size-related exceptions (e.g. Crop on a smaller placeholder)
surf = renpy.display.pgrender.transform_scale(surf, force_size)
self.is_svg = filename.lower().endswith(".svg")
self.pixel_perfect = self.is_svg
if self.is_svg:
width, height = surf.get_size()
width = int(width * renpy.display.draw.draw_per_virt * self.dpi / 96)
height = int(height * renpy.display.draw.draw_per_virt * self.dpi / 96)
width = int(width * renpy.display.draw.draw_per_virt)
height = int(height * renpy.display.draw.draw_per_virt)
if filename != self.filename:
filelike = renpy.loader.load(self.filename, directory="images")
# This should only run for placeholder images.
surf = renpy.display.pgrender.transform_scale(surf, (width, height))
else:
filelike = renpy.loader.load(self.filename, directory="images")
with filelike as f:
surf = renpy.display.pgrender.load_image(filelike, filename, size=(width, height))
with filelike as f:
surf = renpy.display.pgrender.load_image(filelike, filename, size=(width, height))
return surf
@@ -1020,9 +1012,8 @@ class FactorScale(ImageBase):
image logo doubled = im.FactorScale("logo.png", 1.5)
.. deprecated:: 7.4.0
Use the :tpref:`zoom`, or the
:tpref:`xzoom` and :tpref:`yzoom` transform properties.
The same effect can now be achieved with the :tpref:`zoom` or the
:tpref:`xzoom` and :tpref:`yzoom` transform properties.
"""
def __init__(self, im, width, height=None, bilinear=True, **properties):
@@ -1082,9 +1073,9 @@ class Flip(ImageBase):
image eileen flip = im.Flip("eileen_happy.png", vertical=True)
.. deprecated:: 7.4.0
Set :tpref:`xzoom` (for horizontal flip)
or :tpref:`yzoom` (for vertical flip) to a negative value.
The same effect can now be achieved by setting
:tpref:`xzoom` (for horizontal flip)
or :tpref:`yzoom` (for vertical flip) to a negative value.
"""
def __init__(self, im, horizontal=False, vertical=False, **properties):
@@ -1174,8 +1165,7 @@ class Crop(ImageBase):
image logo crop = im.Crop("logo.png", (0, 0, 100, 307))
.. deprecated:: 7.4.0
Use the :tpref:`crop` transform property.
The same effect can now be achieved by setting the :tpref:`crop` transform property.
"""
def __init__(self, im, x, y=None, w=None, h=None, **properties):
@@ -1375,8 +1365,7 @@ class Blur(ImageBase):
image logo blurred = im.Blur("logo.png", 1.5)
.. deprecated:: 7.4.0
Use the :tpref:`blur` transform property.
The same effect can now be achieved with the :tpref:`blur` transform property.
"""
def __init__(self, im, xrad, yrad=None, **properties):
@@ -1438,10 +1427,6 @@ class MatrixColor(ImageBase):
The components of the transformed color are clamped to the
range [0.0, 1.0].
.. deprecated:: 7.4.0
Use ``Transform(im, matrixcolor=matrix, **properties)``.
See :func:`Transform` and :tpref:`matrixcolor`.
"""
def __init__(self, im, matrix, **properties):
@@ -1490,9 +1475,6 @@ class matrix(tuple):
`matrix` is a 20 or 25 element list or tuple. If it is 20 elements
long, it is padded with (0, 0, 0, 0, 1) to make a 5x5 matrix,
suitable for multiplication.
.. deprecated:: 7.4.0
Use :class:`Matrix`.
"""
def __new__(cls, *args):
@@ -1585,9 +1567,8 @@ im.matrix(%f, %f, %f, %f, %f.
Returns an identity matrix, one that does not change color or
alpha.
.. deprecated:: 7.4.0
Use :func:`IdentityMatrix() <IdentityMatrix>`
with the :tpref:`matrixcolor` transform property.
A suitable equivalent for the :tpref:`matrixcolor` transform property
is IdentityMatrix().
"""
return matrix(1, 0, 0, 0, 0,
@@ -1617,9 +1598,8 @@ im.matrix(%f, %f, %f, %f, %f.
mostly sensitive to green, more of the green channel is
kept then the other two channels.
.. deprecated:: 7.4.0
Use :func:`SaturationMatrix(value, desat) <SaturationMatrix>`
with the :tpref:`matrixcolor` transform property.
A suitable equivalent for the :tpref:`matrixcolor` transform property
is SaturationMatrix(value, desat).
"""
r, g, b = desat
@@ -1642,9 +1622,8 @@ im.matrix(%f, %f, %f, %f, %f.
grayscale). This is equivalent to calling
im.matrix.saturation(0).
.. deprecated:: 7.4.0
Use :func:`SaturationMatrix(0) <SaturationMatrix>`
with the :tpref:`matrixcolor` transform property.
A suitable equivalent for the :tpref:`matrixcolor` transform property
is SaturationMatrix(0).
"""
return matrix.saturation(0.0)
@@ -1662,9 +1641,8 @@ im.matrix(%f, %f, %f, %f, %f.
the value of the red channel is 100, the transformed color
will have a red value of 50.)
.. deprecated:: 7.4.0
Use :func:`TintMatrix(Color((r, g, b))) <TintMatrix>`
with the :tpref:`matrixcolor` transform property.
A suitable equivalent for the :tpref:`matrixcolor` transform property
is TintMatrix(Color((r, g, b))).
"""
return matrix(r, 0, 0, 0, 0,
@@ -1681,9 +1659,8 @@ im.matrix(%f, %f, %f, %f, %f.
Returns an im.matrix that inverts the red, green, and blue
channels of the image without changing the alpha channel.
.. deprecated:: 7.4.0
Use :func:`InvertMatrix(1.0) <InvertMatrix>`
with the :tpref:`matrixcolor` transform property.
A suitable equivalent for the :tpref:`matrixcolor` transform property
is InvertMatrix(1.0).
"""
return matrix(-1, 0, 0, 0, 1,
@@ -1704,9 +1681,8 @@ im.matrix(%f, %f, %f, %f, %f.
a number between -1 and 1, with -1 the darkest possible
image and 1 the brightest.
.. deprecated:: 7.4.0
Use :func:`BrightnessMatrix(b) <BrightnessMatrix>`
with the :tpref:`matrixcolor` transform property.
A suitable equivalent for the :tpref:`matrixcolor` transform property
is BrightnessMatrix(b).
"""
return matrix(1, 0, 0, 0, b,
@@ -1723,9 +1699,8 @@ im.matrix(%f, %f, %f, %f, %f.
Returns an im.matrix that alters the opacity of an image. An
`o` of 0.0 is fully transparent, while 1.0 is fully opaque.
.. deprecated:: 7.4.0
Use :func:`OpacityMatrix(o) <OpacityMatrix>`
with the :tpref:`matrixcolor` transform property.
A suitable equivalent for the :tpref:`matrixcolor` transform property
is OpacityMatrix(o).
"""
return matrix(1, 0, 0, 0, 0,
@@ -1743,9 +1718,8 @@ im.matrix(%f, %f, %f, %f, %f.
be greater than 0.0, with values between 0.0 and 1.0 decreasing contrast, and
values greater than 1.0 increasing contrast.
.. deprecated:: 7.4.0
Use :func:`ContrastMatrix(c) <ContrastMatrix>`
with the :tpref:`matrixcolor` transform property.
A suitable equivalent for the :tpref:`matrixcolor` transform property
is ContrastMatrix(c).
"""
return matrix.brightness(-.5) * matrix.tint(c, c, c) * matrix.brightness(.5)
@@ -1760,9 +1734,8 @@ im.matrix(%f, %f, %f, %f, %f.
Returns an im.matrix that rotates the hue by `h` degrees, while
preserving luminosity.
.. deprecated:: 7.4.0
Use :func:`HueMatrix(h) <HueMatrix>`
with the :tpref:`matrixcolor` transform property.
A suitable equivalent for the :tpref:`matrixcolor` transform property
is HueMatrix(h).
"""
h = h * math.pi / 180
@@ -1796,9 +1769,8 @@ im.matrix(%f, %f, %f, %f, %f.
im.matrix.colorize("#f00", "#00f"))
.. deprecated:: 7.4.0
Use :func:`ColorizeMatrix(black_color, white_color) <ColorizeMatrix>`
with the :tpref:`matrixcolor` transform property.
A suitable equivalent for the :tpref:`matrixcolor` transform property
is ColorizeMatrix(black_color, white_color).
"""
(r0, g0, b0, _a0) = renpy.easy.color(black_color) # type: ignore
@@ -1825,9 +1797,8 @@ def Grayscale(im, desat=(0.2126, 0.7152, 0.0722), **properties):
An image manipulator that creates a desaturated version of the image
manipulator `im`.
.. deprecated:: 7.4.0
Set the :tpref:`matrixcolor` transform property to
:func:`SaturationMatrix(0) <SaturationMatrix>`.
The same effect can now be achieved by supplying SaturationMatrix(0)
to the :tpref:`matrixcolor` transform property.
"""
return MatrixColor(im, matrix.saturation(0.0, desat), **properties)
@@ -1841,9 +1812,8 @@ def Sepia(im, tint=(1.0, .94, .76), desat=(0.2126, 0.7152, 0.0722), **properties
An image manipulator that creates a sepia-toned version of the image
manipulator `im`.
.. deprecated:: 7.4.0
Set the :tpref:`matrixcolor` transform property to
:func:`SepiaMatrix() <SepiaMatrix>`
The same effect can now be achieved by supplying SepiaMatrix()
to the :tpref:`matrixcolor` transform property.
"""
return MatrixColor(im, matrix.saturation(0.0, desat) * matrix.tint(tint[0], tint[1], tint[2]), **properties)
@@ -1884,8 +1854,8 @@ class Tile(ImageBase):
If not None, a (width, height) tuple. If None, this defaults to
(:var:`config.screen_width`, :var:`config.screen_height`).
.. deprecated:: 7.4.0
Use :func:`Tile(im, xysize=size, **properties) <Tile>`.
The same effect can now be achieved using the :func:`Tile`
displayable, with ``Tile(im, size=size)``.
"""
def __init__(self, im, size=None, **properties):
@@ -1981,7 +1951,7 @@ def image(arg, loose=False, **properties):
"""
:doc: im_image
:name: Image
:args: (filename, *, optimize_bounds=True, oversample=1, dpi=96, **properties)
:args: (filename, *, optimize_bounds=True, oversample=1, **properties)
Loads an image from a file. `filename` is a
string giving the name of the file.
@@ -2000,11 +1970,6 @@ def image(arg, loose=False, **properties):
with more pixels than its logical size would imply. For example, if
an image file is 2048x2048 and oversample is 2, then the image will
be treated as a 1024x1024 image for the purpose of layout.
`dpi`
The DPI of an SVG image. This defaults to 96, but that can be
increased to render the SVG larger, and decreased to render
it smaller.
"""
"""
+2 -12
View File
@@ -31,11 +31,7 @@ import renpy
from renpy.display.render import render, Render
if PY2:
def compute_raw(value, room):
return renpy.display.core.absolute.compute_raw(value, room)
else:
compute_raw = renpy.display.core.absolute.compute_raw
compute_raw = renpy.display.core.absolute.compute_raw
def xyminimums(style, width, height):
@@ -209,8 +205,6 @@ class Container(renpy.display.displayable.Displayable):
if child._duplicatable:
self._duplicatable = True
renpy.display.render.invalidate(self)
def _clear(self):
self.child = None
self.children = self._list_type()
@@ -2075,7 +2069,7 @@ class AdjustTimes(Container):
def event(self, ev, x, y, st):
st, _ = self.adjusted_times()
return Container.event(self, ev, x, y, st)
Container.event(self, ev, x, y, st)
def get_placement(self):
return self.child.get_placement()
@@ -2424,7 +2418,6 @@ class NearRect(Container):
class Layer(AdjustTimes):
"""
:doc: disp_layer
:args: (layer, *, clipping=True, **properties)
This allows a layer to be shown as a displayable on another layer.
Intended for use with detached layers.
@@ -2437,9 +2430,6 @@ class Layer(AdjustTimes):
`clipping`
If False, the layer's contents may exceed its bounds, otherwise
anything exceeding the bounds will be trimmed.
An entry in config.layer_clipping will cause this option to be
ignored, and clipping to occur as specified by that config.
"""
# Used to store layer_transitions when processing this layer.
+2 -2
View File
@@ -901,7 +901,7 @@ class Transform(Container):
self.active = True
if self.state.last_events != self.state.events:
if self.state.events and renpy.game.interface is not None:
if self.state.events:
renpy.game.interface.timeout(0)
self.state.last_events = self.state.events
@@ -1109,7 +1109,7 @@ class ATLTransform(renpy.atl.ATLTransformBase, Transform):
self.active = True
if self.state.last_events != self.state.events:
if self.state.events and renpy.game.interface is not None:
if self.state.events:
renpy.game.interface.timeout(0)
self.state.last_events = self.state.events
-9
View File
@@ -233,9 +233,6 @@ def set_root(d):
old_self_voicing = False
# The text used to show a notification.
notify_text = None
def displayable(d):
"""
Causes the TTS system to read the text of the displayable `d`.
@@ -244,7 +241,6 @@ def displayable(d):
global old_self_voicing
global last
global last_raw
global notify_text
self_voicing = renpy.game.preferences.self_voicing
@@ -286,11 +282,6 @@ def displayable(d):
else:
d = root
if notify_text and not s.startswith(notify_text):
s = notify_text + ": " + s
notify_text = None
if s != last_raw:
last_raw = s
+44 -43
View File
@@ -249,43 +249,33 @@ def get_movie_texture_web(channel, mask_channel, side_mask, mipmap):
return tex, new
def resize_movie(r, width, height):
"""
A utility function to resize a Render or texture to the given
dimensions.
"""
def render_movie(channel, width, height, group=None):
tex, _new = get_movie_texture(channel)
if r is None:
if group is not None:
if tex is None:
tex = group_texture.get(group, None)
else:
group_texture[group] = tex
if tex is None:
return None
rv = renpy.display.render.Render(width, height)
sw, sh = tex.get_size()
sw, sh = r.get_size()
if not (sw and sh):
return rv
scale = min(1.0 * width / sw, 1.0 * height / sh) # type: float
scale = min(1.0 * width / sw, 1.0 * height / sh)
dw = scale * sw
dh = scale * sh
rv = renpy.display.render.Render(width, height)
rv.forward = renpy.display.matrix.Matrix2D(1.0 / scale, 0.0, 0.0, 1.0 / scale)
rv.reverse = renpy.display.matrix.Matrix2D(scale, 0.0, 0.0, scale)
rv.blit(r, (int((width - dw) / 2), int((height - dh) / 2)))
rv.blit(tex, (int((width - dw) / 2), int((height - dh) / 2)))
return rv
def render_movie(channel, width, height):
"""
Called from the Draw objects to render and scale a fullscreen movie.
"""
tex, _new = get_movie_texture(channel)
return resize_movie(tex, width, height)
def default_play_callback(old, new): # @UnusedVariable
renpy.audio.music.play(new._play, channel=new.channel, loop=new.loop, synchro_start=True)
@@ -549,31 +539,42 @@ class Movie(renpy.display.displayable.Displayable):
return rv
tex, _ = get_movie_texture(self.channel, self.mask_channel, self.side_mask, self.style.mipmap)
if self.size is None:
tex, _ = get_movie_texture(self.channel, self.mask_channel, self.side_mask, self.style.mipmap)
if self.group is not None:
if tex is None:
tex = group_texture.get(self.group, None)
else:
group_texture[self.group] = tex
if (not not_playing) and (tex is not None):
width, height = tex.get_size()
rv = renpy.display.render.Render(width, height)
rv.blit(tex, (0, 0))
elif (not not_playing) and (self.start_image is not None):
surf = renpy.display.render.render(self.start_image, width, height, st, at)
w, h = surf.get_size()
rv = renpy.display.render.Render(w, h)
rv.blit(surf, (0, 0))
if self.group is not None:
if tex is None:
tex = group_texture.get(self.group, None)
else:
group_texture[self.group] = tex
if (not not_playing) and (tex is not None):
width, height = tex.get_size()
rv = renpy.display.render.Render(width, height)
rv.blit(tex, (0, 0))
elif (not not_playing) and (self.start_image is not None):
surf = renpy.display.render.render(self.start_image, width, height, st, at)
w, h = surf.get_size()
rv = renpy.display.render.Render(w, h)
rv.blit(surf, (0, 0))
rv = renpy.display.render.Render(0, 0)
else:
rv = renpy.display.render.Render(0, 0)
if self.size is not None:
rv = resize_movie(rv, self.size[0], self.size[1])
w, h = self.size
if not playing:
rv = None
else:
rv = render_movie(self.channel, w, h, group=self.group)
if rv is None:
rv = renpy.display.render.Render(w, h)
# Usually we get redrawn when the frame is ready - but we want
# the movie to disappear if it's ended, or if it hasn't started
+6 -18
View File
@@ -120,9 +120,6 @@ class Viewport(renpy.display.layout.Container):
self.yoffset = offsets[1] if (offsets[1] is not None) else yinitial
if isinstance(replaces, Viewport) and replaces.offsets:
self.xadjustment.viewport_replaces(replaces.xadjustment)
self.yadjustment.viewport_replaces(replaces.yadjustment)
self.xadjustment.range = replaces.xadjustment.range
self.xadjustment.value = replaces.xadjustment.value
self.yadjustment.range = replaces.yadjustment.range
@@ -131,11 +128,9 @@ class Viewport(renpy.display.layout.Container):
self.yoffset = replaces.yoffset
self.drag_position = replaces.drag_position
self.drag_position_time = replaces.drag_position_time
self.drag_speed = replaces.drag_speed
else:
self.drag_position = None # type: tuple[int, int]|None
self.drag_position_time = None # type: float|None
self.drag_speed = None
self.child_width, self.child_height = child_size
@@ -183,13 +178,6 @@ class Viewport(renpy.display.layout.Container):
self.xadjustment.register(self)
self.yadjustment.register(self)
def set_style_prefix(self, prefix, root):
"""
Do not change the style of children when the viewport is focused.
"""
return
def update_offsets(self, cw, ch, st):
"""
This is called by render once we know the width (`cw`) and height (`ch`)
@@ -660,13 +648,13 @@ class VPGrid(Viewport):
yspacing = self.style.spacing
if renpy.config.relative_spacing:
xspacing = renpy.display.layout.compute_raw(xspacing, width)
yspacing = renpy.display.layout.compute_raw(yspacing, height)
xspacing = renpy.display.layout.scale(xspacing, width)
yspacing = renpy.display.layout.scale(yspacing, height)
left_margin = renpy.display.layout.compute_raw(self.style.left_margin, width)
right_margin = renpy.display.layout.compute_raw(self.style.right_margin, width)
top_margin = renpy.display.layout.compute_raw(self.style.top_margin, height)
bottom_margin = renpy.display.layout.compute_raw(self.style.bottom_margin, height)
left_margin = renpy.display.layout.scale(self.style.left_margin, width)
right_margin = renpy.display.layout.scale(self.style.right_margin, width)
top_margin = renpy.display.layout.scale(self.style.top_margin, height)
bottom_margin = renpy.display.layout.scale(self.style.bottom_margin, height)
rend = renpy.display.render.render(self.children[0], child_width, child_height, st, at)
cw, ch = rend.get_size()
-2
View File
@@ -777,8 +777,6 @@ class Context(renpy.object.Object):
rv.last_abnormal = self.last_abnormal
rv.abnormal_stack = list(self.abnormal_stack)
rv.interacting = False
return rv
def predict_call(self, label, return_site):
+10 -231
View File
@@ -102,12 +102,11 @@ from renpy.character import show_display_say, predict_show_display_say, display_
import renpy.audio.sound as sound
import renpy.audio.music as music
from renpy.statements import register as register_statement
from renpy.statements import register as register_statement, register_decorator
from renpy.text.extras import check_text_tags
from renpy.memory import profile_memory, diff_memory, profile_rollback
from renpy.text.font import variable_font_info
from renpy.text.textsupport import TAG as TEXT_TAG, TEXT as TEXT_TEXT, PARAGRAPH as TEXT_PARAGRAPH, DISPLAYABLE as TEXT_DISPLAYABLE
from renpy.execution import not_infinite_loop, reset_all_contexts
@@ -123,8 +122,6 @@ from renpy.lint import try_compile, try_eval
from renpy.gl2.gl2shadercache import register_shader
from renpy.gl2.live2d import has_live2d
from renpy.bootstrap import get_alternate_base
renpy_pure("ParameterizedText")
renpy_pure("Keymap")
renpy_pure("has_screen")
@@ -852,7 +849,7 @@ def web_input(prompt, default='', allow=None, exclude='{}', length=None, mask=Fa
if roll_forward is not None:
default = roll_forward
wi = renpy.display.behavior.WebInput(substitute(prompt), default, length=length, allow=allow, exclude=exclude, mask=mask)
wi = renpy.display.behavior.WebInput(prompt, default, length=length, allow=allow, exclude=exclude, mask=mask)
renpy.ui.add(wi)
renpy.exports.shown_window()
@@ -1441,7 +1438,7 @@ def say(who, what, *args, **kwargs):
`who`
Either the character that will say something, None for the narrator,
or a string giving the character name. In the latter case, the
:var:`say` store function is called.
:func:`say` is used to create the speaking character.
`what`
A string giving the line to say. Percent-substitutions are performed
@@ -1458,7 +1455,6 @@ def say(who, what, *args, **kwargs):
e "Hello, world."
$ renpy.say(e, "Hello, world.")
$ e("Hello, world.") # when e is not a string
$ say(e, "Hello, world.") # when e is a string
"""
if renpy.config.old_substitutions:
@@ -2390,7 +2386,7 @@ def do_reshow_say(who, what, interact=False, *args, **kwargs):
if who is not None:
who = renpy.python.py_eval(who)
say(who, what, *args, interact=interact, **kwargs)
say(who, what, interact=interact, *args, **kwargs)
curried_do_reshow_say = curry(do_reshow_say)
@@ -2449,7 +2445,7 @@ def context_dynamic(*variables):
This can be given one or more variable names as arguments. This makes
the variables dynamically scoped to the current context. The variables will
be reset to their original value when returning to the prior context.
be reset to their original value when the call returns.
An example call is::
@@ -2759,9 +2755,9 @@ def iconify():
# New context stuff.
call_in_new_context = renpy.game.call_in_new_context
curried_call_in_new_context = curry(call_in_new_context)
curried_call_in_new_context = renpy.curry.curry(renpy.game.call_in_new_context)
invoke_in_new_context = renpy.game.invoke_in_new_context
curried_invoke_in_new_context = curry(invoke_in_new_context)
curried_invoke_in_new_context = renpy.curry.curry(renpy.game.invoke_in_new_context)
call_replay = renpy.game.call_replay
renpy_pure("curried_call_in_new_context")
@@ -3004,9 +3000,8 @@ def pop_call():
:doc: label
:name: renpy.pop_call
Pops the current call from the call stack, without returning to the
location. Also reverts the values of :func:`dynamic <renpy.dynamic>`
variables, the same way the Ren'Py return statement would.
Pops the current call from the call stack, without returning to
the location.
This can be used if a label that is called decides not to return
to its caller.
@@ -3343,7 +3338,7 @@ def call_screen(_screen_name, *args, **kwargs):
if "_with_none" in kwargs:
with_none = kwargs.pop("_with_none")
show_screen(_screen_name, *args, _transient=True, **kwargs)
show_screen(_screen_name, _transient=True, *args, **kwargs)
roll_forward = renpy.exports.roll_forward_info()
@@ -3513,8 +3508,6 @@ def display_notify(message):
hide_screen('notify')
show_screen('notify', message=message)
renpy.display.tts.notify_text = renpy.text.extras.filter_alt_text(message)
restart_interaction()
@@ -3916,12 +3909,6 @@ def set_return_stack(stack):
Statement names may be strings (for labels) or opaque tuples (for
non-label statements).
The most common use of this is to use::
renpy.set_return_stack([])
to clear the return stack.
"""
renpy.game.context().set_return_stack(stack)
@@ -4579,211 +4566,3 @@ def clear_retain(layer="screens", prefix="_retain"):
for i in get_showing_tags(layer):
if i.startswith(prefix):
hide_screen(i)
def confirm(message):
"""
:doc: other
This causes the a yes/no prompt screen with the given message
to be displayed, and dismissed when the player hits yes or no.
Returns True if the player hits yes, and False if the player hits no.
`message`
The message that will be displayed.
See :func:`Confirm` for a similar Action.
"""
Return = renpy.store.Return
renpy.store.layout.yesno_screen(message, yes=Return(True), no=Return(False))
return renpy.ui.interact()
class FetchError(Exception):
"""
:undocumented:
The type of errors raised by :func:`renpy.fetch`.
"""
pass
def fetch_requests(url, method, data, content_type, timeout):
"""
:undocumented:
Used by fetch on non-emscripten systems.
Returns either a bytes object, or a FetchError.
"""
import threading
import requests
# Because we don't have nonlocal yet.
resp = [ None ]
def make_request():
try:
r = requests.request(method, url, data=data, timeout=timeout, headers={ "Content-Type" : content_type } if data is not None else {})
r.raise_for_status()
resp[0] = r.content # type: ignore
except Exception as e:
resp[0] = FetchError(str(e)) # type: ignore
t = threading.Thread(target=make_request)
t.start()
while resp[0] is None:
renpy.exports.pause(0)
t.join()
return resp[0]
def fetch_emscripten(url, method, data, content_type, timeout):
"""
:undocumented:
Used by fetch on emscripten systems.
Returns either a bytes object, or a FetchError.
"""
import emscripten
import time
import os
fn = "/req-" + str(time.time()) + ".data"
with open(fn, "wb") as f:
if data is not None:
f.write(data)
url = url.replace('"' , '\\"')
if method == "GET" or method == "HEAD":
command = """fetchFile("{method}", "{url}", null, "{fn}", null)""".format( method=method, url=url, fn=fn, content_type=content_type)
else:
command = """fetchFile("{method}", "{url}", "{fn}", "{fn}", "{content_type}")""".format( method=method, url=url, fn=fn, content_type=content_type)
fetch_id = emscripten.run_script_int(command)
status = "PENDING"
message = "Pending."
start = time.time()
while start - time.time() < timeout:
renpy.exports.pause(0)
result = emscripten.run_script_string("""fetchFileResult({})""".format(fetch_id))
status, _ignored, message = result.partition(" ")
if status != "PENDING":
break
try:
if status == "OK":
with open(fn, "rb") as f:
return f.read()
else:
return FetchError(message)
finally:
os.unlink(fn)
def fetch(url, method=None, data=None, json=None, content_type=None, timeout=5, result="bytes"):
"""
:doc: fetch
This performs an HTTP (or HTTPS) request to the given URL, and returns
the content of that request. If it fails, raises a FetchError exception,
with text that describes the failure. (But may not be suitable for
presentation to the user.)
`url`
The URL to fetch.
`method`
The method to use. Generally one of "GET", "POST", or "PUT", but other
HTTP methdos are possible. If `data` or `json` are not None, defaults to
"POST", otherwise defaults to GET.
`data`
If not None, a byte string of data to send with the request.
`json`
If not None, a JSON object to send with the request. This takes precendence
over `data`.
`content_type`
The content type of the data. If not given, defaults to "application/json"
if `json` is not None, or "application/octet-stream" otherwise. Only
used on a POST or PUT request.
`timeout`
The number of seconds to wait for the request to complete.
`result`
How to process the result. If "bytes", returns the raw bytes of the result.
If "text", decodes the result using UTF-8 and returns a unicode string. If "json",
decodes the result as JSON. (Other exceptions may be generated by the decoding
process.)
While waiting for `timeout` to pass, this will repeatedly call :func:`renpy.pause`(0),
so Ren'Py doesn't lock up. It may make sense to display a screen to the user
to let them know what is going on.
This function should work on all platforms. However, on the web platform,
requests going to a different origin than the game will fail unless allowed
by CORS.
"""
import json as _json
if data is not None and json is not None:
raise FetchError("data and json arguments are mutually exclusive.")
if result not in ( "bytes", "text", "json" ):
raise FetchError("result must be one of 'bytes', 'text', or 'json'.")
if renpy.game.context().init_phase:
raise FetchError("renpy.fetch may not be called during init.")
if method is None:
if data is not None or json is not None:
method = "POST"
else:
method = "GET"
if content_type is None:
if json is not None:
content_type = "application/json"
else:
content_type = "application/octet-stream"
if json is not None:
data = _json.dumps(json).encode("utf-8")
if renpy.emscripten:
content = fetch_emscripten(url, method, data, content_type, timeout)
else:
content = fetch_requests(url, method, data, content_type, timeout)
if isinstance(content, Exception):
raise content # type: ignore
if result == "bytes":
return content
elif result == "text":
return content.decode("utf-8")
elif result == "json":
return _json.loads(content)
+5 -1
View File
@@ -262,6 +262,10 @@ def invoke_in_new_context(callable, *args, **kwargs): # @ReservedAssignment
information to the player (like a confirmation prompt) from inside
an event handler.
A context maintains the state of the display (including what screens
and images are being shown) and the audio system. Both are restored
when the context returns.
Additional arguments and keyword arguments are passed to the
callable.
@@ -368,7 +372,7 @@ def call_replay(label, scope={}):
Calls a label as a memory.
The `scope` argument is used to set the initial values of variables in the
Keyword arguments are used to set the initial values of variables in the
memory context.
"""
+1 -1
View File
@@ -658,7 +658,7 @@ cdef class GLDraw:
rv = self.texture_cache.get(surf, None)
if rv is None:
rv = gltexture.texture_grid_from_surface(surf, transient, properties)
rv = gltexture.texture_grid_from_surface(surf, transient)
self.texture_cache[surf] = rv
self.ready_texture_queue.add(rv)
+50 -30
View File
@@ -142,12 +142,21 @@ def test_texture_sizes(Environ environ, draw):
with nogil:
if tex_format == GL_RGBA:
for i from 0 <= i < size * size:
bitmap[i * 4 + 0] = 0xff # r
bitmap[i * 4 + 1] = 0x00 # g
bitmap[i * 4 + 2] = 0x00 # b
bitmap[i * 4 + 3] = 0xff # a
for i from 0 <= i < size * size:
bitmap[i * 4 + 0] = 0xff # r
bitmap[i * 4 + 1] = 0x00 # g
bitmap[i * 4 + 2] = 0x00 # b
bitmap[i * 4 + 3] = 0xff # a
else:
for i from 0 <= i < size * size:
bitmap[i * 4 + 0] = 0x00 # b
bitmap[i * 4 + 1] = 0x00 # g
bitmap[i * 4 + 2] = 0xff # r
bitmap[i * 4 + 3] = 0xff # a
# Create a texture of the given size.
glActiveTexture(GL_TEXTURE0)
@@ -358,8 +367,7 @@ cdef class TextureCore:
def load_surface(self, surf, x, y, w, h,
border_left, border_top, border_right, border_bottom,
properties):
border_left, border_top, border_right, border_bottom):
"""
Loads a pygame surface into this texture rectangle.
@@ -371,8 +379,7 @@ cdef class TextureCore:
self.premult = premultiply(
surf, x, y, w, h,
border_left, border_top, border_right, border_bottom,
properties.get("premultiplied", False))
border_left, border_top, border_right, border_bottom)
self.premult_size = (w, h)
@@ -887,7 +894,7 @@ def compute_tiling(width, max_size, min_fill_factor):
return row, tiles
def texture_grid_from_surface(surf, transient, properties):
def texture_grid_from_surface(surf, transient):
"""
This takes a Surface and turns it into a TextureGrid.
"""
@@ -924,8 +931,7 @@ def texture_grid_from_surface(surf, transient, properties):
tex = alloc_texture(texwidth, texheight)
tex.load_surface(surf, x, y, width, height,
border_x, border_y, border_x, border_y,
properties)
border_x, border_y, border_x, border_y)
row.append(tex)
@@ -1195,17 +1201,12 @@ def premultiply(
int y,
int w,
int h,
bint border_left, bint border_top, bint border_right, bint border_bottom,
bint premultiplied):
bint border_left, bint border_top, bint border_right, bint border_bottom):
"""
Creates a string containing the premultiplied image data for
for the (x, y, w, h) box inside pysurf. The various border_
parameters control the addition of a border on the sides.
`premultipled`
If true, the data has been premultiplied already, and
should be simply copied.
"""
# Iterator y and x.
@@ -1271,18 +1272,7 @@ def premultiply(
# Advance to the next row.
pixels += surf.pitch
if premultiplied:
while p < pend:
op[0] = p[0]
op[1] = p[1]
op[2] = p[2]
op[3] = p[3]
p += 4
op += 4
else:
if tex_format == GL_RGBA:
# RGBA path.
@@ -1310,6 +1300,36 @@ def premultiply(
p += 4
op += 4
else:
# BGRA Path.
if alpha:
while p < pend:
a = p[3]
op[0] = (p[2] * a + a) >> 8 # b
op[1] = (p[1] * a + a) >> 8 # g
op[2] = (p[0] * a + a) >> 8 # r
op[3] = a
p += 4
op += 4
else:
while p < pend:
op[0] = p[2] # b
op[1] = p[1] # g
op[2] = p[0] # r
op[3] = 0xff # a
p += 4
op += 4
if border_left:
pp = <unsigned int *> (out)
ppend = pp + w * h
+2 -1
View File
@@ -42,7 +42,7 @@ cdef class GL2Model:
for i in self.uniforms.itervalues():
if isinstance(i, GLTexture):
i.load()
i.load_gltexture()
def program_uniforms(self, shader):
"""
@@ -118,3 +118,4 @@ cdef class GL2Model:
rv.forward = Matrix.cscale(reciprocal_factor, reciprocal_factor, reciprocal_factor) * rv.forward
return rv
+1 -68
View File
@@ -493,67 +493,6 @@ cdef class GLTexture(GL2Model):
self.loaded = True
self.surface = None
def load_gltexture_premultiplied(GLTexture self):
"""
Loads this texture. When it's loaded, generation and number are set,
and the texture is ready to use.
"""
cdef GLuint premultiplied
cdef Program program
cdef SDL_Surface *s
cdef GLuint pixel_buffer
if self.loaded:
return
draw = self.loader.draw
s = PySurface_AsSurface(self.surface)
glGenTextures(1, &premultiplied)
# Load the pixel data into tex, and set it up for drawing.
glActiveTexture(GL_TEXTURE0)
self.allocate_texture(premultiplied, self.width, self.height, self.properties)
glBindTexture(GL_TEXTURE_2D, premultiplied)
# Use a pixel buffer to create a texture.
# Why use a Pixel Buffer? Apart from potentially being faster, this
# works around a bug in Samsung android devices running Android 11,
# where glTexImage2D doesn't seem to work when the pixels are not
# aligned.
# But it doesn't seem to work with ANGLE or emscripten, so we avoid using PBOs when
# angle is in use.
if not renpy.emscripten and not draw.angle:
glGenBuffers(1, &pixel_buffer)
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixel_buffer)
glBufferData(GL_PIXEL_UNPACK_BUFFER, s.h * s.pitch, s.pixels, GL_STATIC_DRAW)
glPixelStorei(GL_UNPACK_ROW_LENGTH, s.pitch // 4)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self.width, self.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, <void *> 0)
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER)
glDeleteBuffers(1, &pixel_buffer)
else:
glPixelStorei(GL_UNPACK_ROW_LENGTH, s.pitch // 4)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self.width, self.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, s.pixels)
self.mipmap_texture(premultiplied, self.width, self.height, self.properties)
# Store the loaded texture.
self.number = premultiplied
self.loader.allocated.add(self.number)
self.loaded = True
self.surface = None
def allocate_texture(GLTexture self, GLuint tex, int tw, int th, properties={}):
"""
Allocates the VRAM required to store `tex`, which is a `tw` x `th`
@@ -649,13 +588,7 @@ cdef class GLTexture(GL2Model):
pass # Let's not error on shutdown.
def load(self):
if self.properties.get("premultiplied", False):
self.load_gltexture_premultiplied()
else:
self.load_gltexture()
self.load_gltexture()
def program_uniforms(self, shader):
shader.set_uniform("tex0", self)
-3
View File
@@ -661,9 +661,6 @@ class Live2D(renpy.display.displayable.Displayable):
def _choose_attributes(self, tag, attributes, optional):
# Filter out _sustain.
attributes = [ i for i in attributes if i != "_sustain" ]
common = self.common
# Chose all motions.
+1 -4
View File
@@ -866,7 +866,7 @@ class Lexer(object):
def string(self):
"""
Lexes a non-triple-quoted string, and returns the string to the user, or None if
Lexes a string, and returns the string to the user, or None if
no string could be found. This also takes care of expanding
escapes and collapsing whitespace.
@@ -928,9 +928,6 @@ class Lexer(object):
This is about the same as the double-quoted strings, except that
runs of whitespace with multiple newlines are turned into a single
newline.
Except in the case of a raw string where this returns a simple string,
this returns a list of strings.
"""
s = self.match(r'r?"""([^\\"]|\\.|"(?!""))*"""')
+18 -92
View File
@@ -444,9 +444,6 @@ def quote_text(s):
def text_checks(s):
if renpy.config.say_menu_text_filter is not None:
s = renpy.config.say_menu_text_filter(s)
msg = renpy.text.extras.check_text_tags(s)
if msg:
report("%s (in %s)", msg, quote_text(s))
@@ -692,49 +689,8 @@ def check_style(name, s):
check_style_property_displayable(name, k, v)
def check_parameters(kind, node_name, parameter_info):
"""
`kind`
What we're parsing the parameters of, for the error message.
"screen", "label", "function", "ATL transform"...
`node_name`
The name of the (kind) we're defining.
`parameter_info`
The ParameterInfo we're scanning, or None.
"""
if parameter_info is None:
return
names = [p[0] for p in parameter_info.parameters]
if parameter_info.extrakw:
names.extend(parameter_info.extrakw)
if parameter_info.extrapos:
names.extend(parameter_info.extrapos)
rv = [name for name in names if (name in python_builtins) or (name in renpy_builtins)]
if len(rv) == 1:
name = rv[0]
report("In {0} {1!r}, the {2!r} parameter replaces a built-in name from either Python or Ren'Py, which may cause problems.".format(kind, node_name, name))
if not "_" in name:
add("This can be fixed by naming it '{}_'".format(name))
elif rv:
last = rv.pop()
prettyprevious = ", ".join(repr(name) for name in rv)
report("In {0} {1!r}, the {2} and {3!r} parameters replace built-in names from either Python or Ren'Py, which may cause problems.".format(kind,
node_name,
prettyprevious,
last))
def check_label(node):
if args.builtins_parameters:
check_parameters("label", node.name, node.parameters)
def add_arg(n):
if n is None:
return
@@ -759,9 +715,6 @@ def check_screen(node):
report("The screen %s has not been given a parameter list.", node.screen.name)
add("This can be fixed by writing 'screen %s():' instead.", node.screen.name)
if args.builtins_parameters:
check_parameters("screen", node.screen.name, node.screen.parameters)
def check_styles():
for full_name, s in renpy.style.styles.items(): # @UndefinedVariable
@@ -777,11 +730,6 @@ def check_init(node):
report("The init priority ({}) is not in the -999 to 999 range.".format(node.priority))
def check_transform(node):
if args.builtins_parameters:
check_parameters("ATL transform", node.varname, node.parameters)
def humanize(n):
s = str(n)
@@ -832,9 +780,6 @@ class Count(object):
self.words += len(s.split())
self.characters += len(s)
def tuple(self):
return (self.blocks, self.words, self.characters)
def common(n):
"""
@@ -855,33 +800,22 @@ def report_character_stats(charastats):
rv = [ "Character statistics (for default language):" ]
if args.words_char_count:
for char in sorted(charastats, key=lambda char: charastats[char].tuple(), reverse=True):
count = charastats[char]
rv.append(
" * " + char
+ " has " + humanize(count.blocks) + (" block " if count.blocks == 1 else " blocks ") + "of dialogue, "
+ "containing " + humanize(count.words) + " words and "
+ humanize(count.characters) + " characters."
count_to_char = collections.defaultdict(list)
for char, count in charastats.items():
count_to_char[count].append(char)
for count, chars in sorted(count_to_char.items(), reverse=True):
chars.sort()
start = humanize_listing(chars, singular_suffix=" has ", plural_suffix=" have ")
rv.append(
" * " + start + humanize(count) +
(" block " if count == 1 else " blocks ") + "of dialogue" +
(" each." if len(chars) > 1 else ".")
)
else:
nblocks_to_char = collections.defaultdict(list)
for char, count in charastats.items():
nblocks_to_char[count.blocks].append(char)
for nblocks, chars in sorted(nblocks_to_char.items(), reverse=True):
chars.sort()
start = humanize_listing(chars, singular_suffix=" has ", plural_suffix=" have ")
rv.append(
" * " + start + humanize(nblocks) +
(" block " if nblocks == 1 else " blocks ") + "of dialogue" +
(" each." if len(chars) > 1 else ".")
)
return rv
@@ -1033,11 +967,7 @@ def lint():
ap = renpy.arguments.ArgumentParser(description="Checks the script for errors and prints script statistics.", require_command=False)
ap.add_argument("filename", nargs='?', action="store", help="The file to write to.")
ap.add_argument("--error-code", action="store_true", help="If given, the error code is 0 if the game has no lint errros, 1 if lint errors are found.")
ap.add_argument("--orphan-tl", action="store_true", help="If given, orphan translations are reported.")
ap.add_argument("--builtins-parameters", action="store_true", help="If given, renpy or python builtin names in renpy statement parameters are reported.")
ap.add_argument("--words-char-count", action="store_true", help="If given, the number of words and characters for each character is reported.")
global args
args = ap.parse_args()
if args.filename:
@@ -1070,7 +1000,7 @@ def lint():
# The current count.
counts = collections.defaultdict(Count)
charastats = collections.defaultdict(Count)
charastats = collections.defaultdict(int)
# The current language.
language = None
@@ -1116,7 +1046,7 @@ def lint():
counts[language].add(node.what)
if language is None:
charastats[node.who or 'narrator'].add(node.what)
charastats[node.who or 'narrator' ] += 1
elif isinstance(node, renpy.ast.Menu):
check_menu(node)
@@ -1140,7 +1070,7 @@ def lint():
elif isinstance(node, renpy.ast.Label):
check_label(node)
elif isinstance(node, renpy.ast.Translate) and args.orphan_tl:
elif isinstance(node, renpy.ast.Translate):
language = node.language
if language is None:
none_language_ids.add(node.identifier)
@@ -1165,16 +1095,12 @@ def lint():
elif isinstance(node, renpy.ast.Init):
check_init(node)
elif isinstance(node, renpy.ast.Transform):
check_transform(node)
report_node = None
check_styles()
check_filename_encodings()
check_unreachables(all_stmts)
if args.orphan_tl:
check_orphan_translations(none_language_ids, translated_ids)
check_orphan_translations(none_language_ids, translated_ids)
for f in renpy.config.lint_hooks:
f()
+24 -46
View File
@@ -26,7 +26,6 @@ from typing import Optional
import renpy
import os
import os.path
import sys
import types
import threading
@@ -68,20 +67,34 @@ def get_path(fn):
# Asset Loading
apks = [ ]
game_apks = [ ]
if renpy.android:
import android.apk # type: ignore
if renpy.config.renpy_base == renpy.config.basedir:
# Read the game data from the APK.
expansion = os.environ.get("ANDROID_EXPANSION", None)
if expansion is not None:
print("Using expansion file", expansion)
apks.append(android.apk.APK(prefix='assets/x-game/'))
game_apks.append(apks[0])
apks = [
android.apk.APK(apk=expansion, prefix='assets/x-game/'),
android.apk.APK(apk=expansion, prefix='assets/x-renpy/x-common/'),
]
apks.append(android.apk.APK(prefix='assets/x-renpy/x-common/'))
game_apks = [ apks[0] ]
else:
print("Not using expansion file.")
apks = [
android.apk.APK(prefix='assets/x-game/'),
android.apk.APK(prefix='assets/x-renpy/x-common/'),
]
game_apks = [ apks[0] ]
else:
apks = [ ]
game_apks = [ ]
# Files on disk should be checked before archives. Otherwise, among
# other things, using a new version of bytecode.rpyb will break.
@@ -974,30 +987,19 @@ class RenpyImporter(object):
if self.translate(fullname):
return self
def load_module(self, fullname, mode="full"):
"""
Loads a module. Possible modes include "is_package", "get_source", "get_code", or "full".
"""
def load_module(self, fullname):
filename = self.translate(fullname, self.prefix)
if mode == "is_package":
return filename.endswith("__init__.py")
pyname = pystr(fullname)
mod = sys.modules.setdefault(pyname, types.ModuleType(pyname))
mod.__name__ = pyname
mod.__file__ = renpy.config.gamedir + "/" + filename
mod.__file__ = filename
mod.__loader__ = self
if filename.endswith("__init__.py"):
mod.__package__ = pystr(fullname)
else:
mod.__package__ = pystr(fullname.rpartition(".")[0])
if mod.__file__.endswith("__init__.py"):
mod.__path__ = [ mod.__file__[:-len("__init__.py")] ]
mod.__path__ = [ filename[:-len("__init__.py")] ]
for encoding in [ "utf-8", "latin-1" ]:
@@ -1009,41 +1011,17 @@ class RenpyImporter(object):
source = source.encode("raw_unicode_escape")
source = source.replace(b"\r", b"")
if mode == "get_source":
return source
code = compile(source, filename, 'exec', renpy.python.old_compile_flags, 1)
break
except Exception:
if encoding == "latin-1":
raise
if mode == "get_code":
return code # type: ignore
exec(code, mod.__dict__) # type: ignore
return sys.modules[fullname]
def is_package(self, fullname):
return self.load_module(fullname, "is_package")
def get_source(self, fullname):
return self.load_module(fullname, "get_source")
def get_code(self, fullname):
return self.load_module(fullname, "get_code")
def get_data(self, filename):
filename = os.path.normpath(filename).replace('\\', '/')
_check_prefix = "{0}/".format(
os.path.normpath(renpy.config.gamedir).replace('\\', '/')
)
if filename.startswith(_check_prefix):
filename = filename[len(_check_prefix):]
return load(filename).read()
+1 -11
View File
@@ -36,7 +36,6 @@ import types
import shutil
import os
import sys
import time
import renpy
from json import dumps as json_dumps
@@ -437,13 +436,7 @@ def save(slotname, extra_info='', mutate_flag=False):
screenshot = renpy.game.interface.get_screenshot()
json = {
"_save_name" : extra_info,
"_renpy_version" : list(renpy.version_tuple),
"_version" : renpy.config.version,
"_game_runtime" : renpy.exports.get_game_runtime(),
"_ctime" : time.time(),
}
json = { "_save_name" : extra_info, "_renpy_version" : list(renpy.version_tuple), "_version" : renpy.config.version }
for i in renpy.config.save_json_callbacks:
i(json)
@@ -565,9 +558,6 @@ def force_autosave(take_screenshot=False, block=False):
if renpy.game.after_rollback or renpy.exports.in_rollback():
return
if not renpy.store._autosave:
return
# That is, autosave is running.
if not autosave_not_running.is_set():
return
-1
View File
@@ -239,7 +239,6 @@ class StdioRedirector(object):
def __init__(self):
self.buffer = ''
self.log = open("log", developer=False, append=False, flush=True)
self.encoding = "utf-8"
def write(self, s):
+2 -3
View File
@@ -252,9 +252,8 @@ def choose_variants():
renpy.config.variants.insert(0, 'web') # type: ignore
# mobile
mobile = emscripten.run_script_int(
r'''/Mobile|Android|iPad|iPhone/.test(navigator.userAgent)
|| (navigator.userAgent.indexOf("Mac") != -1 && navigator.maxTouchPoints > 1)''')
userAgent = emscripten.run_script_string(r'''navigator.userAgent''')
mobile = re.search('Mobile|Android|iPad|iPhone', userAgent)
if mobile:
renpy.config.variants.insert(0, 'mobile') # type: ignore
# Reserve android/ios for when the OS API is exposed
+1 -1
View File
@@ -501,7 +501,7 @@ def parse_arguments(l):
state = l.checkpoint()
if not (expect_starred or expect_doublestarred):
name = l.word()
name = l.name()
if name and l.match(r'='):
if name in names:
+1 -1
View File
@@ -137,7 +137,7 @@ Preference("self_voicing_volume_drop", 0.5)
Preference("emphasize_audio", False)
# Is the gamepad enabled?
Preference("pad_enabled", True, (bool, str))
Preference("pad_enabled", True)
# The side of the screen used for rollback. ("left", "right", or "disable")
Preference("mobile_rollback_side", "disable")

Some files were not shown because too many files have changed in this diff Show More