Compare commits
367 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d8644f2f8 | |||
| c7a284c1e4 | |||
| 8b79a3663c | |||
| fdf38adc83 | |||
| 921feb4ada | |||
| f367e996aa | |||
| c0c182d200 | |||
| a5fec5a7a5 | |||
| 0f668f2810 | |||
| 5cba186326 | |||
| 74631fe701 | |||
| 13eeb9d0c0 | |||
| 1066157585 | |||
| 7742627373 | |||
| 95102a6b71 | |||
| acac446c8b | |||
| 9fc8e43f17 | |||
| 6d6003f4e0 | |||
| 7ff1b3b5c5 | |||
| 4fd318880e | |||
| 6cd57b192a | |||
| 8c6aab0bc6 | |||
| 2451b3f0da | |||
| daea1375c3 | |||
| ada2b860f6 | |||
| 6758447cad | |||
| 035d6e7b30 | |||
| 0675f6ce44 | |||
| c7317c076b | |||
| 6bd420b421 | |||
| c1f2db72b5 | |||
| f87917e399 | |||
| 6bc50d6454 | |||
| a2bfa02de1 | |||
| a3ad875263 | |||
| 80d3075bf0 | |||
| 0b213cc7db | |||
| 23fc2f66c9 | |||
| 19498d5bce | |||
| 7f37382012 | |||
| 7fcad404b6 | |||
| d8ad7d1672 | |||
| e4b550b8c3 | |||
| 67bdb9ab03 | |||
| 6f44533bbe | |||
| d084b624b9 | |||
| 06d3a90bd7 | |||
| 5e64403699 | |||
| d8954aa732 | |||
| 669b07afef | |||
| e2bc3fb55c | |||
| 088b1572ef | |||
| a30f562d38 | |||
| 4768a012db | |||
| c7d8bd1dc9 | |||
| be226363e8 | |||
| 267a807146 | |||
| 34468cf57d | |||
| 172f9f926e | |||
| f6afced012 | |||
| 00cd7e4ada | |||
| 9f4ab7d761 | |||
| 9ac420c259 | |||
| 6c548d30ad | |||
| 615c6a728d | |||
| bb8c508837 | |||
| 30833f1f5f | |||
| 57bf18c705 | |||
| e30855cb93 | |||
| ed0f536853 | |||
| e4f601310b | |||
| b92107dd02 | |||
| 19c5e806b1 | |||
| f6d9762c5c | |||
| 8b679cca71 | |||
| 34fe132e55 | |||
| 0036bc3fe9 | |||
| d321d1ff51 | |||
| a98d15eed4 | |||
| 57dba01448 | |||
| 3532b589d5 | |||
| 51cc42a901 | |||
| e09b87dffe | |||
| c5752398b9 | |||
| d5fe9c9a11 | |||
| b8ca91a62a | |||
| 5aec41f9e1 | |||
| b1d1db1075 | |||
| 2708eda3d3 | |||
| ad83e0354e | |||
| 1c823a4e7c | |||
| 11186c86ae | |||
| 6014bfa007 | |||
| ef0d2db11d | |||
| 7dd1db50d2 | |||
| c8413912d2 | |||
| 969b2ccecf | |||
| 228c72f6c1 | |||
| ad9af51510 | |||
| 45faa71da8 | |||
| e530c78cc0 | |||
| c3a26654f7 | |||
| 14710606ef | |||
| 72cb9e3929 | |||
| 9c3b082260 | |||
| 46cfe60086 | |||
| 588d1e1137 | |||
| 15a12efd61 | |||
| b0434ea1d9 | |||
| 96b44cdcb2 | |||
| 1fde5f985a | |||
| 34dcdc7f0e | |||
| 2238b460a7 | |||
| ff8ba2d10c | |||
| 5e3495c333 | |||
| 79945d9649 | |||
| 254fa8c4ee | |||
| 14b596e15a | |||
| f33aef4711 | |||
| 7d86e8509a | |||
| dd26bbf859 | |||
| cde1f301d2 | |||
| 2d8e7d3a1a | |||
| 1c2b3ed231 | |||
| 021aa22a80 | |||
| e548ed13b2 | |||
| b1cf7eb64e | |||
| 86e9454b79 | |||
| f7d2434dff | |||
| 07c96e8872 | |||
| 0f51948f80 | |||
| 8448808c67 | |||
| 8da08dd187 | |||
| eedc86f20e | |||
| ccb978f454 | |||
| 33830fd6b4 | |||
| 615c624a70 | |||
| 66f17ae6dd | |||
| 7bf7303f85 | |||
| 4d8d065c89 | |||
| fc111cc0ab | |||
| b535e2b63c | |||
| fb294567fe | |||
| b2893b5ea8 | |||
| 5853d63076 | |||
| bd7cd2a0c6 | |||
| 9d42e5c667 | |||
| ab9ee40e1a | |||
| 0aa28b6804 | |||
| 0891fff309 | |||
| ca37ebacd1 | |||
| 51aff5d94d | |||
| 7ff6acc36c | |||
| cda1c18e03 | |||
| 7f615dcddc | |||
| 646a7535b0 | |||
| 4600339cf7 | |||
| 03fdf2801e | |||
| e427b4e910 | |||
| 0826778a5e | |||
| 5050ee20ee | |||
| b4cf19cb0c | |||
| dee35cfc66 | |||
| 325175f3c3 | |||
| 787f09929b | |||
| c5f9ddeae8 | |||
| c43c34c943 | |||
| ce07691bbc | |||
| 9e76a31da1 | |||
| 90701d03c1 | |||
| 0256f2bba7 | |||
| a12d9c544e | |||
| 8bb5291324 | |||
| 186b082e3c | |||
| 4dd40736a1 | |||
| 8c3741e6f9 | |||
| 6e7f28f574 | |||
| fa24e47d29 | |||
| 12acbfc963 | |||
| 8e1945a628 | |||
| fdae52f6ad | |||
| 4b47d59b3b | |||
| 9cde14fa6d | |||
| a98fa5f7fe | |||
| bf0bc1d837 | |||
| db24c1eb3a | |||
| c8a88bb17e | |||
| 9c73cff8ce | |||
| 5a4b14009e | |||
| a4cb1a123e | |||
| 057f69602b | |||
| 2040da88db | |||
| 82de5b7af0 | |||
| a688409eb9 | |||
| 2ec1cb0cdc | |||
| a73082df6f | |||
| e33888949a | |||
| 6422153ce1 | |||
| 9d1263d7c3 | |||
| 608cbab10c | |||
| 2b08c40a4a | |||
| bd01358481 | |||
| 1e518437dd | |||
| 2096c691ce | |||
| 6b4eadf0fd | |||
| fc8ac8344d | |||
| f6d8afdad6 | |||
| 420564557f | |||
| a8cab927bb | |||
| 81ee1d994e | |||
| 52c1e64b6f | |||
| 3ef755d5d4 | |||
| e719dd8d47 | |||
| ab3f1242d7 | |||
| 8df82cbac0 | |||
| 13c557b5a2 | |||
| 44e32ddffb | |||
| 852b489529 | |||
| 3ff08b9c03 | |||
| 67c2469d37 | |||
| 60745892c1 | |||
| d511804be3 | |||
| 487c60da2d | |||
| 96c63ea9e2 | |||
| 2eca653f47 | |||
| 6f8378f5b4 | |||
| 232eda7c98 | |||
| b6fc8496f4 | |||
| 8a2634b206 | |||
| 3521d54e0a | |||
| 6c1f1c8138 | |||
| 24c0616158 | |||
| 1102904380 | |||
| 2d690bd7cb | |||
| 3ec2ad0a7b | |||
| d3d33f31bb | |||
| 78dfdd024b | |||
| 517e12d3e5 | |||
| 1b26542f0c | |||
| 29e84de69c | |||
| 991fbf107a | |||
| 041a4c4d07 | |||
| e8de3df9c4 | |||
| f0ba1ac5a1 | |||
| 6931b5f06d | |||
| 2850cea6c0 | |||
| e364ca4173 | |||
| c6cc1dc5f6 | |||
| 12dbf12f44 | |||
| 9c7b8a6398 | |||
| 18d3f48686 | |||
| 78c71f0a0f | |||
| 86d0e1e275 | |||
| 935173fcf5 | |||
| 05e72cbe27 | |||
| 3dd1d32ef4 | |||
| 73725e2131 | |||
| c29b180647 | |||
| 65e2d27efb | |||
| 7045295eee | |||
| 3d1ab26448 | |||
| eeb6958174 | |||
| 80e2d1d3a9 | |||
| 2ccdb65cc5 | |||
| 6135979a7c | |||
| 0a01a4af64 | |||
| 0afd06dd49 | |||
| 9c5b8f5ba4 | |||
| ec6a8f8e1a | |||
| 99090ed87a | |||
| 3793a3f9ba | |||
| 5b88d6fb32 | |||
| b3beae8ab5 | |||
| c8d451838b | |||
| 678ac18b5f | |||
| 5cc6b34f56 | |||
| 4606bcce76 | |||
| d3595146b0 | |||
| 20ecec1412 | |||
| ebeed588fa | |||
| ef08ad74b3 | |||
| 0b47bc2eea | |||
| b2a49cc60e | |||
| 468733bd75 | |||
| 37d216097d | |||
| a1bcd63ba9 | |||
| 16c2455370 | |||
| d30d986a51 | |||
| b5288343fc | |||
| 371b3e15fc | |||
| 589a510c33 | |||
| 85648df290 | |||
| 936363d243 | |||
| 05075beed5 | |||
| b729eaed7d | |||
| d06bcd0dd2 | |||
| 2e34102cf5 | |||
| 3018c21828 | |||
| 457d0c6cce | |||
| c9573e3737 | |||
| a11a7bb2ee | |||
| e1b37142c2 | |||
| 32923dd8f8 | |||
| e4bef44f2c | |||
| 8a151ed1b4 | |||
| 54b4406974 | |||
| 4b3561d821 | |||
| fb197d90b9 | |||
| d32f5b10f5 | |||
| 446e4609a9 | |||
| 1f9a4df252 | |||
| 08eb08562f | |||
| 8d3401f2ac | |||
| 3370a07f18 | |||
| c164c1af40 | |||
| df26c03087 | |||
| db8a6f9f13 | |||
| 25986373d1 | |||
| f067652a3b | |||
| 122dbf2619 | |||
| 547bef2483 | |||
| efe6609085 | |||
| 1d43fb11dd | |||
| ff177d0dae | |||
| 1dd5ba512c | |||
| 3906f99ebc | |||
| f89c672c13 | |||
| 80ea555ad6 | |||
| af7a0baa2e | |||
| 3fa7da1384 | |||
| 014de8a873 | |||
| 89cfd03516 | |||
| 0b937fdea6 | |||
| 802652d579 | |||
| bcd4fd4d2c | |||
| 21016cca6c | |||
| 1921e7c985 | |||
| 6be8d94372 | |||
| 84c22334fe | |||
| 9f286afbbb | |||
| e32cd6c124 | |||
| f523069468 | |||
| 674d5d87dd | |||
| 12b37de529 | |||
| 562ce9734a | |||
| 148a14f939 | |||
| fe2ad45dc0 | |||
| 12330dead6 | |||
| 17fb5397c8 | |||
| 8398c3081e | |||
| 71e8bae6d2 | |||
| d312de84a4 | |||
| c3d1c2cee7 | |||
| 0dd3895b47 | |||
| 8230e4186d | |||
| 8e4e8b0e23 | |||
| 49eab5ffa9 | |||
| 8a7fcd95ce | |||
| d1ad17256b | |||
| 4035fc9621 | |||
| 3dab93666d | |||
| ff64ab49f1 | |||
| 15657da6dc | |||
| 872d4694db | |||
| 617c7e5367 | |||
| 1a375c525c | |||
| 553ada3b04 |
@@ -103,6 +103,9 @@ cache/
|
||||
# Pygame_sdl2.
|
||||
/pygame_sdl2
|
||||
|
||||
# Updater.
|
||||
update.pem
|
||||
|
||||
# Steam.
|
||||
steam_appid.txt
|
||||
/steamapi.py
|
||||
@@ -127,3 +130,6 @@ CubismSdkForNative-4-*.zip
|
||||
# Works in progress, throwaway scripts, etc.
|
||||
/scratch
|
||||
/*-dists
|
||||
/c
|
||||
/cg
|
||||
/.vscode/launch.json
|
||||
|
||||
Vendored
+18
@@ -190,6 +190,24 @@
|
||||
"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
@@ -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 libfreetype6-dev libfribidi-dev libsdl2-dev \
|
||||
libswresample-dev libswscale-dev libharfbuzz-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
@@ -252,7 +252,7 @@ def main():
|
||||
"-q",
|
||||
"egg_info",
|
||||
"--tag-build",
|
||||
"-for-renpy-" + args.version,
|
||||
"+renpy" + args.version,
|
||||
"sdist",
|
||||
"-d",
|
||||
os.path.abspath(destination)
|
||||
|
||||
@@ -42,7 +42,7 @@ class Editor(renpy.editor.Editor):
|
||||
elif renpy.arch == "armv7l":
|
||||
arch = "arm"
|
||||
else:
|
||||
arch = "x86_64"
|
||||
arch = "x64"
|
||||
|
||||
code = os.path.join(RENPY_VSCODE, "VSCode-linux-" + arch, "bin", "code")
|
||||
else:
|
||||
|
||||
@@ -42,7 +42,7 @@ class Editor(renpy.editor.Editor):
|
||||
elif renpy.arch == "armv7l":
|
||||
arch = "arm"
|
||||
else:
|
||||
arch = "x86_64"
|
||||
arch = "x64"
|
||||
|
||||
code = os.path.join(RENPY_VSCODE, "VSCode-linux-" + arch, "bin", "code")
|
||||
else:
|
||||
|
||||
+123
-34
@@ -528,6 +528,11 @@ 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
|
||||
|
||||
@@ -579,6 +584,9 @@ 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"
|
||||
@@ -676,11 +684,12 @@ change_renpy_executable()
|
||||
dlc=p["dlc"])
|
||||
|
||||
if self.build_update and p["update"]:
|
||||
self.make_package(
|
||||
p["name"],
|
||||
"update",
|
||||
p["file_lists"],
|
||||
dlc=p["dlc"])
|
||||
for update_format in self.list_update_formats():
|
||||
self.make_package(
|
||||
p["name"],
|
||||
update_format,
|
||||
p["file_lists"],
|
||||
dlc=p["dlc"])
|
||||
|
||||
wait_parallel_threads()
|
||||
|
||||
@@ -699,6 +708,23 @@ 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
|
||||
@@ -723,29 +749,37 @@ change_renpy_executable()
|
||||
is_dir = os.path.isdir(path)
|
||||
|
||||
if is_dir:
|
||||
match_name = name + "/"
|
||||
match_names = [ name + "/", name ]
|
||||
else:
|
||||
match_name = name
|
||||
match_names = [ name ]
|
||||
|
||||
for pattern, file_list in patterns:
|
||||
|
||||
if match(match_name, pattern):
|
||||
matched = False
|
||||
|
||||
# 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
|
||||
for match_name in match_names:
|
||||
|
||||
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_name), "doesn't match anything.", file=self.log)
|
||||
print(str(match_names[0]), "doesn't match anything.", file=self.log)
|
||||
|
||||
pattern = None
|
||||
file_list = None
|
||||
|
||||
print(str(match_name), "matches", str(pattern), "(" + str(file_list) + ").", file=self.log)
|
||||
print(str(match_names[0]), "matches", str(pattern), "(" + str(file_list) + ").", file=self.log)
|
||||
|
||||
if file_list is None:
|
||||
return
|
||||
@@ -1412,6 +1446,7 @@ 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),
|
||||
@@ -1461,7 +1496,7 @@ change_renpy_executable()
|
||||
|
||||
update_fn = os.path.join(self.destination, filename + ".update.json")
|
||||
|
||||
if self.include_update and (variant not in [ 'ios', 'android', 'source']) and (not format.startswith("app-")):
|
||||
if self.include_update and not format.startswith("app-"):
|
||||
|
||||
with open(update_fn, "wb" if PY2 else "w") as f:
|
||||
json.dump(update, f, indent=2)
|
||||
@@ -1470,6 +1505,9 @@ 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)
|
||||
@@ -1480,6 +1518,10 @@ 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:
|
||||
@@ -1516,6 +1558,8 @@ 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)
|
||||
@@ -1552,13 +1596,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)
|
||||
|
||||
pkg.close()
|
||||
def close_progress(done, total):
|
||||
self.reporter.progress(_("Finishing the [variant] [format] package."), done, total, variant=variant, format=format)
|
||||
|
||||
pkg.close(close_progress)
|
||||
|
||||
if done is not None:
|
||||
done()
|
||||
@@ -1579,33 +1623,78 @@ 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,
|
||||
"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",
|
||||
}
|
||||
"renpy_version" : renpy.version_only,
|
||||
}
|
||||
|
||||
fn = renpy.fsencode(os.path.join(self.destination, self.base_name + "-" + variant + ".update"))
|
||||
if "update" in self.build["update_formats"]:
|
||||
|
||||
if os.path.exists(fn):
|
||||
os.unlink(fn)
|
||||
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
|
||||
|
||||
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:
|
||||
json.dump(index, f, indent=2)
|
||||
f.write(update_data)
|
||||
|
||||
# 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']:
|
||||
|
||||
@@ -267,7 +267,23 @@ 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
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ init 1 python in editor:
|
||||
elif renpy.arch == "armv7l":
|
||||
arch = "arm"
|
||||
else:
|
||||
arch = "x86_64"
|
||||
arch = "x64"
|
||||
|
||||
installed = os.path.exists(os.path.join(config.renpy_base, "vscode/VSCode-linux-" + arch))
|
||||
|
||||
|
||||
@@ -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 ], wait=True)
|
||||
project.current.launch([ 'lint', lint_fn, ] + list(persistent.lint_options), wait=True)
|
||||
|
||||
e = renpy.editor.editor
|
||||
e.begin(True)
|
||||
|
||||
@@ -81,7 +81,6 @@ init python:
|
||||
|
||||
label install_live2d:
|
||||
python hide:
|
||||
|
||||
if PY2:
|
||||
_prefix = r"lib/py2-"
|
||||
else:
|
||||
@@ -98,6 +97,11 @@ 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
|
||||
|
||||
@@ -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=None, pause=pause, show_screen=True, **kwargs)
|
||||
common(title, store.INTERACTION_COLOR, message, submessage=submessage, pause=pause, show_screen=True, **kwargs)
|
||||
renpy.pause(pause)
|
||||
|
||||
def processing(message, submessage=None, complete=None, total=None, **kwargs):
|
||||
|
||||
@@ -62,6 +62,9 @@ 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:
|
||||
|
||||
|
||||
@@ -242,10 +242,12 @@ 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)
|
||||
|
||||
@@ -33,7 +33,6 @@ 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
|
||||
@@ -195,7 +194,7 @@ init python in distribute:
|
||||
|
||||
self.zipfile.write_with_info(zi, path)
|
||||
|
||||
def close(self):
|
||||
def close(self, progress=None):
|
||||
self.zipfile.close()
|
||||
|
||||
|
||||
@@ -207,7 +206,7 @@ init python in distribute:
|
||||
If true, times will be forced to the epoch.
|
||||
"""
|
||||
|
||||
self.tarfile = tarfile.open(filename, mode)
|
||||
self.tarfile = tarfile.open(filename, mode, format=tarfile.PAX_FORMAT)
|
||||
self.tarfile.dereference = True
|
||||
self.notime = notime
|
||||
|
||||
@@ -243,9 +242,10 @@ init python in distribute:
|
||||
def add_directory(self, name, path):
|
||||
self.add_file(name, path, True)
|
||||
|
||||
def close(self):
|
||||
def close(self, progress=None):
|
||||
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):
|
||||
def close(self, progress=None):
|
||||
TarPackage.close(self)
|
||||
|
||||
cmd = [
|
||||
@@ -283,7 +283,6 @@ init python in distribute:
|
||||
sums.write(struct.pack("<I", zlib.adler32(data) & 0xffffffff))
|
||||
|
||||
|
||||
|
||||
class DirectoryPackage(object):
|
||||
|
||||
def mkdir(self, path):
|
||||
@@ -315,9 +314,10 @@ init python in distribute:
|
||||
fn = os.path.join(self.path, name)
|
||||
self.mkdir(fn)
|
||||
|
||||
def close(self):
|
||||
def close(self, progress=None):
|
||||
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):
|
||||
def close(self, progress=None):
|
||||
self.dp.close()
|
||||
|
||||
if os.path.exists(self.path):
|
||||
@@ -350,17 +350,40 @@ 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):
|
||||
def close(self, progress=None):
|
||||
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):
|
||||
@@ -378,7 +401,7 @@ init python in distribute:
|
||||
def add_directory(self, name, path):
|
||||
self.worklist.append((True, name, path, True))
|
||||
|
||||
def close(self):
|
||||
def close(self, progress=None):
|
||||
t = threading.Thread(target=self.run)
|
||||
t.start()
|
||||
|
||||
|
||||
@@ -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,6 +64,9 @@ 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
|
||||
@@ -71,6 +74,14 @@ 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():
|
||||
|
||||
@@ -98,17 +109,12 @@ screen preferences():
|
||||
|
||||
has vbox
|
||||
|
||||
# Projects directory selection.
|
||||
add SEPARATOR2
|
||||
|
||||
add HALF_SPACER
|
||||
|
||||
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"
|
||||
|
||||
for i, l in preference_tabs.items():
|
||||
textbutton l action SetVariable("preference_tab", i) style "l_list"
|
||||
|
||||
if preference_tab == "general":
|
||||
|
||||
@@ -119,7 +125,6 @@ screen preferences():
|
||||
|
||||
has vbox
|
||||
|
||||
# Projects directory selection.
|
||||
add SEPARATOR2
|
||||
|
||||
|
||||
@@ -207,6 +212,22 @@ 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
|
||||
@@ -218,15 +239,11 @@ 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:
|
||||
@@ -236,7 +253,6 @@ screen preferences():
|
||||
|
||||
has vbox
|
||||
|
||||
# Projects directory selection.
|
||||
add SEPARATOR2
|
||||
|
||||
frame:
|
||||
@@ -276,7 +292,6 @@ screen preferences():
|
||||
|
||||
use install_preferences
|
||||
|
||||
|
||||
elif preference_tab == "actions":
|
||||
|
||||
frame:
|
||||
@@ -300,6 +315,39 @@ 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"
|
||||
|
||||
|
||||
@@ -55,12 +55,12 @@ init python in project:
|
||||
|
||||
def __init__(self, path, name=None):
|
||||
|
||||
if name is None:
|
||||
name = os.path.basename(path)
|
||||
|
||||
while path.endswith("/"):
|
||||
path = path[:-1]
|
||||
|
||||
if name is None:
|
||||
name = os.path.basename(path)
|
||||
|
||||
if not os.path.exists(path):
|
||||
raise Exception("{} does not exist.".format(path))
|
||||
|
||||
@@ -128,6 +128,7 @@ 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"]
|
||||
@@ -259,8 +260,16 @@ 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():
|
||||
@@ -761,7 +770,12 @@ init python in project:
|
||||
blurb = LAUNCH_BLURBS[persistent.blurb % len(LAUNCH_BLURBS)]
|
||||
persistent.blurb += 1
|
||||
|
||||
interface.interaction(_("Launching"), blurb, pause=2.5)
|
||||
if persistent.skip_splashscreen:
|
||||
submessage = _("Splashscreen skipped in launcher preferences.")
|
||||
else:
|
||||
submessage = None
|
||||
|
||||
interface.interaction(_("Launching"), blurb, submessage=submessage, pause=2.5)
|
||||
|
||||
|
||||
def __call__(self):
|
||||
|
||||
@@ -731,7 +731,7 @@
|
||||
|
||||
# renpy/common/_developer/developer.rpym:57
|
||||
old "Show Image Load Log (F4)"
|
||||
new "Montrer le log ne chargement des images (F4)"
|
||||
new "Afficher le log de chargement des images (F4)"
|
||||
|
||||
# renpy/common/_developer/developer.rpym:60
|
||||
old "Hide Image Load Log (F4)"
|
||||
|
||||
@@ -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 optimié pour un ratio de cadre 16:9."
|
||||
new "Personnalisé. Le GUI est optimisé pour un ratio de cadre 16:9."
|
||||
|
||||
# game/gui7.rpy:355
|
||||
old "WIDTH"
|
||||
@@ -2147,3 +2147,23 @@
|
||||
# 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."
|
||||
|
||||
@@ -228,3 +228,18 @@
|
||||
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}"
|
||||
|
||||
@@ -2147,3 +2147,6 @@
|
||||
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."
|
||||
|
||||
@@ -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 "%d %b, %H:%M"
|
||||
new "%b %d, %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. Повний список ліцензій, враховуючи посилання на повний вихідний код, можна знайти {a=https://www.renpy.org/l/license}тут{/a}."
|
||||
new "Ця програма містить вільне програмне забезпечення під низкою ліцензій, зокрема ліцензією MIT та GNU Lesser General Public License. Повний список ПЗ, включно з посиланнями на повний вихідний код, можна знайти {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 "Натисніть праву кнопку миші або ESC, щоб вийти."
|
||||
new "Натисніть ПКМ або Escape, щоб вийти."
|
||||
|
||||
# _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() провалений>"
|
||||
new "<repr() failed>"
|
||||
|
||||
# _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 Updater не підтримується на мобільних пристроях."
|
||||
new "Оновлювач Ren'Py не підтримується на мобільних пристроях."
|
||||
|
||||
# 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,22 +875,23 @@ 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"
|
||||
@@ -898,11 +899,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"
|
||||
@@ -910,15 +911,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:
|
||||
|
||||
@@ -936,31 +937,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"
|
||||
@@ -980,7 +981,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."
|
||||
@@ -988,7 +989,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."
|
||||
@@ -1016,7 +1017,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"
|
||||
@@ -1024,7 +1025,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"
|
||||
@@ -1036,7 +1037,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"
|
||||
|
||||
@@ -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 script активовано."
|
||||
new "Скрипт Ren'Py увімкено."
|
||||
|
||||
# 00console.rpy:261
|
||||
old "Ren'Py script disabled."
|
||||
new "Ren'Py script деактивовано."
|
||||
new "Скрипт Ren'Py вимкнуто."
|
||||
|
||||
# 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 script>: запуск оператора\n"
|
||||
new " <функція скрипту renpy>: запуск функції\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: Скорочене представлення об'єктів (repr) у консолі (за замовчуванням)."
|
||||
new "short: Скоротити представлення об’єктів на консолі (за стандартом)."
|
||||
|
||||
# 00console.rpy:690
|
||||
old "long: Print the full representation of objects on the console."
|
||||
new "long: Виводити повне уявлення об'єктів (repr) у консолі."
|
||||
new "long: Вивести повне представлення об’єктів на консолі."
|
||||
|
||||
# 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: виводить стек повернення (return stack)"
|
||||
new "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 <expr>: показати підпис та документацію <expr>"
|
||||
new "help: показує цю довідку\n help <вираз>: показує підпис та документацію <виразу>"
|
||||
|
||||
# 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"
|
||||
|
||||
|
||||
@@ -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 (OpenGL 2+)"
|
||||
new "NPOT"
|
||||
|
||||
# 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 за замовчуванням намагається встановити панель інструментів Bing. Якщо ви цього не хочете, зніміть відповідну галочку."
|
||||
new "{b}Нотатка:{/b} Програма вебналаштування DirectX від Microsoft за стандартом встановлює панель інструментів 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 для Дискорду."
|
||||
new "Копіює файл errors.txt у буфер обміну як Markdown для Discord."
|
||||
|
||||
# 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 "Натисніть або посуньте [kind] '[control!s]'"
|
||||
new "Натисніть або рухніть '[control!s]' [kind]."
|
||||
|
||||
# renpy/common/00gltest.rpy:136
|
||||
old "Enable (No Blocklist)"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,15 +11,15 @@ translate ukrainian strings:
|
||||
|
||||
# gui/game/screens.rpy:85
|
||||
old "## Say screen"
|
||||
new "## Екран розмови"
|
||||
new "## Екран Say"
|
||||
|
||||
# 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 може мати значення None, якщо ім’я не вказано.)"
|
||||
new "## Екран Say використовується для відображення діалогу гравцеві. Він приймає два параметри, who і what, ім’я персонажа, що говорить, і текст, який буде відображатися відповідно. (Параметр 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 і в Веб."
|
||||
new "## Кнопка виходу заборонена на iOS і непотрібна на Android і в Web."
|
||||
|
||||
# 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}A"
|
||||
new "{#auto_page}А"
|
||||
|
||||
# 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 "## діапазон(1, 10) дає числа від 1 до 9."
|
||||
new "## range(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 "## Взяти колір тексту що від персонажа, якщо встановлено."
|
||||
new "## Бере колір тексту who з Character, якщо він заданий."
|
||||
|
||||
# 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 "D-Pad, Стіки"
|
||||
new "Хрестовина, Стики"
|
||||
|
||||
# gui/game/screens.rpy:1082
|
||||
old "Start, Guide"
|
||||
new "Старт, Гайд"
|
||||
new "Start, Guide"
|
||||
|
||||
# gui/game/screens.rpy:1086
|
||||
old "Y/Top Button"
|
||||
new "Кнопка Y/Top"
|
||||
new "Y/Верхня кнопка"
|
||||
|
||||
# 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 "## Клацніть правою кнопкою миші та вийдіть із відповіді \"no\"."
|
||||
new "## Клацніть ПКМ та вийдіть із відповіді «Ні»."
|
||||
|
||||
# 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 "## Відображає діалог у vpgrid або vbox."
|
||||
new "## Цей екран використовується для діалогу та меню режиму NVL."
|
||||
|
||||
# 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,23 +658,19 @@ 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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)]
|
||||
$ 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))]
|
||||
|
||||
if c["channel"].startswith("Release"):
|
||||
$ current = _("• {a=https://www.renpy.org/doc/html/changelog.html}View change log{/a}")
|
||||
@@ -132,15 +132,14 @@ 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] • [c[pretty_version]] [current!t]" style "l_small_text"
|
||||
text "[date] • [pretty] [current!t]" style "l_small_text"
|
||||
|
||||
add HALF_SPACER
|
||||
|
||||
|
||||
@@ -228,11 +228,11 @@ init python:
|
||||
return f_rule
|
||||
return False
|
||||
|
||||
def generate_pwa_icons(p, destination):
|
||||
def generate_web_icons(p, destination):
|
||||
"""
|
||||
Checks if the pwa_icon.png file exists in the game folder and generates
|
||||
Checks if the web-icon.png file exists in the game folder and generates
|
||||
required icons for PWA in subdirectory icons/ in the destination folder.
|
||||
If no pwa_icon.png is found, the default Ren'Py icon is used instead in
|
||||
If no web-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", "pwa_icon.png" }:
|
||||
if fn in { "game.zip", "hash.txt", "index.html", "web-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_pwa_icons(p, destination)
|
||||
generate_web_icons(p, destination)
|
||||
prepare_pwa_files(p, destination)
|
||||
|
||||
# Zip up the game.
|
||||
|
||||
@@ -41,3 +41,4 @@ 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
Executable
+214
@@ -0,0 +1,214 @@
|
||||
#!/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()
|
||||
@@ -134,7 +134,7 @@ style_properties = sorted_dict(
|
||||
alt=None,
|
||||
altruby_style=None,
|
||||
antialias=None,
|
||||
vertical=None,
|
||||
axis=None,
|
||||
background='renpy.easy.displayable_or_none',
|
||||
bar_invert=None,
|
||||
bar_resizing=None,
|
||||
@@ -155,6 +155,7 @@ 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,
|
||||
@@ -168,6 +169,7 @@ style_properties = sorted_dict(
|
||||
hover_sound=None,
|
||||
hyperlink_functions=None,
|
||||
italic=None,
|
||||
instance=None,
|
||||
justify=None,
|
||||
kerning=None,
|
||||
key_events=None,
|
||||
@@ -187,10 +189,12 @@ 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,
|
||||
@@ -209,6 +213,7 @@ style_properties = sorted_dict(
|
||||
top_margin=None,
|
||||
top_padding=None,
|
||||
underline=None,
|
||||
vertical=None,
|
||||
xanchor='expand_anchor',
|
||||
xfill=None,
|
||||
xfit=None,
|
||||
|
||||
@@ -88,6 +88,7 @@ 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
|
||||
|
||||
@@ -394,6 +395,13 @@ 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)
|
||||
@@ -515,7 +523,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 Rothame.
|
||||
# Additions by Tom Rothamel.
|
||||
|
||||
cdef struct FT_MemoryRec_
|
||||
ctypedef FT_MemoryRec_ *FT_Memory
|
||||
@@ -605,3 +613,42 @@ 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 )
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#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)
|
||||
|
||||
@@ -86,6 +86,7 @@ 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")
|
||||
@@ -197,6 +198,13 @@ 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()
|
||||
|
||||
|
||||
@@ -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.realpath(sys.argv[0]))
|
||||
renpy_base = os.path.dirname(os.path.abspath(__file__))
|
||||
renpy_base = os.path.abspath(renpy_base)
|
||||
|
||||
return renpy_base
|
||||
|
||||
+3
-7
@@ -278,9 +278,6 @@ 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
|
||||
@@ -571,8 +568,7 @@ def import_all():
|
||||
|
||||
global backup
|
||||
|
||||
if not mobile:
|
||||
backup = Backup()
|
||||
backup = Backup()
|
||||
|
||||
post_import()
|
||||
|
||||
@@ -618,8 +614,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
|
||||
|
||||
|
||||
+31
-131
@@ -242,24 +242,11 @@ 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")
|
||||
@@ -285,28 +272,6 @@ 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
|
||||
@@ -447,7 +412,12 @@ class ATLTransformBase(renpy.object.Object):
|
||||
# a way that would affect the execution of the ATL.
|
||||
|
||||
if t.atl.constant != GLOBAL_CONST:
|
||||
if not self.context.variables_equal(t.context, t.atl.find_loaded_variables()):
|
||||
|
||||
block = self.get_block()
|
||||
if block is None:
|
||||
block = self.compile()
|
||||
|
||||
if not deep_compare(self.block, t.block):
|
||||
return
|
||||
|
||||
self.done = t.done
|
||||
@@ -597,9 +567,6 @@ class ATLTransformBase(renpy.object.Object):
|
||||
|
||||
def execute(self, trans, st, at):
|
||||
|
||||
if self.state.debug:
|
||||
print("A", st)
|
||||
|
||||
if self.done:
|
||||
return None
|
||||
|
||||
@@ -704,12 +671,6 @@ 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.
|
||||
|
||||
@@ -768,10 +729,6 @@ 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):
|
||||
|
||||
@@ -834,20 +791,6 @@ 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):
|
||||
@@ -1186,26 +1129,6 @@ 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:
|
||||
@@ -1241,9 +1164,6 @@ 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):
|
||||
@@ -1278,14 +1198,6 @@ 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):
|
||||
@@ -1595,9 +1507,6 @@ 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):
|
||||
@@ -1635,13 +1544,6 @@ 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):
|
||||
@@ -1723,15 +1625,6 @@ 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):
|
||||
|
||||
@@ -1801,9 +1694,6 @@ 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):
|
||||
|
||||
@@ -1851,15 +1741,6 @@ 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):
|
||||
@@ -1968,9 +1849,6 @@ class RawEvent(RawStatement):
|
||||
def mark_constant(self, analysis):
|
||||
self.constant = GLOBAL_CONST
|
||||
|
||||
def find_loaded_variables(self):
|
||||
return set()
|
||||
|
||||
|
||||
class Event(Statement):
|
||||
|
||||
@@ -1997,9 +1875,6 @@ 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):
|
||||
|
||||
@@ -2311,3 +2186,28 @@ 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
|
||||
|
||||
+16
-14
@@ -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, relative_volume=1.0):
|
||||
def enqueue(self, filenames, loop=True, synchro_start=False, fadein=0, tight=None, loop_only=False, relative_volume=1.0):
|
||||
|
||||
with lock:
|
||||
|
||||
@@ -676,20 +676,22 @@ class Channel(object):
|
||||
filename, _, _ = self.split_filename(filename, False)
|
||||
renpy.game.persistent._seen_audio[str(filename)] = True # type: ignore
|
||||
|
||||
if tight is None:
|
||||
tight = self.tight
|
||||
if not loop_only:
|
||||
|
||||
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 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
|
||||
|
||||
if loop:
|
||||
self.loop = list(filenames)
|
||||
|
||||
@@ -116,11 +116,13 @@ 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
|
||||
@@ -128,7 +130,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, relative_volume=relative_volume)
|
||||
c.enqueue(filenames, loop=loop, synchro_start=synchro_start, fadein=fadein, tight=tight, loop_only=loop_only, relative_volume=relative_volume)
|
||||
|
||||
t = get_serial()
|
||||
ctx.last_changed = t
|
||||
|
||||
+79
-1
@@ -121,11 +121,82 @@ def mac_start(fn):
|
||||
|
||||
def popen_del(self, *args, **kwargs):
|
||||
"""
|
||||
Fix an issue where the __del__ method of popen doesn't wor,
|
||||
Fix an issue where the __del__ method of popen doesn't work.
|
||||
"""
|
||||
|
||||
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
|
||||
@@ -189,6 +260,13 @@ 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"):
|
||||
|
||||
@@ -492,6 +492,9 @@ 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.
|
||||
|
||||
@@ -196,7 +196,7 @@ init -1500 python:
|
||||
:func:`renpy.show_screen` function.
|
||||
"""
|
||||
|
||||
return Show(screen, transition, _transient=True, *args, **kwargs)
|
||||
return Show(screen, transition, *args, _transient=True, **kwargs)
|
||||
|
||||
@renpy.pure
|
||||
class Hide(Action, DictEquality):
|
||||
|
||||
+222
-274
@@ -21,10 +21,7 @@
|
||||
|
||||
init -1600 python:
|
||||
|
||||
##########################################################################
|
||||
# Functions that set variables or fields.
|
||||
|
||||
__FieldNotFound = object()
|
||||
__Sentinel = object()
|
||||
|
||||
def _get_field(obj, name, kind):
|
||||
|
||||
@@ -34,372 +31,323 @@ init -1600 python:
|
||||
rv = obj
|
||||
|
||||
for i in name.split("."):
|
||||
rv = getattr(rv, i, __FieldNotFound)
|
||||
if rv is __FieldNotFound:
|
||||
raise NameError("The {} {} does not exist.".format(kind, name))
|
||||
rv = getattr(rv, i, __Sentinel)
|
||||
if rv is __Sentinel:
|
||||
raise Exception("The {!r} {} does not exist.".format(name, kind))
|
||||
|
||||
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 NameError("The {} {} does not exist.".format(kind, name))
|
||||
raise Exception("The {!r} {} cannot be set.".format(name, kind))
|
||||
|
||||
@renpy.pure
|
||||
class SetField(Action, FieldEquality):
|
||||
|
||||
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):
|
||||
"""
|
||||
: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.
|
||||
An Accessor mixin class for Actions setting a field on an object.
|
||||
"""
|
||||
|
||||
identity_fields = [ "object" ]
|
||||
equality_fields = [ "field", "value" ]
|
||||
|
||||
identity_fields = ("object",)
|
||||
equality_fields = ("field",)
|
||||
kind = "field"
|
||||
|
||||
def __init__(self, object, field, value, kind="field"):
|
||||
def __init__(self, object, field, *args, **kwargs):
|
||||
super(Field, self).__init__(*args, **kwargs)
|
||||
self.object = object
|
||||
self.field = field
|
||||
self.value = value
|
||||
self.kind = kind
|
||||
|
||||
def __call__(self):
|
||||
_set_field(self.object, self.field, self.value, self.kind)
|
||||
_set_field(self.object, self.field, self.value_to_set(), self.kind)
|
||||
renpy.restart_interaction()
|
||||
|
||||
def get_selected(self):
|
||||
return _get_field(self.object, self.field, self.kind) == self.value
|
||||
def current_value(self):
|
||||
return _get_field(self.object, self.field, self.kind)
|
||||
|
||||
@renpy.pure
|
||||
def SetVariable(name, value):
|
||||
class Variable(Field):
|
||||
"""
|
||||
: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".
|
||||
An Accessor mixin class for Actions setting a global variable.
|
||||
"""
|
||||
|
||||
return SetField(store, name, value, kind="variable")
|
||||
kind = "global variable"
|
||||
|
||||
@renpy.pure
|
||||
class SetDict(Action, FieldEquality):
|
||||
def __init__(self, name, *args, **kwargs):
|
||||
super(Variable, self).__init__(store, name, *args, **kwargs)
|
||||
|
||||
class Dict(Accessor):
|
||||
"""
|
||||
: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.
|
||||
An Accessor mixin class for Actions setting an index on a sequence or a key on a mapping.
|
||||
"""
|
||||
|
||||
identity_fields = [ "dict" ]
|
||||
equality_fields = [ "key", "value" ]
|
||||
identity_fields = ("dict",)
|
||||
equality_fields = ("key",)
|
||||
kind = "key or index"
|
||||
|
||||
def __init__(self, dict, key, value):
|
||||
def __init__(self, dict, key, *args, **kwargs):
|
||||
super(Dict, self).__init__(*args, **kwargs)
|
||||
self.dict = dict
|
||||
self.key = key
|
||||
self.value = value
|
||||
|
||||
def __call__(self):
|
||||
self.dict[self.key] = self.value
|
||||
self.dict[self.key] = self.value_to_set()
|
||||
renpy.restart_interaction()
|
||||
|
||||
def get_selected(self):
|
||||
if self.key not in self.dict:
|
||||
return False
|
||||
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
|
||||
|
||||
return self.dict[self.key] == self.value
|
||||
|
||||
|
||||
@renpy.pure
|
||||
class SetScreenVariable(Action, FieldEquality):
|
||||
class ScreenVariable(Accessor):
|
||||
"""
|
||||
: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.
|
||||
An Accessor mixin class for Actions setting a screen variable.
|
||||
"""
|
||||
|
||||
identity_fields = [ "value" ]
|
||||
equality_fields = [ "name" ]
|
||||
identity_fields = ()
|
||||
equality_fields = ("name",)
|
||||
kind = "screen variable"
|
||||
|
||||
def __init__(self, name, value):
|
||||
def __init__(self, name, *args, **kwargs):
|
||||
super(ScreenVariable, self).__init__(*args, **kwargs)
|
||||
self.name = name
|
||||
self.value = value
|
||||
|
||||
def __call__(self):
|
||||
|
||||
cs = renpy.current_screen()
|
||||
|
||||
if cs is None:
|
||||
return
|
||||
|
||||
cs.scope[self.name] = self.value
|
||||
cs.scope[self.name] = self.value_to_set()
|
||||
renpy.restart_interaction()
|
||||
|
||||
def get_selected(self):
|
||||
|
||||
def current_value(self):
|
||||
cs = renpy.current_screen()
|
||||
|
||||
if cs is None:
|
||||
return False
|
||||
raise Exception("No current screen.")
|
||||
|
||||
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)
|
||||
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
|
||||
|
||||
|
||||
@renpy.pure
|
||||
def ToggleVariable(variable, true_value=None, false_value=None):
|
||||
class LocalVariable(Dict):
|
||||
"""
|
||||
: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.
|
||||
An Accessor mixin class for Actions setting a local variable.
|
||||
"""
|
||||
|
||||
return ToggleField(store, variable, true_value=true_value, false_value=false_value, kind="variable")
|
||||
kind = "local variable"
|
||||
|
||||
def __init__(self, name, *args, **kwargs):
|
||||
super(LocalVariable, self).__init__(sys._getframe(1).f_locals,
|
||||
name,
|
||||
*args, **kwargs)
|
||||
|
||||
@renpy.pure
|
||||
class ToggleDict(Action, FieldEquality):
|
||||
# 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):
|
||||
"""
|
||||
: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.
|
||||
A Manager mixin class for Actions setting a single value
|
||||
"""
|
||||
|
||||
identity_fields = [ "dict", ]
|
||||
equality_fields = [ "key", "true_value", "false_value" ]
|
||||
identity_fields = ()
|
||||
equality_fields = ("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 __init__(self, value, *args, **kwargs):
|
||||
super(Set, self).__init__(*args, **kwargs)
|
||||
self.value = 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 value_to_set(self):
|
||||
return self.value
|
||||
|
||||
def get_selected(self):
|
||||
try:
|
||||
rv = self.dict[self.key]
|
||||
except (KeyError, IndexError):
|
||||
val = self.current_value()
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
if self.true_value is not None:
|
||||
rv = (rv == self.true_value)
|
||||
return val == self.value
|
||||
|
||||
return rv
|
||||
|
||||
# Not pure.
|
||||
def ToggleLocalVariable(name, true_value=None, false_value=None):
|
||||
class Toggle(Manager):
|
||||
"""
|
||||
: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.
|
||||
A Manager mixin class for Actions toggling between two values.
|
||||
"""
|
||||
|
||||
return ToggleDict(sys._getframe(1).f_locals, name, true_value=true_value, false_value=false_value)
|
||||
identity_fields = ()
|
||||
equality_fields = ("true_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
|
||||
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 __call__(self):
|
||||
cs = renpy.current_screen()
|
||||
def value_to_set(self):
|
||||
value = self.current_value()
|
||||
|
||||
if cs is None:
|
||||
return
|
||||
|
||||
value = cs.scope[self.name]
|
||||
|
||||
if self.true_value is not None:
|
||||
value = (value == self.true_value)
|
||||
tv = self.true_value
|
||||
if tv is not None:
|
||||
value = (value == tv)
|
||||
|
||||
value = not value
|
||||
|
||||
if self.true_value is not None:
|
||||
if tv is not None:
|
||||
if value:
|
||||
value = self.true_value
|
||||
return tv
|
||||
else:
|
||||
value = self.false_value
|
||||
return self.false_value
|
||||
|
||||
cs.scope[self.name] = value
|
||||
renpy.restart_interaction()
|
||||
return value
|
||||
|
||||
def get_selected(self):
|
||||
cs = renpy.current_screen()
|
||||
|
||||
if cs is None:
|
||||
try:
|
||||
val = self.current_value()
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
if self.name not in cs.scope:
|
||||
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:
|
||||
return False
|
||||
|
||||
rv = cs.scope[self.name]
|
||||
if self.loop:
|
||||
return True
|
||||
|
||||
if self.true_value is not None:
|
||||
rv = (rv == self.true_value)
|
||||
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
|
||||
|
||||
|
||||
return rv
|
||||
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:
|
||||
|
||||
@renpy.pure
|
||||
class AddToSet(Action, FieldEquality):
|
||||
|
||||
@@ -291,6 +291,23 @@ 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))
|
||||
|
||||
@@ -36,13 +36,15 @@ 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. If the screen doesn't exist, then "_screen"
|
||||
is appended to it, and that label is jumped to.
|
||||
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`.
|
||||
|
||||
If the optional keyword argument `_transition` is given, the
|
||||
menu will change screens using the provided transition.
|
||||
@@ -60,7 +62,7 @@ init -1500 python:
|
||||
* ShowMenu("stats")
|
||||
|
||||
ShowMenu without an argument will enter the game menu at the
|
||||
default screen, taken from _game_menu_screen.
|
||||
default screen, taken from :var:`_game_menu_screen`.
|
||||
|
||||
Extra arguments and keyword arguments are passed on to the screen
|
||||
"""
|
||||
@@ -97,7 +99,7 @@ init -1500 python:
|
||||
if renpy.has_screen(screen):
|
||||
|
||||
renpy.transition(transition)
|
||||
renpy.show_screen(screen, _transient=True, *self.args, **self.kwargs)
|
||||
renpy.show_screen(screen, *self.args, _transient=True, **self.kwargs)
|
||||
renpy.restart_interaction()
|
||||
|
||||
elif renpy.has_label(screen):
|
||||
|
||||
@@ -73,6 +73,7 @@ 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
|
||||
@@ -106,6 +107,7 @@ 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
|
||||
@@ -607,7 +609,7 @@ init -1500 python:
|
||||
The sensitivity and selectedness of this action match those
|
||||
of the `yes` action.
|
||||
|
||||
See :func:`layout.yesno_screen` for a function version of this action.
|
||||
See :func:`renpy.confirm` for a function version of this action.
|
||||
"""
|
||||
|
||||
|
||||
@@ -874,6 +876,56 @@ 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:
|
||||
|
||||
+152
-216
@@ -112,59 +112,26 @@ init -1500 python:
|
||||
self.old_value = other.value
|
||||
self.start_time = None
|
||||
|
||||
@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.
|
||||
"""
|
||||
|
||||
class __GenericValue(BarValue, FieldEquality):
|
||||
# pickle defaults
|
||||
offset = 0
|
||||
action = None
|
||||
force_step = False
|
||||
|
||||
identity_fields = [ 'dict' ]
|
||||
equality_fields = [ 'key', 'range', 'max_is_zero', 'style', 'offset', 'step', 'action', 'force_step' ]
|
||||
identity_fields = ()
|
||||
equality_fields = ('range', 'max_is_zero', 'style', 'offset', 'step', 'action', 'force_step')
|
||||
|
||||
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
|
||||
def __init__(self, range, max_is_zero=False, style="bar", offset=0, step=None, action=None, force_step=False):
|
||||
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
|
||||
|
||||
@@ -178,14 +145,13 @@ init -1500 python:
|
||||
|
||||
value += self.offset
|
||||
|
||||
self.dict[self.key] = value
|
||||
self.set_value(value)
|
||||
renpy.restart_interaction()
|
||||
|
||||
return renpy.run(self.action)
|
||||
|
||||
def get_adjustment(self):
|
||||
|
||||
value = self.dict[self.key]
|
||||
value = self.get_value()
|
||||
|
||||
value -= self.offset
|
||||
|
||||
@@ -207,10 +173,46 @@ init -1500 python:
|
||||
return self.style, "v" + self.style
|
||||
|
||||
@renpy.pure
|
||||
class FieldValue(BarValue, FieldEquality):
|
||||
class DictValue(__GenericValue):
|
||||
"""
|
||||
:doc: value
|
||||
:args: (self, object, field, range, max_is_zero=False, style="bar", offset=0, step=None, action=None, force_step=False)
|
||||
: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}
|
||||
|
||||
A bar value that allows the user to adjust the value of a field
|
||||
on an object.
|
||||
@@ -218,223 +220,157 @@ init -1500 python:
|
||||
`object`
|
||||
The object.
|
||||
`field`
|
||||
The field, a string.
|
||||
`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.
|
||||
The field name, a string.
|
||||
"""
|
||||
|
||||
offset = 0
|
||||
action = None
|
||||
force_step = False
|
||||
kind = "field"
|
||||
|
||||
identity_fields = [ 'object', ]
|
||||
equality_fields = [ 'range', 'max_is_zero', 'style', 'offset', 'step', 'action', 'force_step', 'field' ]
|
||||
identity_fields = ('object',)
|
||||
equality_fields = __GenericValue.equality_fields + ("field",)
|
||||
|
||||
def __init__(self, object, field, range, max_is_zero=False, style="bar", offset=0, step=None, action=None, force_step=False, kind="field"):
|
||||
def __init__(self, object, field, *args, **kwargs):
|
||||
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
|
||||
super(FieldValue, self).__init__(*args, **kwargs)
|
||||
|
||||
if step is None:
|
||||
if isinstance(range, float):
|
||||
step = range / 10.0
|
||||
else:
|
||||
step = max(range / 10, 1)
|
||||
def get_value(self):
|
||||
value = _get_field(self.object, self.field, self.kind)
|
||||
|
||||
self.step = step
|
||||
self.action = action
|
||||
return value
|
||||
|
||||
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
|
||||
def set_value(self, value):
|
||||
_set_field(self.object, self.field, value, self.kind)
|
||||
|
||||
@renpy.pure
|
||||
def VariableValue(variable, range, max_is_zero=False, style="bar", offset=0, step=None, action=None, force_step=False):
|
||||
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.
|
||||
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.
|
||||
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".
|
||||
"""
|
||||
|
||||
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")
|
||||
kind = "variable"
|
||||
|
||||
def __init__(self, variable, *args, **kwargs):
|
||||
super(VariableValue, self).__init__(store, variable, *args, **kwargs)
|
||||
|
||||
@renpy.pure
|
||||
class ScreenVariableValue(BarValue, FieldEquality):
|
||||
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("""
|
||||
`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.
|
||||
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.
|
||||
|
||||
This is used internally, for some preferences.
|
||||
`style`
|
||||
The styles of the bar created.
|
||||
The styles of the created bar.
|
||||
`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.
|
||||
"""
|
||||
If not None, an action to call when the {kind}'s value is changed.
|
||||
""")
|
||||
|
||||
action = None
|
||||
offset = 0
|
||||
force_step = False
|
||||
for value in (DictValue, FieldValue, VariableValue, ScreenVariableValue, LocalVariableValue):
|
||||
docstr = inspect.cleandoc(value.__doc__)
|
||||
|
||||
identity_fields = [ ]
|
||||
equality_fields = [ 'variable', 'max_is_zero', 'style', 'offset', 'step', 'action', 'force_step' ]
|
||||
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)
|
||||
|
||||
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
|
||||
params.extend(generic_params)
|
||||
|
||||
if step is None:
|
||||
if isinstance(range, float):
|
||||
step = range / 10.0
|
||||
else:
|
||||
step = max(range / 10, 1)
|
||||
value.__doc__ = (docstr+"\n"+suffix).format(
|
||||
args=inspect.Signature(parameters=params),
|
||||
kind=value.kind)
|
||||
|
||||
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
|
||||
init -1500 python:
|
||||
|
||||
@renpy.pure
|
||||
class MixerValue(BarValue, DictEquality):
|
||||
|
||||
@@ -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,6 +159,7 @@ init -1500 python in build:
|
||||
("*.dll", None),
|
||||
("*.manifest", None),
|
||||
("*.keystore", None),
|
||||
("update.pem", None),
|
||||
|
||||
("lib/", None),
|
||||
("renpy/", None),
|
||||
@@ -207,6 +208,7 @@ 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),
|
||||
@@ -506,6 +508,9 @@ 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():
|
||||
@@ -589,6 +594,8 @@ init -1500 python in build:
|
||||
|
||||
rv["_sdk_fonts"] = _sdk_fonts
|
||||
|
||||
rv["update_formats"] = update_formats
|
||||
|
||||
return rv
|
||||
|
||||
init 1500 python in build:
|
||||
|
||||
@@ -232,7 +232,6 @@ 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
|
||||
@@ -292,9 +291,14 @@ 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.
|
||||
@@ -406,7 +410,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")
|
||||
|
||||
@@ -593,7 +593,7 @@ init -1500 python in _console:
|
||||
else:
|
||||
break
|
||||
|
||||
if s.rstrip().endswith(":"):
|
||||
if s.partition("#")[0].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:
|
||||
if indent or line.startswith("@") or line.endswith("\\"):
|
||||
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:
|
||||
if persistent._console_short and not getattr(result, "_console_always_long", False):
|
||||
he.result = aRepr.repr(result)
|
||||
else:
|
||||
he.result = repr(result)
|
||||
@@ -1103,21 +1103,25 @@ screen _console:
|
||||
|
||||
has vbox
|
||||
|
||||
$ last_line = ""
|
||||
|
||||
for line in lines:
|
||||
hbox:
|
||||
spacing 4
|
||||
|
||||
if line[:1] != " ":
|
||||
if (line[:1] != " ") and (last_line[:1] != "@") and (last_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] != " ":
|
||||
if (default[:1] != " ") and (last_line[:1] != "@") and (last_line[-1:] != "\\"):
|
||||
text "> " style "_console_prompt"
|
||||
else:
|
||||
text "... " style "_console_prompt"
|
||||
|
||||
@@ -280,6 +280,10 @@ init python in director:
|
||||
|
||||
return show_director
|
||||
|
||||
def after_load():
|
||||
state.mode = "lines"
|
||||
|
||||
config.after_load_callbacks.append(after_load)
|
||||
|
||||
def interact():
|
||||
"""
|
||||
@@ -1766,6 +1770,7 @@ 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)
|
||||
|
||||
@@ -1786,6 +1791,8 @@ 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)
|
||||
@@ -1806,6 +1813,11 @@ 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)
|
||||
|
||||
|
||||
@@ -1824,6 +1836,11 @@ 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)
|
||||
|
||||
|
||||
|
||||
@@ -99,11 +99,15 @@ 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
|
||||
|
||||
@@ -257,6 +257,9 @@ init -1200 python:
|
||||
is true.
|
||||
"""
|
||||
|
||||
if developer and config.developer:
|
||||
return True
|
||||
|
||||
return renpy.display.controller.exists()
|
||||
|
||||
|
||||
|
||||
@@ -568,6 +568,8 @@ 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
|
||||
|
||||
+143
-90
@@ -40,12 +40,12 @@ init -1510 python:
|
||||
|
||||
elif self.action == "disable":
|
||||
|
||||
if current is self.input_value:
|
||||
if current == self.input_value:
|
||||
renpy.set_editable_input_value(self.input_value, False)
|
||||
|
||||
elif self.action == "toggle":
|
||||
|
||||
if current is self.input_value and editable:
|
||||
if current == 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 is self.input_value) and editable
|
||||
rv = (current == self.input_value) and editable
|
||||
|
||||
if self.action == "disable":
|
||||
rv = not rv
|
||||
@@ -79,22 +79,27 @@ 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 Exception("Not implemented.")
|
||||
raise NotImplementedError
|
||||
|
||||
def set_text(self, s):
|
||||
raise Exception("Not implemented.")
|
||||
raise NotImplementedError
|
||||
|
||||
def enter(self):
|
||||
if self.returnable:
|
||||
return self.get_text()
|
||||
else:
|
||||
return None
|
||||
elif self.disable_on_enter:
|
||||
renpy.run(self.Disable())
|
||||
raise renpy.IgnoreEvent
|
||||
|
||||
def Enable(self):
|
||||
if self.editable:
|
||||
@@ -114,70 +119,41 @@ init -1510 python:
|
||||
else:
|
||||
return None
|
||||
|
||||
@renpy.pure
|
||||
class VariableInputValue(InputValue, FieldEquality):
|
||||
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):
|
||||
"""
|
||||
:doc: input_value
|
||||
:args: {args}
|
||||
|
||||
An input value that updates `variable`.
|
||||
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.
|
||||
|
||||
`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 = [ ]
|
||||
equality_fields = [ "variable", "returnable" ]
|
||||
identity_fields = ("screen",)
|
||||
equality_fields = __GenericInputValue.equality_fields+("variable",)
|
||||
|
||||
def __init__(self, variable, default=True, returnable=False):
|
||||
def __init__(self, variable, *args, **kwargs):
|
||||
super(ScreenVariableInputValue, self).__init__(*args, **kwargs)
|
||||
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):
|
||||
@@ -190,68 +166,145 @@ init -1510 python:
|
||||
renpy.restart_interaction()
|
||||
|
||||
@renpy.pure
|
||||
class FieldInputValue(InputValue, FieldEquality):
|
||||
class FieldInputValue(__GenericInputValue):
|
||||
"""
|
||||
: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 = [ "field", "returnable" ]
|
||||
identity_fields = ("object",)
|
||||
equality_fields = __GenericInputValue.equality_fields+("field",)
|
||||
|
||||
def __init__(self, object, field, default=True, returnable=False):
|
||||
kind = "field"
|
||||
|
||||
def __init__(self, object, field, *args, **kwargs):
|
||||
super(FieldInputValue, self).__init__(*args, **kwargs)
|
||||
self.object = object
|
||||
self.field = field
|
||||
|
||||
self.default = default
|
||||
self.returnable = returnable
|
||||
|
||||
def get_text(self):
|
||||
return _get_field(self.object, self.field, "field")
|
||||
return _get_field(self.object, self.field, self.kind)
|
||||
|
||||
def set_text(self, s):
|
||||
_set_field(self.object, self.field, s, "field")
|
||||
_set_field(self.object, self.field, s, self.kind)
|
||||
renpy.restart_interaction()
|
||||
|
||||
@renpy.pure
|
||||
class DictInputValue(InputValue, FieldEquality):
|
||||
class VariableInputValue(FieldInputValue):
|
||||
"""
|
||||
:doc: input_value
|
||||
:args: {args}
|
||||
|
||||
An input value that updates `key` in `dict`.
|
||||
An input value that updates `variable`.
|
||||
|
||||
`default`
|
||||
If true, this input can be editable by default.
|
||||
`variable`
|
||||
A string giving the name of the variable to update.
|
||||
|
||||
`returnable`
|
||||
If true, the value of this input will be returned when the
|
||||
user presses enter.
|
||||
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".
|
||||
"""
|
||||
|
||||
identity_fields = [ "dict", "key" ]
|
||||
equality_fields = [ "returnable" ]
|
||||
__version__ = 1
|
||||
|
||||
def __init__(self, dict, key, default=True, returnable=False):
|
||||
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)
|
||||
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),
|
||||
)
|
||||
|
||||
@@ -423,7 +423,7 @@ python early in layeredimage:
|
||||
args.append(None)
|
||||
args.append(Null())
|
||||
|
||||
return ConditionSwitch(predict_all=predict_all, *args)
|
||||
return ConditionSwitch(*args, predict_all=predict_all)
|
||||
|
||||
class RawConditionGroup(object):
|
||||
|
||||
@@ -883,6 +883,11 @@ 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')
|
||||
@@ -917,18 +922,23 @@ python early in layeredimage:
|
||||
|
||||
if not l.match(':'):
|
||||
l.expect_eol()
|
||||
l.expect_noblock('attribute')
|
||||
l.expect_noblock('always')
|
||||
return
|
||||
|
||||
l.expect_block('attribute')
|
||||
l.expect_block('always')
|
||||
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('attribute')
|
||||
ll.expect_noblock('always')
|
||||
|
||||
if a.image is None:
|
||||
l.error("The always statement must have a displayable.")
|
||||
@@ -954,6 +964,11 @@ 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
|
||||
@@ -987,7 +1002,12 @@ 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:
|
||||
|
||||
@@ -1078,6 +1098,11 @@ 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" ] +
|
||||
|
||||
@@ -478,23 +478,12 @@ init -1400 python hide:
|
||||
@layout
|
||||
def yesno_screen(message, yes=None, no=None):
|
||||
"""
|
||||
:doc: other
|
||||
:undocumented:
|
||||
|
||||
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.
|
||||
renpy.confirm is a better alternative in a python context,
|
||||
or the Confirm action in a screen context.
|
||||
"""
|
||||
|
||||
if config.confirm_screen and renpy.has_screen('confirm'):
|
||||
screen = "confirm"
|
||||
elif renpy.has_screen("yesno_prompt"):
|
||||
|
||||
@@ -303,24 +303,6 @@ 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.
|
||||
#
|
||||
|
||||
@@ -409,7 +409,7 @@ init -1500 python:
|
||||
if filename is None:
|
||||
return
|
||||
|
||||
if self.single_track:
|
||||
if self.single_track or filename not in self.filenames:
|
||||
self.play(None, offset=0, queue=True)
|
||||
else:
|
||||
self.play(None, offset=1, queue=True)
|
||||
|
||||
@@ -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.overlay_screens.append("_bubble_editor")
|
||||
config.always_shown_screens.append("_bubble_editor")
|
||||
|
||||
|
||||
|
||||
screen _bubble_editor():
|
||||
zorder 1050
|
||||
|
||||
if bubble.shown.value:
|
||||
if bubble.shown.value and not _menu:
|
||||
|
||||
drag:
|
||||
draggable True
|
||||
|
||||
@@ -560,7 +560,7 @@ init -1499 python in _renpysteam:
|
||||
################################################################## Keyboard
|
||||
|
||||
# True to show the keyboard once, False otherwise.
|
||||
keyboard_mode = "always"
|
||||
keyboard_mode = "once"
|
||||
|
||||
# True if this is the start of a new interaction, and so the keyboard
|
||||
# should be shown if a text box appears.
|
||||
@@ -570,12 +570,16 @@ init -1499 python in _renpysteam:
|
||||
keyboard_showing = None
|
||||
|
||||
# Should the layers be shifted so the baseline is in view?
|
||||
keyboard_shift = True
|
||||
keyboard_shift = False
|
||||
|
||||
# Where the basline is shifted to on the screen. This is a floating point number,
|
||||
# Where the baseline 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
|
||||
@@ -600,10 +604,19 @@ 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)
|
||||
|
||||
if keyboard_shift:
|
||||
y = int(renpy.exports.get_physical_size()[1] * keyboard_baseline) - h
|
||||
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)
|
||||
|
||||
steamapi.SteamUtils().ShowFloatingGamepadTextInput(
|
||||
steamapi.k_EFloatingGamepadTextInputModeModeSingleLine,
|
||||
|
||||
@@ -168,8 +168,11 @@ init -1800:
|
||||
ruby_style style.ruby_text
|
||||
altruby_style style.altruby_text
|
||||
# hyperlink_functions is set in 00defaults.rpy
|
||||
hinting "auto"
|
||||
hinting True
|
||||
adjust_spacing True
|
||||
emoji_font "TwemojiCOLRv0.ttf"
|
||||
prefer_emoji True
|
||||
shaper ("harfbuzz" if (not PY2) else "freetype")
|
||||
|
||||
# Window properties
|
||||
background None
|
||||
|
||||
@@ -210,7 +210,7 @@ init -1100 python in _sync:
|
||||
f.write(content)
|
||||
|
||||
fetch_id = emscripten.run_script_int(
|
||||
"""fetchFile("PUT", "{url}", "/sync.data", null)""".format(url=url))
|
||||
"""fetchFile("PUT", "{url}", "/sync.data", null, "application/octet-string")""".format(url=url))
|
||||
|
||||
status = "PENDING"
|
||||
message = "Pending."
|
||||
@@ -515,6 +515,7 @@ init -1100 python in _sync:
|
||||
init -1100:
|
||||
|
||||
screen sync_confirm():
|
||||
style_prefix "sync"
|
||||
modal True
|
||||
zorder 100
|
||||
|
||||
@@ -544,6 +545,7 @@ init -1100:
|
||||
key "game_menu" action Return(False)
|
||||
|
||||
screen sync_prompt(prompt):
|
||||
style_prefix "sync"
|
||||
modal True
|
||||
zorder 100
|
||||
|
||||
@@ -582,6 +584,7 @@ init -1100:
|
||||
|
||||
|
||||
screen sync_success(sync_id):
|
||||
style_prefix "sync"
|
||||
modal True
|
||||
zorder 100
|
||||
|
||||
@@ -617,6 +620,7 @@ init -1100:
|
||||
key "game_menu" action Return(False)
|
||||
|
||||
screen sync_error(message):
|
||||
style_prefix "sync"
|
||||
modal True
|
||||
zorder 100
|
||||
|
||||
|
||||
+289
-33
@@ -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):
|
||||
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):
|
||||
"""
|
||||
Takes the same arguments as update().
|
||||
"""
|
||||
@@ -312,6 +312,12 @@ 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.
|
||||
|
||||
@@ -344,6 +350,9 @@ 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())
|
||||
@@ -415,14 +424,11 @@ 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()
|
||||
|
||||
pretty_version = self.check_versions()
|
||||
self.pretty_version = self.check_versions()
|
||||
|
||||
if not self.modules:
|
||||
self.can_cancel = False
|
||||
@@ -432,12 +438,61 @@ init -1500 python in updater:
|
||||
renpy.restart_interaction()
|
||||
return
|
||||
|
||||
persistent._update_version[self.url] = pretty_version
|
||||
persistent._update_version[self.url] = self.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.
|
||||
@@ -445,7 +500,7 @@ init -1500 python in updater:
|
||||
self.can_cancel = True
|
||||
self.can_proceed = True
|
||||
self.state = self.UPDATE_AVAILABLE
|
||||
self.version = pretty_version
|
||||
self.version = self.pretty_version
|
||||
|
||||
renpy.restart_interaction()
|
||||
|
||||
@@ -461,15 +516,131 @@ init -1500 python in updater:
|
||||
self.can_cancel = True
|
||||
self.can_proceed = False
|
||||
|
||||
# Disable autoreload.
|
||||
renpy.set_autoreload(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
|
||||
|
||||
# Perform the update.
|
||||
self.new_state = dict(self.current_state)
|
||||
renpy.restart_interaction()
|
||||
|
||||
self.progress = 0.0
|
||||
self.state = self.PREPARING
|
||||
def zsync_update(self):
|
||||
|
||||
self.prompt_confirm()
|
||||
|
||||
if self.patch:
|
||||
for i in self.modules:
|
||||
@@ -735,6 +906,10 @@ 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:
|
||||
@@ -763,36 +938,106 @@ 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()
|
||||
|
||||
if self.public_key is not None:
|
||||
fn = os.path.join(self.updatedir, "updates.json.sig")
|
||||
urlretrieve(self.url + ".sig", fn)
|
||||
# Was updates.json verified?
|
||||
verified = False
|
||||
|
||||
with open(fn, "rb") as f:
|
||||
import codecs
|
||||
signature = codecs.decode(f.read(), "base64")
|
||||
# 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:
|
||||
rsa.verify(updates_json, signature, self.public_key)
|
||||
except Exception:
|
||||
raise UpdateError(_("Could not verify update signature."))
|
||||
|
||||
if "monkeypatch" in self.updates:
|
||||
future.utils.exec_(self.updates["monkeypatch"], globals(), globals())
|
||||
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
|
||||
|
||||
self.log.write("Verifying with RSA.\n")
|
||||
|
||||
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)
|
||||
|
||||
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())
|
||||
|
||||
def add_dlc_state(self, name):
|
||||
url = urlparse.urljoin(self.url, self.updates[name]["json_url"])
|
||||
f = urlopen(url)
|
||||
d = json.load(f)
|
||||
|
||||
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)
|
||||
|
||||
d[name]["version"] = 0
|
||||
self.current_state.update(d)
|
||||
|
||||
|
||||
def check_versions(self):
|
||||
"""
|
||||
Decides what modules need to be updated, if any.
|
||||
@@ -1448,7 +1693,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):
|
||||
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):
|
||||
"""
|
||||
:doc: updater
|
||||
|
||||
@@ -1494,12 +1739,23 @@ 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)
|
||||
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)
|
||||
ui.timer(.1, repeat=True, action=renpy.restart_interaction)
|
||||
renpy.call_screen("updater", u=u)
|
||||
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,11 @@
|
||||
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/
|
||||
@@ -65,11 +65,25 @@ 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)
|
||||
|
||||
@@ -532,6 +546,102 @@ 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))
|
||||
|
||||
@@ -85,8 +85,11 @@ init label _errorhandling:
|
||||
slow_cps None
|
||||
slow_cps_multiplier 1.0
|
||||
slow_abortable False
|
||||
hinting "auto"
|
||||
hinting True
|
||||
adjust_spacing True
|
||||
emoji_font "TwemojiCOLRv0.ttf"
|
||||
prefer_emoji True
|
||||
shaper ("harfbuzz" if (not PY2) else "freetype")
|
||||
|
||||
# Window properties
|
||||
background None
|
||||
|
||||
+7
-1
@@ -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 = False
|
||||
tts_front_to_back = True
|
||||
|
||||
# Should live2d loading be logged to log.txt
|
||||
log_live2d_loading = False
|
||||
@@ -1398,6 +1398,12 @@ 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
|
||||
|
||||
@@ -402,9 +402,8 @@ adv = ADVCharacter(None,
|
||||
|
||||
kind=False)
|
||||
|
||||
# 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.)
|
||||
# This character is copied when a name-only say statement is called.
|
||||
name_only = adv
|
||||
|
||||
|
||||
def predict_say(who, what):
|
||||
@@ -417,8 +416,7 @@ def predict_say(who, what):
|
||||
|
||||
def say(who, what, interact=True, *args, **kwargs):
|
||||
who = Character(who, kind=adv)
|
||||
who(what, interact=interact, *args, **kwargs)
|
||||
|
||||
who(what, *args, interact=interact, **kwargs)
|
||||
|
||||
# Used by renpy.reshow_say and extend.
|
||||
_last_say_who = None
|
||||
|
||||
@@ -690,6 +690,9 @@ 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)
|
||||
|
||||
@@ -707,9 +710,6 @@ 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
|
||||
|
||||
@@ -308,7 +308,7 @@ class SMAnimation(renpy.display.displayable.Displayable):
|
||||
for edges in self.edges.values():
|
||||
args.extend(edges)
|
||||
|
||||
return SMAnimation(self.initial, delay=self.delay, *args, **self.properties)
|
||||
return SMAnimation(self.initial, *args, delay=self.delay, **self.properties)
|
||||
|
||||
|
||||
def Animation(*args, **kwargs):
|
||||
|
||||
@@ -1903,6 +1903,19 @@ 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:
|
||||
@@ -2114,7 +2127,9 @@ class Bar(renpy.display.displayable.Displayable):
|
||||
|
||||
self.value = value
|
||||
adjustment = value.get_adjustment()
|
||||
renpy.game.interface.timeout(0)
|
||||
|
||||
if renpy.game.interface is not None:
|
||||
renpy.game.interface.timeout(0)
|
||||
|
||||
tooltip = value.get_tooltip()
|
||||
if tooltip is not None:
|
||||
|
||||
@@ -1937,8 +1937,6 @@ 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
|
||||
@@ -2035,7 +2033,7 @@ class Interface(object):
|
||||
if i in renderers:
|
||||
gl2_renderers.append(i + "2")
|
||||
|
||||
if renpy.config.gl2:
|
||||
if renpy.config.gl2 or renpy.macintosh:
|
||||
renderers = gl2_renderers + renderers
|
||||
|
||||
# Prevent a performance warning if the renderer
|
||||
@@ -2133,7 +2131,6 @@ 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
|
||||
@@ -2919,7 +2916,6 @@ 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
|
||||
@@ -2936,7 +2932,6 @@ 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')
|
||||
|
||||
@@ -457,6 +457,7 @@ 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
|
||||
@@ -936,6 +937,7 @@ class DragGroup(renpy.display.layout.MultiBox):
|
||||
super(DragGroup, self).add(child)
|
||||
|
||||
self.sorted = False
|
||||
renpy.display.render.invalidate(self)
|
||||
|
||||
def remove(self, child):
|
||||
"""
|
||||
|
||||
@@ -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(base=True, *override) # type: ignore
|
||||
d = renpy.exports.get_displayable(*override, base=True) # type: ignore
|
||||
|
||||
if (d is not None) and (current is not d) and not grab:
|
||||
current = d
|
||||
|
||||
+78
-43
@@ -712,8 +712,9 @@ class Image(ImageBase):
|
||||
"""
|
||||
|
||||
is_svg = False
|
||||
dpi = 96
|
||||
|
||||
def __init__(self, filename, **properties):
|
||||
def __init__(self, filename, dpi=96, **properties):
|
||||
"""
|
||||
@param filename: The filename that the image will be loaded from.
|
||||
"""
|
||||
@@ -731,7 +732,10 @@ 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)
|
||||
@@ -771,19 +775,23 @@ 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)
|
||||
height = int(height * renpy.display.draw.draw_per_virt)
|
||||
width = int(width * renpy.display.draw.draw_per_virt * self.dpi / 96)
|
||||
height = int(height * renpy.display.draw.draw_per_virt * self.dpi / 96)
|
||||
|
||||
filelike = renpy.loader.load(self.filename, directory="images")
|
||||
if filename != self.filename:
|
||||
|
||||
with filelike as f:
|
||||
surf = renpy.display.pgrender.load_image(filelike, filename, size=(width, height))
|
||||
# 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))
|
||||
|
||||
return surf
|
||||
|
||||
@@ -1012,8 +1020,9 @@ class FactorScale(ImageBase):
|
||||
|
||||
image logo doubled = im.FactorScale("logo.png", 1.5)
|
||||
|
||||
The same effect can now be achieved with the :tpref:`zoom` or the
|
||||
:tpref:`xzoom` and :tpref:`yzoom` transform properties.
|
||||
.. deprecated:: 7.4.0
|
||||
Use the :tpref:`zoom`, or the
|
||||
:tpref:`xzoom` and :tpref:`yzoom` transform properties.
|
||||
"""
|
||||
|
||||
def __init__(self, im, width, height=None, bilinear=True, **properties):
|
||||
@@ -1073,9 +1082,9 @@ class Flip(ImageBase):
|
||||
|
||||
image eileen flip = im.Flip("eileen_happy.png", vertical=True)
|
||||
|
||||
The same effect can now be achieved by setting
|
||||
:tpref:`xzoom` (for horizontal flip)
|
||||
or :tpref:`yzoom` (for vertical flip) to a negative value.
|
||||
.. deprecated:: 7.4.0
|
||||
Set :tpref:`xzoom` (for horizontal flip)
|
||||
or :tpref:`yzoom` (for vertical flip) to a negative value.
|
||||
"""
|
||||
|
||||
def __init__(self, im, horizontal=False, vertical=False, **properties):
|
||||
@@ -1165,7 +1174,8 @@ class Crop(ImageBase):
|
||||
|
||||
image logo crop = im.Crop("logo.png", (0, 0, 100, 307))
|
||||
|
||||
The same effect can now be achieved by setting the :tpref:`crop` transform property.
|
||||
.. deprecated:: 7.4.0
|
||||
Use the :tpref:`crop` transform property.
|
||||
"""
|
||||
|
||||
def __init__(self, im, x, y=None, w=None, h=None, **properties):
|
||||
@@ -1365,7 +1375,8 @@ class Blur(ImageBase):
|
||||
|
||||
image logo blurred = im.Blur("logo.png", 1.5)
|
||||
|
||||
The same effect can now be achieved with the :tpref:`blur` transform property.
|
||||
.. deprecated:: 7.4.0
|
||||
Use the :tpref:`blur` transform property.
|
||||
"""
|
||||
|
||||
def __init__(self, im, xrad, yrad=None, **properties):
|
||||
@@ -1427,6 +1438,10 @@ 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):
|
||||
@@ -1475,6 +1490,9 @@ 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):
|
||||
@@ -1567,8 +1585,9 @@ im.matrix(%f, %f, %f, %f, %f.
|
||||
Returns an identity matrix, one that does not change color or
|
||||
alpha.
|
||||
|
||||
A suitable equivalent for the :tpref:`matrixcolor` transform property
|
||||
is IdentityMatrix().
|
||||
.. deprecated:: 7.4.0
|
||||
Use :func:`IdentityMatrix() <IdentityMatrix>`
|
||||
with the :tpref:`matrixcolor` transform property.
|
||||
"""
|
||||
|
||||
return matrix(1, 0, 0, 0, 0,
|
||||
@@ -1598,8 +1617,9 @@ im.matrix(%f, %f, %f, %f, %f.
|
||||
mostly sensitive to green, more of the green channel is
|
||||
kept then the other two channels.
|
||||
|
||||
A suitable equivalent for the :tpref:`matrixcolor` transform property
|
||||
is SaturationMatrix(value, desat).
|
||||
.. deprecated:: 7.4.0
|
||||
Use :func:`SaturationMatrix(value, desat) <SaturationMatrix>`
|
||||
with the :tpref:`matrixcolor` transform property.
|
||||
"""
|
||||
|
||||
r, g, b = desat
|
||||
@@ -1622,8 +1642,9 @@ im.matrix(%f, %f, %f, %f, %f.
|
||||
grayscale). This is equivalent to calling
|
||||
im.matrix.saturation(0).
|
||||
|
||||
A suitable equivalent for the :tpref:`matrixcolor` transform property
|
||||
is SaturationMatrix(0).
|
||||
.. deprecated:: 7.4.0
|
||||
Use :func:`SaturationMatrix(0) <SaturationMatrix>`
|
||||
with the :tpref:`matrixcolor` transform property.
|
||||
"""
|
||||
|
||||
return matrix.saturation(0.0)
|
||||
@@ -1641,8 +1662,9 @@ 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.)
|
||||
|
||||
A suitable equivalent for the :tpref:`matrixcolor` transform property
|
||||
is TintMatrix(Color((r, g, b))).
|
||||
.. deprecated:: 7.4.0
|
||||
Use :func:`TintMatrix(Color((r, g, b))) <TintMatrix>`
|
||||
with the :tpref:`matrixcolor` transform property.
|
||||
"""
|
||||
|
||||
return matrix(r, 0, 0, 0, 0,
|
||||
@@ -1659,8 +1681,9 @@ 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.
|
||||
|
||||
A suitable equivalent for the :tpref:`matrixcolor` transform property
|
||||
is InvertMatrix(1.0).
|
||||
.. deprecated:: 7.4.0
|
||||
Use :func:`InvertMatrix(1.0) <InvertMatrix>`
|
||||
with the :tpref:`matrixcolor` transform property.
|
||||
"""
|
||||
|
||||
return matrix(-1, 0, 0, 0, 1,
|
||||
@@ -1681,8 +1704,9 @@ im.matrix(%f, %f, %f, %f, %f.
|
||||
a number between -1 and 1, with -1 the darkest possible
|
||||
image and 1 the brightest.
|
||||
|
||||
A suitable equivalent for the :tpref:`matrixcolor` transform property
|
||||
is BrightnessMatrix(b).
|
||||
.. deprecated:: 7.4.0
|
||||
Use :func:`BrightnessMatrix(b) <BrightnessMatrix>`
|
||||
with the :tpref:`matrixcolor` transform property.
|
||||
"""
|
||||
|
||||
return matrix(1, 0, 0, 0, b,
|
||||
@@ -1699,8 +1723,9 @@ 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.
|
||||
|
||||
A suitable equivalent for the :tpref:`matrixcolor` transform property
|
||||
is OpacityMatrix(o).
|
||||
.. deprecated:: 7.4.0
|
||||
Use :func:`OpacityMatrix(o) <OpacityMatrix>`
|
||||
with the :tpref:`matrixcolor` transform property.
|
||||
"""
|
||||
|
||||
return matrix(1, 0, 0, 0, 0,
|
||||
@@ -1718,8 +1743,9 @@ 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.
|
||||
|
||||
A suitable equivalent for the :tpref:`matrixcolor` transform property
|
||||
is ContrastMatrix(c).
|
||||
.. deprecated:: 7.4.0
|
||||
Use :func:`ContrastMatrix(c) <ContrastMatrix>`
|
||||
with the :tpref:`matrixcolor` transform property.
|
||||
"""
|
||||
|
||||
return matrix.brightness(-.5) * matrix.tint(c, c, c) * matrix.brightness(.5)
|
||||
@@ -1734,8 +1760,9 @@ im.matrix(%f, %f, %f, %f, %f.
|
||||
Returns an im.matrix that rotates the hue by `h` degrees, while
|
||||
preserving luminosity.
|
||||
|
||||
A suitable equivalent for the :tpref:`matrixcolor` transform property
|
||||
is HueMatrix(h).
|
||||
.. deprecated:: 7.4.0
|
||||
Use :func:`HueMatrix(h) <HueMatrix>`
|
||||
with the :tpref:`matrixcolor` transform property.
|
||||
"""
|
||||
|
||||
h = h * math.pi / 180
|
||||
@@ -1769,8 +1796,9 @@ im.matrix(%f, %f, %f, %f, %f.
|
||||
im.matrix.colorize("#f00", "#00f"))
|
||||
|
||||
|
||||
A suitable equivalent for the :tpref:`matrixcolor` transform property
|
||||
is ColorizeMatrix(black_color, white_color).
|
||||
.. deprecated:: 7.4.0
|
||||
Use :func:`ColorizeMatrix(black_color, white_color) <ColorizeMatrix>`
|
||||
with the :tpref:`matrixcolor` transform property.
|
||||
"""
|
||||
|
||||
(r0, g0, b0, _a0) = renpy.easy.color(black_color) # type: ignore
|
||||
@@ -1797,8 +1825,9 @@ def Grayscale(im, desat=(0.2126, 0.7152, 0.0722), **properties):
|
||||
An image manipulator that creates a desaturated version of the image
|
||||
manipulator `im`.
|
||||
|
||||
The same effect can now be achieved by supplying SaturationMatrix(0)
|
||||
to the :tpref:`matrixcolor` transform property.
|
||||
.. deprecated:: 7.4.0
|
||||
Set the :tpref:`matrixcolor` transform property to
|
||||
:func:`SaturationMatrix(0) <SaturationMatrix>`.
|
||||
"""
|
||||
|
||||
return MatrixColor(im, matrix.saturation(0.0, desat), **properties)
|
||||
@@ -1812,8 +1841,9 @@ 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`.
|
||||
|
||||
The same effect can now be achieved by supplying SepiaMatrix()
|
||||
to the :tpref:`matrixcolor` transform property.
|
||||
.. deprecated:: 7.4.0
|
||||
Set the :tpref:`matrixcolor` transform property to
|
||||
:func:`SepiaMatrix() <SepiaMatrix>`
|
||||
"""
|
||||
|
||||
return MatrixColor(im, matrix.saturation(0.0, desat) * matrix.tint(tint[0], tint[1], tint[2]), **properties)
|
||||
@@ -1854,8 +1884,8 @@ class Tile(ImageBase):
|
||||
If not None, a (width, height) tuple. If None, this defaults to
|
||||
(:var:`config.screen_width`, :var:`config.screen_height`).
|
||||
|
||||
The same effect can now be achieved using the :func:`Tile`
|
||||
displayable, with ``Tile(im, size=size)``.
|
||||
.. deprecated:: 7.4.0
|
||||
Use :func:`Tile(im, xysize=size, **properties) <Tile>`.
|
||||
"""
|
||||
|
||||
def __init__(self, im, size=None, **properties):
|
||||
@@ -1951,7 +1981,7 @@ def image(arg, loose=False, **properties):
|
||||
"""
|
||||
:doc: im_image
|
||||
:name: Image
|
||||
:args: (filename, *, optimize_bounds=True, oversample=1, **properties)
|
||||
:args: (filename, *, optimize_bounds=True, oversample=1, dpi=96, **properties)
|
||||
|
||||
Loads an image from a file. `filename` is a
|
||||
string giving the name of the file.
|
||||
@@ -1970,6 +2000,11 @@ 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.
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
+12
-2
@@ -31,7 +31,11 @@ import renpy
|
||||
from renpy.display.render import render, Render
|
||||
|
||||
|
||||
compute_raw = renpy.display.core.absolute.compute_raw
|
||||
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
|
||||
|
||||
|
||||
def xyminimums(style, width, height):
|
||||
@@ -205,6 +209,8 @@ 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()
|
||||
@@ -2069,7 +2075,7 @@ class AdjustTimes(Container):
|
||||
|
||||
def event(self, ev, x, y, st):
|
||||
st, _ = self.adjusted_times()
|
||||
Container.event(self, ev, x, y, st)
|
||||
return Container.event(self, ev, x, y, st)
|
||||
|
||||
def get_placement(self):
|
||||
return self.child.get_placement()
|
||||
@@ -2418,6 +2424,7 @@ 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.
|
||||
@@ -2430,6 +2437,9 @@ 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.
|
||||
|
||||
@@ -901,7 +901,7 @@ class Transform(Container):
|
||||
self.active = True
|
||||
|
||||
if self.state.last_events != self.state.events:
|
||||
if self.state.events:
|
||||
if self.state.events and renpy.game.interface is not None:
|
||||
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:
|
||||
if self.state.events and renpy.game.interface is not None:
|
||||
renpy.game.interface.timeout(0)
|
||||
self.state.last_events = self.state.events
|
||||
|
||||
|
||||
@@ -233,6 +233,9 @@ 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`.
|
||||
@@ -241,6 +244,7 @@ def displayable(d):
|
||||
global old_self_voicing
|
||||
global last
|
||||
global last_raw
|
||||
global notify_text
|
||||
|
||||
self_voicing = renpy.game.preferences.self_voicing
|
||||
|
||||
@@ -282,6 +286,11 @@ 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
|
||||
|
||||
+43
-44
@@ -249,33 +249,43 @@ def get_movie_texture_web(channel, mask_channel, side_mask, mipmap):
|
||||
return tex, new
|
||||
|
||||
|
||||
def render_movie(channel, width, height, group=None):
|
||||
tex, _new = get_movie_texture(channel)
|
||||
def resize_movie(r, width, height):
|
||||
"""
|
||||
A utility function to resize a Render or texture to the given
|
||||
dimensions.
|
||||
"""
|
||||
|
||||
if group is not None:
|
||||
if tex is None:
|
||||
tex = group_texture.get(group, None)
|
||||
else:
|
||||
group_texture[group] = tex
|
||||
|
||||
if tex is None:
|
||||
if r is None:
|
||||
return None
|
||||
|
||||
sw, sh = tex.get_size()
|
||||
rv = renpy.display.render.Render(width, height)
|
||||
|
||||
scale = min(1.0 * width / sw, 1.0 * height / sh)
|
||||
sw, sh = r.get_size()
|
||||
|
||||
if not (sw and sh):
|
||||
return rv
|
||||
|
||||
scale = min(1.0 * width / sw, 1.0 * height / sh) # type: float
|
||||
|
||||
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(tex, (int((width - dw) / 2), int((height - dh) / 2)))
|
||||
rv.blit(r, (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)
|
||||
@@ -539,42 +549,31 @@ class Movie(renpy.display.displayable.Displayable):
|
||||
|
||||
return rv
|
||||
|
||||
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))
|
||||
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:
|
||||
rv = renpy.display.render.Render(0, 0)
|
||||
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))
|
||||
|
||||
else:
|
||||
rv = renpy.display.render.Render(0, 0)
|
||||
|
||||
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)
|
||||
if self.size is not None:
|
||||
rv = resize_movie(rv, self.size[0], self.size[1])
|
||||
|
||||
# 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
|
||||
|
||||
@@ -120,6 +120,9 @@ 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
|
||||
@@ -128,9 +131,11 @@ 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
|
||||
|
||||
@@ -178,6 +183,13 @@ 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`)
|
||||
@@ -648,13 +660,13 @@ class VPGrid(Viewport):
|
||||
yspacing = self.style.spacing
|
||||
|
||||
if renpy.config.relative_spacing:
|
||||
xspacing = renpy.display.layout.scale(xspacing, width)
|
||||
yspacing = renpy.display.layout.scale(yspacing, height)
|
||||
xspacing = renpy.display.layout.compute_raw(xspacing, width)
|
||||
yspacing = renpy.display.layout.compute_raw(yspacing, 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)
|
||||
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)
|
||||
|
||||
rend = renpy.display.render.render(self.children[0], child_width, child_height, st, at)
|
||||
cw, ch = rend.get_size()
|
||||
|
||||
@@ -777,6 +777,8 @@ 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):
|
||||
|
||||
+231
-10
@@ -102,11 +102,12 @@ 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, register_decorator
|
||||
from renpy.statements import register as register_statement
|
||||
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
|
||||
@@ -122,6 +123,8 @@ 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")
|
||||
@@ -849,7 +852,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(prompt, default, length=length, allow=allow, exclude=exclude, mask=mask)
|
||||
wi = renpy.display.behavior.WebInput(substitute(prompt), default, length=length, allow=allow, exclude=exclude, mask=mask)
|
||||
renpy.ui.add(wi)
|
||||
|
||||
renpy.exports.shown_window()
|
||||
@@ -1438,7 +1441,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
|
||||
:func:`say` is used to create the speaking character.
|
||||
:var:`say` store function is called.
|
||||
|
||||
`what`
|
||||
A string giving the line to say. Percent-substitutions are performed
|
||||
@@ -1455,6 +1458,7 @@ 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:
|
||||
@@ -2386,7 +2390,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, interact=interact, *args, **kwargs)
|
||||
say(who, what, *args, interact=interact, **kwargs)
|
||||
|
||||
|
||||
curried_do_reshow_say = curry(do_reshow_say)
|
||||
@@ -2445,7 +2449,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 the call returns.
|
||||
be reset to their original value when returning to the prior context.
|
||||
|
||||
An example call is::
|
||||
|
||||
@@ -2755,9 +2759,9 @@ def iconify():
|
||||
|
||||
# New context stuff.
|
||||
call_in_new_context = renpy.game.call_in_new_context
|
||||
curried_call_in_new_context = renpy.curry.curry(renpy.game.call_in_new_context)
|
||||
curried_call_in_new_context = curry(call_in_new_context)
|
||||
invoke_in_new_context = renpy.game.invoke_in_new_context
|
||||
curried_invoke_in_new_context = renpy.curry.curry(renpy.game.invoke_in_new_context)
|
||||
curried_invoke_in_new_context = curry(invoke_in_new_context)
|
||||
call_replay = renpy.game.call_replay
|
||||
|
||||
renpy_pure("curried_call_in_new_context")
|
||||
@@ -3000,8 +3004,9 @@ def pop_call():
|
||||
:doc: label
|
||||
:name: renpy.pop_call
|
||||
|
||||
Pops the current call from the call stack, without returning to
|
||||
the location.
|
||||
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.
|
||||
|
||||
This can be used if a label that is called decides not to return
|
||||
to its caller.
|
||||
@@ -3338,7 +3343,7 @@ def call_screen(_screen_name, *args, **kwargs):
|
||||
if "_with_none" in kwargs:
|
||||
with_none = kwargs.pop("_with_none")
|
||||
|
||||
show_screen(_screen_name, _transient=True, *args, **kwargs)
|
||||
show_screen(_screen_name, *args, _transient=True, **kwargs)
|
||||
|
||||
roll_forward = renpy.exports.roll_forward_info()
|
||||
|
||||
@@ -3508,6 +3513,8 @@ 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()
|
||||
|
||||
|
||||
@@ -3909,6 +3916,12 @@ 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)
|
||||
@@ -4566,3 +4579,211 @@ 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)
|
||||
|
||||
+1
-5
@@ -262,10 +262,6 @@ 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.
|
||||
|
||||
@@ -372,7 +368,7 @@ def call_replay(label, scope={}):
|
||||
|
||||
Calls a label as a memory.
|
||||
|
||||
Keyword arguments are used to set the initial values of variables in the
|
||||
The `scope` argument is used to set the initial values of variables in the
|
||||
memory context.
|
||||
"""
|
||||
|
||||
|
||||
+1
-1
@@ -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)
|
||||
rv = gltexture.texture_grid_from_surface(surf, transient, properties)
|
||||
self.texture_cache[surf] = rv
|
||||
self.ready_texture_queue.add(rv)
|
||||
|
||||
|
||||
+30
-50
@@ -142,21 +142,12 @@ 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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
# Create a texture of the given size.
|
||||
glActiveTexture(GL_TEXTURE0)
|
||||
@@ -367,7 +358,8 @@ cdef class TextureCore:
|
||||
|
||||
|
||||
def load_surface(self, surf, x, y, w, h,
|
||||
border_left, border_top, border_right, border_bottom):
|
||||
border_left, border_top, border_right, border_bottom,
|
||||
properties):
|
||||
|
||||
"""
|
||||
Loads a pygame surface into this texture rectangle.
|
||||
@@ -379,7 +371,8 @@ cdef class TextureCore:
|
||||
|
||||
self.premult = premultiply(
|
||||
surf, x, y, w, h,
|
||||
border_left, border_top, border_right, border_bottom)
|
||||
border_left, border_top, border_right, border_bottom,
|
||||
properties.get("premultiplied", False))
|
||||
|
||||
self.premult_size = (w, h)
|
||||
|
||||
@@ -894,7 +887,7 @@ def compute_tiling(width, max_size, min_fill_factor):
|
||||
return row, tiles
|
||||
|
||||
|
||||
def texture_grid_from_surface(surf, transient):
|
||||
def texture_grid_from_surface(surf, transient, properties):
|
||||
"""
|
||||
This takes a Surface and turns it into a TextureGrid.
|
||||
"""
|
||||
@@ -931,7 +924,8 @@ def texture_grid_from_surface(surf, transient):
|
||||
|
||||
tex = alloc_texture(texwidth, texheight)
|
||||
tex.load_surface(surf, x, y, width, height,
|
||||
border_x, border_y, border_x, border_y)
|
||||
border_x, border_y, border_x, border_y,
|
||||
properties)
|
||||
|
||||
row.append(tex)
|
||||
|
||||
@@ -1201,12 +1195,17 @@ def premultiply(
|
||||
int y,
|
||||
int w,
|
||||
int h,
|
||||
bint border_left, bint border_top, bint border_right, bint border_bottom):
|
||||
bint border_left, bint border_top, bint border_right, bint border_bottom,
|
||||
bint premultiplied):
|
||||
|
||||
"""
|
||||
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.
|
||||
@@ -1272,7 +1271,18 @@ def premultiply(
|
||||
# Advance to the next row.
|
||||
pixels += surf.pitch
|
||||
|
||||
if tex_format == GL_RGBA:
|
||||
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:
|
||||
|
||||
# RGBA path.
|
||||
|
||||
@@ -1300,36 +1310,6 @@ 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
|
||||
|
||||
@@ -42,7 +42,7 @@ cdef class GL2Model:
|
||||
|
||||
for i in self.uniforms.itervalues():
|
||||
if isinstance(i, GLTexture):
|
||||
i.load_gltexture()
|
||||
i.load()
|
||||
|
||||
def program_uniforms(self, shader):
|
||||
"""
|
||||
@@ -118,4 +118,3 @@ cdef class GL2Model:
|
||||
rv.forward = Matrix.cscale(reciprocal_factor, reciprocal_factor, reciprocal_factor) * rv.forward
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
@@ -493,6 +493,67 @@ 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`
|
||||
@@ -588,7 +649,13 @@ cdef class GLTexture(GL2Model):
|
||||
pass # Let's not error on shutdown.
|
||||
|
||||
def load(self):
|
||||
self.load_gltexture()
|
||||
|
||||
if self.properties.get("premultiplied", False):
|
||||
self.load_gltexture_premultiplied()
|
||||
else:
|
||||
self.load_gltexture()
|
||||
|
||||
|
||||
|
||||
def program_uniforms(self, shader):
|
||||
shader.set_uniform("tex0", self)
|
||||
|
||||
@@ -661,6 +661,9 @@ 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.
|
||||
|
||||
+4
-1
@@ -866,7 +866,7 @@ class Lexer(object):
|
||||
|
||||
def string(self):
|
||||
"""
|
||||
Lexes a string, and returns the string to the user, or None if
|
||||
Lexes a non-triple-quoted 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,6 +928,9 @@ 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?"""([^\\"]|\\.|"(?!""))*"""')
|
||||
|
||||
+92
-18
@@ -444,6 +444,9 @@ 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))
|
||||
@@ -689,8 +692,49 @@ 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
|
||||
@@ -715,6 +759,9 @@ 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
|
||||
@@ -730,6 +777,11 @@ 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)
|
||||
|
||||
@@ -780,6 +832,9 @@ 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):
|
||||
"""
|
||||
@@ -800,22 +855,33 @@ def report_character_stats(charastats):
|
||||
|
||||
rv = [ "Character statistics (for default language):" ]
|
||||
|
||||
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 ".")
|
||||
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."
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -967,7 +1033,11 @@ 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:
|
||||
@@ -1000,7 +1070,7 @@ def lint():
|
||||
# The current count.
|
||||
counts = collections.defaultdict(Count)
|
||||
|
||||
charastats = collections.defaultdict(int)
|
||||
charastats = collections.defaultdict(Count)
|
||||
|
||||
# The current language.
|
||||
language = None
|
||||
@@ -1046,7 +1116,7 @@ def lint():
|
||||
|
||||
counts[language].add(node.what)
|
||||
if language is None:
|
||||
charastats[node.who or 'narrator' ] += 1
|
||||
charastats[node.who or 'narrator'].add(node.what)
|
||||
|
||||
elif isinstance(node, renpy.ast.Menu):
|
||||
check_menu(node)
|
||||
@@ -1070,7 +1140,7 @@ def lint():
|
||||
elif isinstance(node, renpy.ast.Label):
|
||||
check_label(node)
|
||||
|
||||
elif isinstance(node, renpy.ast.Translate):
|
||||
elif isinstance(node, renpy.ast.Translate) and args.orphan_tl:
|
||||
language = node.language
|
||||
if language is None:
|
||||
none_language_ids.add(node.identifier)
|
||||
@@ -1095,12 +1165,16 @@ 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)
|
||||
check_orphan_translations(none_language_ids, translated_ids)
|
||||
if args.orphan_tl:
|
||||
check_orphan_translations(none_language_ids, translated_ids)
|
||||
|
||||
for f in renpy.config.lint_hooks:
|
||||
f()
|
||||
|
||||
+46
-24
@@ -26,6 +26,7 @@ from typing import Optional
|
||||
|
||||
import renpy
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
import types
|
||||
import threading
|
||||
@@ -67,34 +68,20 @@ def get_path(fn):
|
||||
|
||||
# Asset Loading
|
||||
|
||||
apks = [ ]
|
||||
game_apks = [ ]
|
||||
|
||||
if renpy.android:
|
||||
import android.apk # type: ignore
|
||||
|
||||
expansion = os.environ.get("ANDROID_EXPANSION", None)
|
||||
if expansion is not None:
|
||||
print("Using expansion file", expansion)
|
||||
if renpy.config.renpy_base == renpy.config.basedir:
|
||||
# Read the game data from the APK.
|
||||
|
||||
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-game/'))
|
||||
game_apks.append(apks[0])
|
||||
|
||||
game_apks = [ apks[0] ]
|
||||
apks.append(android.apk.APK(prefix='assets/x-renpy/x-common/'))
|
||||
|
||||
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.
|
||||
@@ -987,19 +974,30 @@ class RenpyImporter(object):
|
||||
if self.translate(fullname):
|
||||
return self
|
||||
|
||||
def load_module(self, fullname):
|
||||
def load_module(self, fullname, mode="full"):
|
||||
"""
|
||||
Loads a module. Possible modes include "is_package", "get_source", "get_code", or "full".
|
||||
"""
|
||||
|
||||
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__ = filename
|
||||
mod.__file__ = renpy.config.gamedir + "/" + filename
|
||||
mod.__loader__ = self
|
||||
|
||||
if filename.endswith("__init__.py"):
|
||||
mod.__path__ = [ filename[:-len("__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")] ]
|
||||
|
||||
for encoding in [ "utf-8", "latin-1" ]:
|
||||
|
||||
@@ -1011,17 +1009,41 @@ 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()
|
||||
|
||||
|
||||
|
||||
+11
-1
@@ -36,6 +36,7 @@ import types
|
||||
import shutil
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
import renpy
|
||||
from json import dumps as json_dumps
|
||||
@@ -436,7 +437,13 @@ 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 }
|
||||
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(),
|
||||
}
|
||||
|
||||
for i in renpy.config.save_json_callbacks:
|
||||
i(json)
|
||||
@@ -558,6 +565,9 @@ 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
|
||||
|
||||
@@ -239,6 +239,7 @@ 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):
|
||||
|
||||
|
||||
+3
-2
@@ -252,8 +252,9 @@ def choose_variants():
|
||||
renpy.config.variants.insert(0, 'web') # type: ignore
|
||||
|
||||
# mobile
|
||||
userAgent = emscripten.run_script_string(r'''navigator.userAgent''')
|
||||
mobile = re.search('Mobile|Android|iPad|iPhone', userAgent)
|
||||
mobile = emscripten.run_script_int(
|
||||
r'''/Mobile|Android|iPad|iPhone/.test(navigator.userAgent)
|
||||
|| (navigator.userAgent.indexOf("Mac") != -1 && navigator.maxTouchPoints > 1)''')
|
||||
if mobile:
|
||||
renpy.config.variants.insert(0, 'mobile') # type: ignore
|
||||
# Reserve android/ios for when the OS API is exposed
|
||||
|
||||
+1
-1
@@ -501,7 +501,7 @@ def parse_arguments(l):
|
||||
state = l.checkpoint()
|
||||
|
||||
if not (expect_starred or expect_doublestarred):
|
||||
name = l.name()
|
||||
name = l.word()
|
||||
|
||||
if name and l.match(r'='):
|
||||
if name in names:
|
||||
|
||||
@@ -137,7 +137,7 @@ Preference("self_voicing_volume_drop", 0.5)
|
||||
Preference("emphasize_audio", False)
|
||||
|
||||
# Is the gamepad enabled?
|
||||
Preference("pad_enabled", True)
|
||||
Preference("pad_enabled", True, (bool, str))
|
||||
|
||||
# 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
Reference in New Issue
Block a user