Compare commits
459 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 | |||
| e1aff07997 | |||
| 02e365584e | |||
| ec8104ae6b | |||
| df114f794e | |||
| 7a801523e4 | |||
| 3a897f0fee | |||
| a5343eb0fa | |||
| beceb10166 | |||
| c687c175fe | |||
| 1032b4f990 | |||
| e4bed1cbc2 | |||
| bdcd6191bd | |||
| deadc4a466 | |||
| 505303f51a | |||
| 0f84d898f2 | |||
| 6041da97c5 | |||
| cb80aea906 | |||
| 850ad647cc | |||
| c02ccf8dcd | |||
| 49896ba938 | |||
| bdec32fa5b | |||
| be36f99d4c | |||
| c007c85490 | |||
| b349ab06fd | |||
| a33009b382 | |||
| c50c6bb23d | |||
| 60ade3ae19 | |||
| 09d119b2d4 | |||
| 4588c15333 | |||
| b9c5317556 | |||
| e051e1a120 | |||
| ef84c65ce5 | |||
| d3fe5cd6bb | |||
| 5073076bb5 | |||
| c0248b5af0 | |||
| 0e4fca9c2f | |||
| f35e340c16 | |||
| b4dc241e08 | |||
| d1c1c0f1ff | |||
| f45b13a101 | |||
| 3d26079aff | |||
| 4f152e93ab | |||
| 5fd0d17e0b | |||
| 0dd9c81e8c | |||
| fcaa02f21a | |||
| b3a0c8f47c | |||
| ca8977ae7c | |||
| 045932fed6 | |||
| 4c1570fdae | |||
| c7c499c6c8 | |||
| 7459dea76c | |||
| 0f30d74045 | |||
| 1c9cff6be3 | |||
| 2880e63082 | |||
| 55d16830c9 | |||
| 5422467e02 | |||
| 9189178a5f | |||
| 494bb54620 | |||
| d0e925156b | |||
| 7a96e8ad82 | |||
| 4d0d262a76 | |||
| 3dad9048f0 | |||
| 10ecb3f05b | |||
| 648f2835ac | |||
| 23adb33f71 | |||
| d62b8b8dfc | |||
| 5e03d9eb29 | |||
| eef91dcda8 | |||
| 29324f6092 | |||
| b224df24dc | |||
| 446a1d74e7 | |||
| 5b7bd98219 | |||
| c42b975be3 | |||
| 9207334808 | |||
| f976f31e7a | |||
| 3017331aa9 | |||
| 34d4eb57a4 | |||
| c7d7cc23b1 | |||
| a65cecfc18 | |||
| e8323f4cad | |||
| 4284d45069 | |||
| 9d9085d379 | |||
| cb0cf2bf5e | |||
| 2c4194e754 | |||
| ac2534a87f | |||
| 9c442bb694 | |||
| 20d7ecf868 | |||
| bb4e4da909 | |||
| b467514c39 | |||
| e51642ff3f | |||
| 8556d563c9 | |||
| a370643f40 | |||
| 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:
|
||||
|
||||
@@ -31,10 +31,9 @@ init python:
|
||||
ANDROID_NO_BUNDLE = 6
|
||||
ANDROID_OK = 7
|
||||
|
||||
JDK_REQUIREMENT=8
|
||||
JDK_REQUIREMENT = 42
|
||||
|
||||
NO_RAPT_TEXT = _("To build Android packages, please download RAPT, unzip it, and place it into the Ren'Py directory. Then restart the Ren'Py launcher.")
|
||||
NO_JDK_TEXT = _("A 64-bit/x64 Java [JDK_REQUIREMENT] Development Kit is required to build Android packages on Windows. The JDK is different from the JRE, so it's possible you have Java without having the JDK.\n\nPlease {a=https://www.renpy.org/jdk/[JDK_REQUIREMENT]}download and install the JDK{/a}, then restart the Ren'Py launcher.")
|
||||
NO_SDK_TEXT = _("RAPT has been installed, but you'll need to install the Android SDK before you can build Android packages. Choose Install SDK to do this.")
|
||||
NO_KEY_TEXT = _("RAPT has been installed, but a key hasn't been configured. Please generate new keys, or copy android.keystore and bundle.keystore to the base directory.")
|
||||
NO_CONFIG_TEXT = _("The current project has not been configured. Use \"Configure\" to configure it before building.")
|
||||
@@ -99,9 +98,12 @@ init python:
|
||||
rapt.plat.renpy = True
|
||||
rapt.plat.translate = __
|
||||
|
||||
JDK_REQUIREMENT = rapt.plat.jdk_requirement
|
||||
|
||||
else:
|
||||
rapt = None
|
||||
|
||||
|
||||
def AndroidState():
|
||||
"""
|
||||
Determines the state of the android install, and returns it.
|
||||
@@ -109,8 +111,6 @@ init python:
|
||||
|
||||
if RAPT_PATH is None:
|
||||
return ANDROID_NO_RAPT
|
||||
if renpy.windows and not "JAVA_HOME" in os.environ:
|
||||
return ANDROID_NO_JDK
|
||||
if not os.path.exists(rapt.plat.adb):
|
||||
return ANDROID_NO_SDK
|
||||
if not rapt.keys.keys_exist(project.current.path):
|
||||
@@ -133,8 +133,6 @@ init python:
|
||||
|
||||
if state == ANDROID_NO_RAPT:
|
||||
return NO_RAPT_TEXT
|
||||
if state == ANDROID_NO_JDK:
|
||||
return NO_JDK_TEXT
|
||||
if state == ANDROID_NO_SDK:
|
||||
return NO_SDK_TEXT
|
||||
if state == ANDROID_NO_KEY:
|
||||
|
||||
@@ -41,7 +41,7 @@ init python:
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def choose_directory(path):
|
||||
def choose_directory(default_path):
|
||||
"""
|
||||
Pops up a directory chooser.
|
||||
|
||||
@@ -54,33 +54,31 @@ init python:
|
||||
rather than user choice.
|
||||
"""
|
||||
|
||||
if path:
|
||||
default_path = path
|
||||
path = None
|
||||
else:
|
||||
try:
|
||||
default_path = os.path.dirname(os.path.abspath(config.renpy_base))
|
||||
except Exception:
|
||||
default_path = os.path.abspath(config.renpy_base)
|
||||
|
||||
if _renpytfd:
|
||||
path = _renpytfd.selectFolderDialog(__("Select Projects Directory"), default_path)
|
||||
else:
|
||||
path = None
|
||||
|
||||
is_default = False
|
||||
if default_path is None:
|
||||
try:
|
||||
default_path = os.path.dirname(os.path.abspath(config.renpy_base))
|
||||
except Exception:
|
||||
default_path = os.path.abspath(config.renpy_base)
|
||||
|
||||
# Path being None or "" means nothing was selected.
|
||||
if not path:
|
||||
path = default_path
|
||||
is_default = True
|
||||
|
||||
if default_path is None or not os.path.isdir(default_path) or not directory_is_writable(default_path):
|
||||
interface.error(_("No directory was selected, but one is required."))
|
||||
|
||||
return default_path, True
|
||||
|
||||
# Apply more thorough checks to an explicit path.
|
||||
path = renpy.fsdecode(path)
|
||||
|
||||
if (not os.path.isdir(path)) or (not directory_is_writable(path)):
|
||||
interface.error(_("The selected projects directory is not writable."))
|
||||
path = default_path
|
||||
is_default = True
|
||||
if not os.path.isdir(path):
|
||||
interface.error(_("The selected directory does not exist."))
|
||||
elif not directory_is_writable(path):
|
||||
interface.error(_("The selected directory is not writable."))
|
||||
|
||||
if is_default and (not directory_is_writable(path)):
|
||||
path = os.path.expanduser("~")
|
||||
|
||||
return path, is_default
|
||||
return path, False
|
||||
|
||||
+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):
|
||||
|
||||
@@ -98,7 +98,7 @@ label itch:
|
||||
|
||||
for fn in os.listdir(destination):
|
||||
|
||||
for pattern, channel in reversed(build['itch_channels']):
|
||||
for pattern, channel in reversed(build['itch_channels'].items()):
|
||||
if fnmatch.fnmatch(fn, pattern):
|
||||
break
|
||||
else:
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -889,3 +889,8 @@ translate spanish strings:
|
||||
# renpy/common/00sync.rpy:619
|
||||
old "Sync Error"
|
||||
new "Error de sincronización"
|
||||
|
||||
# renpy/common/00speechbubble.rpy:391
|
||||
old "(clear retained bubbles)"
|
||||
new "(borrar burbujas retenidas)"
|
||||
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -435,3 +435,9 @@
|
||||
# gui/game/gui.rpy:14
|
||||
old "## Enable checks for invalid or unstable properties in screens or transforms"
|
||||
new "## Habilitar comprobaciones de propiedades no válidas o inestables en pantallas o transformaciones."
|
||||
|
||||
# gui/game/gui.rpy:347
|
||||
old "## Additional space to add between history screen entries."
|
||||
new "## Espacio adicional para añadir entre las entradas de la pantalla de historial."
|
||||
|
||||
|
||||
|
||||
@@ -2130,3 +2130,23 @@
|
||||
# game/updater.rpy:78
|
||||
old "Nightly Fix (Ren'Py 7, Python 2)"
|
||||
new "Correciones nocturnas (Ren'Py 7, Python 2)"
|
||||
|
||||
# game/choose_directory.rpy:72
|
||||
old "No directory was selected, but one is required."
|
||||
new "No se seleccionó un directorio, pero uno requerido."
|
||||
|
||||
# game/choose_directory.rpy:79
|
||||
old "The selected directory does not exist."
|
||||
new "El directorio seleccionado no existe."
|
||||
|
||||
# game/choose_directory.rpy:81
|
||||
old "The selected directory is not writable."
|
||||
new "No se puede escribir en el directorio seleccionado."
|
||||
|
||||
# game/distribute.rpy:554
|
||||
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()
|
||||
|
||||
|
||||
@@ -204,14 +204,28 @@ def path_to_saves(gamedir, save_directory=None): # type: (str, str|None) -> str
|
||||
# the launcher, usually.)
|
||||
def path_to_renpy_base():
|
||||
"""
|
||||
Returns the absolute path to thew Ren'Py base directory.
|
||||
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
|
||||
|
||||
def path_to_logdir(basedir):
|
||||
"""
|
||||
Returns the absolute path to the log directory.
|
||||
`basedir`
|
||||
The base directory (config.basedir)
|
||||
"""
|
||||
|
||||
import renpy # @UnresolvedImport
|
||||
|
||||
if renpy.android:
|
||||
return os.environ['ANDROID_PUBLIC']
|
||||
|
||||
return basedir
|
||||
|
||||
##############################################################################
|
||||
|
||||
|
||||
|
||||
+18
-19
@@ -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
|
||||
@@ -442,7 +439,7 @@ def import_all():
|
||||
global plog
|
||||
plog = renpy.performance.log # type:ignore
|
||||
|
||||
import renpy.styledata # @UnresolvedImport
|
||||
import renpy.styledata
|
||||
|
||||
import renpy.style
|
||||
renpy.styledata.import_style_functions()
|
||||
@@ -457,14 +454,15 @@ def import_all():
|
||||
import renpy.translation.extract
|
||||
import renpy.translation.merge
|
||||
|
||||
import renpy.display # @UnresolvedImport @Reimport
|
||||
import renpy.display
|
||||
|
||||
import renpy.display.presplash
|
||||
import renpy.display.pgrender
|
||||
import renpy.display.scale
|
||||
import renpy.display.module
|
||||
import renpy.display.render # Most display stuff depends on this. @UnresolvedImport
|
||||
import renpy.display.core # object @UnresolvedImport
|
||||
import renpy.display.render
|
||||
import renpy.display.displayable
|
||||
import renpy.display.core
|
||||
import renpy.display.swdraw
|
||||
|
||||
import renpy.text
|
||||
@@ -484,13 +482,13 @@ def import_all():
|
||||
import renpy.display.layout
|
||||
import renpy.display.viewport
|
||||
import renpy.display.transform
|
||||
import renpy.display.motion # layout @UnresolvedImport
|
||||
import renpy.display.behavior # layout @UnresolvedImport
|
||||
import renpy.display.transition # core, layout @UnresolvedImport
|
||||
import renpy.display.movetransition # core @UnresolvedImport
|
||||
import renpy.display.motion
|
||||
import renpy.display.behavior
|
||||
import renpy.display.transition
|
||||
import renpy.display.movetransition
|
||||
import renpy.display.im
|
||||
import renpy.display.imagelike
|
||||
import renpy.display.image # core, behavior, im, imagelike @UnresolvedImport
|
||||
import renpy.display.image
|
||||
import renpy.display.video
|
||||
import renpy.display.focus
|
||||
import renpy.display.anim
|
||||
@@ -536,7 +534,7 @@ def import_all():
|
||||
import renpy.memory
|
||||
|
||||
import renpy.exports
|
||||
import renpy.character # depends on exports. @UnresolvedImport
|
||||
import renpy.character
|
||||
|
||||
import renpy.add_from
|
||||
import renpy.dump
|
||||
@@ -549,8 +547,8 @@ def import_all():
|
||||
import renpy.gl2.gl2texture
|
||||
import renpy.gl2.live2d
|
||||
|
||||
import renpy.minstore # depends on lots. @UnresolvedImport
|
||||
import renpy.defaultstore # depends on everything. @UnresolvedImport
|
||||
import renpy.minstore
|
||||
import renpy.defaultstore
|
||||
|
||||
import renpy.test
|
||||
import renpy.test.testmouse
|
||||
@@ -570,8 +568,7 @@ def import_all():
|
||||
|
||||
global backup
|
||||
|
||||
if not mobile:
|
||||
backup = Backup()
|
||||
backup = Backup()
|
||||
|
||||
post_import()
|
||||
|
||||
@@ -617,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
|
||||
|
||||
@@ -699,6 +696,7 @@ if 1 == 0:
|
||||
from . import dump
|
||||
from . import easy
|
||||
from . import editor
|
||||
from . import encryption
|
||||
from . import error
|
||||
from . import execution
|
||||
from . import exports
|
||||
@@ -743,5 +741,6 @@ if 1 == 0:
|
||||
from . import ui
|
||||
from . import util
|
||||
from . import vc_version
|
||||
from . import versions
|
||||
from . import warp
|
||||
from . import webloader
|
||||
|
||||
+4
-11
@@ -151,20 +151,13 @@ class ArgumentParser(argparse.ArgumentParser):
|
||||
else:
|
||||
self.group.add_argument(*args, **kwargs)
|
||||
|
||||
def parse_args(self, *args, **kwargs):
|
||||
rv = argparse.ArgumentParser.parse_args(self, *args, **kwargs)
|
||||
|
||||
if rv.command in compile_commands:
|
||||
rv.compile = True
|
||||
|
||||
if renpy.session.get("compile", False):
|
||||
rv.compile = True
|
||||
|
||||
return rv
|
||||
|
||||
def parse_known_args(self, *args, **kwargs):
|
||||
args, rest = argparse.ArgumentParser.parse_known_args(self, *args, **kwargs)
|
||||
|
||||
if renpy.session.get("_reload", False):
|
||||
args.compile = False
|
||||
|
||||
if args.command in compile_commands:
|
||||
args.compile = True
|
||||
|
||||
@@ -195,7 +188,7 @@ def run():
|
||||
|
||||
args = renpy.game.args = ap.parse_args()
|
||||
|
||||
if args.warp:
|
||||
if args.warp and not renpy.session.get("_reload", False):
|
||||
renpy.warp.warp_spec = args.warp
|
||||
|
||||
if args.profile_display: # @UndefinedVariable
|
||||
|
||||
+24
-2
@@ -1697,19 +1697,22 @@ class Call(Node):
|
||||
'label',
|
||||
'arguments',
|
||||
'expression',
|
||||
'global_label',
|
||||
]
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
self = Node.__new__(cls)
|
||||
self.arguments = None
|
||||
self.global_label = ""
|
||||
return self
|
||||
|
||||
def __init__(self, loc, label, expression, arguments):
|
||||
def __init__(self, loc, label, expression, arguments, global_label=""):
|
||||
|
||||
super(Call, self).__init__(loc)
|
||||
self.label = label
|
||||
self.expression = expression
|
||||
self.arguments = arguments
|
||||
self.global_label = global_label
|
||||
|
||||
def diff_info(self):
|
||||
return (Call, self.label, self.expression)
|
||||
@@ -1722,6 +1725,9 @@ class Call(Node):
|
||||
if self.expression:
|
||||
label = renpy.python.py_eval(label)
|
||||
|
||||
if isinstance(label, str) and label.startswith("."):
|
||||
label = self.global_label + label
|
||||
|
||||
rv = renpy.game.context().call(label, return_site=self.next.name)
|
||||
next_node(rv)
|
||||
renpy.game.context().abnormal = True
|
||||
@@ -1748,6 +1754,9 @@ class Call(Node):
|
||||
except Exception:
|
||||
return [ ]
|
||||
|
||||
if isinstance(label, str) and label.startswith("."):
|
||||
label = self.global_label + label
|
||||
|
||||
if not renpy.game.script.has_label(label):
|
||||
return [ ]
|
||||
|
||||
@@ -1966,13 +1975,20 @@ class Jump(Node):
|
||||
__slots__ = [
|
||||
'target',
|
||||
'expression',
|
||||
'global_label',
|
||||
]
|
||||
|
||||
def __init__(self, loc, target, expression):
|
||||
def __new__(cls, *args, **kwargs):
|
||||
self = Node.__new__(cls)
|
||||
self.global_label = ""
|
||||
return self
|
||||
|
||||
def __init__(self, loc, target, expression, global_label=""):
|
||||
super(Jump, self).__init__(loc)
|
||||
|
||||
self.target = target
|
||||
self.expression = expression
|
||||
self.global_label = global_label
|
||||
|
||||
def diff_info(self):
|
||||
return (Jump, self.target, self.expression)
|
||||
@@ -1989,6 +2005,9 @@ class Jump(Node):
|
||||
if self.expression:
|
||||
target = renpy.python.py_eval(target)
|
||||
|
||||
if isinstance(target, str) and target.startswith("."):
|
||||
target = self.global_label + target
|
||||
|
||||
rv = renpy.game.script.lookup(target)
|
||||
renpy.game.context().abnormal = True
|
||||
|
||||
@@ -2008,6 +2027,9 @@ class Jump(Node):
|
||||
except Exception:
|
||||
return [ ]
|
||||
|
||||
if isinstance(label, str) and label.startswith("."):
|
||||
label = self.global_label + label
|
||||
|
||||
if not renpy.game.script.has_label(label):
|
||||
return [ ]
|
||||
|
||||
|
||||
+49
-137
@@ -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):
|
||||
@@ -1382,20 +1294,17 @@ class Interpolation(Statement):
|
||||
setattr(newts, k, v)
|
||||
|
||||
if k == "angle":
|
||||
newts.last_angle = v
|
||||
has_angle = True
|
||||
|
||||
elif k == "radius":
|
||||
has_radius = True
|
||||
|
||||
elif k == "anchorangle":
|
||||
newts.last_anchorangle = v
|
||||
has_anchorangle = True
|
||||
|
||||
elif k == "anchorradius":
|
||||
has_anchorradius = True
|
||||
|
||||
|
||||
# Now, the things we change linearly are in the difference
|
||||
# between the new and old states.
|
||||
linear = trans.state.diff(newts)
|
||||
@@ -1478,14 +1387,29 @@ class Interpolation(Statement):
|
||||
else:
|
||||
|
||||
if has_angle:
|
||||
last_angle = trans.state.angle or trans.state.last_angle
|
||||
angle = (last_angle, newts.last_angle)
|
||||
start = trans.state.angle
|
||||
end = newts.last_angle
|
||||
|
||||
if end - start > 180:
|
||||
start += 360
|
||||
if end - start < -180:
|
||||
start -= 360
|
||||
|
||||
angle = (start, end)
|
||||
|
||||
if has_radius:
|
||||
radius = (trans.state.radius, newts.radius)
|
||||
|
||||
if has_anchorangle:
|
||||
last_anchorangle = trans.state.anchorangle or trans.state.last_anchorangle
|
||||
start = trans.state.anchorangle
|
||||
end = newts.last_anchorangle
|
||||
|
||||
if end - start > 180:
|
||||
start += 360
|
||||
if end - start < -180:
|
||||
start -= 360
|
||||
|
||||
anchorangle = (start, end)
|
||||
|
||||
if has_anchorradius:
|
||||
anchorradius = (trans.state.anchorradius, newts.anchorradius)
|
||||
@@ -1583,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):
|
||||
@@ -1623,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):
|
||||
@@ -1711,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):
|
||||
|
||||
@@ -1789,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):
|
||||
|
||||
@@ -1839,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):
|
||||
@@ -1956,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):
|
||||
|
||||
@@ -1985,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):
|
||||
|
||||
@@ -2299,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
|
||||
|
||||
@@ -1,217 +0,0 @@
|
||||
# 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
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
from __future__ import division, absolute_import, with_statement, print_function, unicode_literals
|
||||
from renpy.compat import PY2, basestring, bchr, bord, chr, open, pystr, range, round, str, tobytes, unicode # *
|
||||
|
||||
|
||||
|
||||
import renpy
|
||||
import jnius # type: ignore
|
||||
|
||||
from renpy.audio.audio import MusicContext
|
||||
|
||||
VideoPlayer = jnius.autoclass("org.renpy.android.VideoPlayer")
|
||||
|
||||
|
||||
class AndroidVideoChannel(object):
|
||||
|
||||
def __init__(self, name, file_prefix="", file_suffix="", default_loop=None):
|
||||
|
||||
# A list of queued filenames.
|
||||
self.queue = [ ]
|
||||
|
||||
# The filename that's currently playing.
|
||||
self.filename = None
|
||||
|
||||
# The videoplayer that's currently playing.
|
||||
self.player = None
|
||||
|
||||
# The name assigned to this channel. This is used to look up
|
||||
# information about the channel in the MusicContext object.
|
||||
self.name = name
|
||||
|
||||
# The name of the mixer this channel uses. Set below, as there's
|
||||
# no good default.
|
||||
self.mixer = None
|
||||
|
||||
# The time the music in this channel was last changed.
|
||||
self.last_changed = 0
|
||||
|
||||
# The callback that is called if the queue becomes empty.
|
||||
self.callback = None
|
||||
|
||||
# Ignored.
|
||||
self.synchro_start = False
|
||||
self.wait_stop = False
|
||||
self.loop = [ ]
|
||||
|
||||
# A prefix and suffix that are used to create the full filenames.
|
||||
self.file_prefix = file_prefix
|
||||
self.file_suffix = file_suffix
|
||||
|
||||
if default_loop is None:
|
||||
# By default, should we loop the music?
|
||||
self.default_loop = True
|
||||
# Was this set explicitly?
|
||||
self.default_loop_set = False
|
||||
|
||||
else:
|
||||
self.default_loop = default_loop
|
||||
self.default_loop_set = True
|
||||
|
||||
def get_context(self):
|
||||
"""
|
||||
Returns the MusicContext corresponding to this channel, taken from
|
||||
the context object. Allocates a MusicContext if none exists.
|
||||
"""
|
||||
|
||||
mcd = renpy.game.context().music
|
||||
|
||||
rv = mcd.get(self.name)
|
||||
if rv is None:
|
||||
rv = mcd[self.name] = MusicContext()
|
||||
|
||||
return rv
|
||||
|
||||
context = property(get_context)
|
||||
|
||||
def copy_context(self):
|
||||
"""
|
||||
Copies the MusicContext associated with this channel, updates the
|
||||
ExecutionContext to point to the copy, and returns the copy.
|
||||
"""
|
||||
|
||||
mcd = renpy.game.context().music
|
||||
|
||||
ctx = self.get_context().copy()
|
||||
mcd[self.name] = ctx
|
||||
return ctx
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Starts playing the first video in the queue.
|
||||
"""
|
||||
|
||||
if not self.queue:
|
||||
return
|
||||
|
||||
filename = self.queue.pop(0)
|
||||
|
||||
with renpy.loader.load(filename, directory="audio") as f:
|
||||
real_fn = f.name
|
||||
base = getattr(f, "base", -1)
|
||||
length = getattr(f, "length", -1)
|
||||
|
||||
self.filename = filename
|
||||
self.player = VideoPlayer(real_fn, base, length)
|
||||
|
||||
def stop(self):
|
||||
|
||||
if self.player is not None:
|
||||
self.player.stop()
|
||||
self.player = None
|
||||
|
||||
self.filename = None
|
||||
|
||||
def get_playing(self):
|
||||
|
||||
if self.player is None:
|
||||
return None
|
||||
|
||||
if self.player.isPlaying():
|
||||
return self.filename
|
||||
|
||||
def periodic(self):
|
||||
|
||||
# This should be set from something that checks to see if our
|
||||
# mixer is muted.
|
||||
force_stop = self.context.force_stop
|
||||
|
||||
if force_stop:
|
||||
self.dequeue()
|
||||
self.stop()
|
||||
return
|
||||
|
||||
if self.get_playing():
|
||||
return
|
||||
|
||||
if self.queue:
|
||||
self.start()
|
||||
|
||||
def dequeue(self, even_tight=False):
|
||||
"""
|
||||
Clears the queued music, except for a first item that has
|
||||
not been started.
|
||||
"""
|
||||
|
||||
if self.get_playing():
|
||||
self.queue = [ ]
|
||||
else:
|
||||
self.queue = self.queue[:1]
|
||||
|
||||
def interact(self):
|
||||
"""
|
||||
Called (mostly) once per interaction.
|
||||
"""
|
||||
|
||||
self.periodic()
|
||||
|
||||
def fadeout(self, secs):
|
||||
"""
|
||||
Causes the playing music to be faded out for the given number
|
||||
of seconds. Also clears any queued music.
|
||||
"""
|
||||
|
||||
self.stop()
|
||||
self.queue = [ ]
|
||||
|
||||
def enqueue(self, filenames, loop=True, synchro_start=False, fadein=0, tight=None, relative_volume=1.0):
|
||||
self.queue.extend(filenames)
|
||||
|
||||
def set_volume(self, volume):
|
||||
pass
|
||||
|
||||
def get_pos(self):
|
||||
pass
|
||||
|
||||
def set_pan(self, pan, delay):
|
||||
pass
|
||||
|
||||
def set_secondary_volume(self, volume, delay):
|
||||
pass
|
||||
|
||||
def pause(self):
|
||||
if self.player is not None:
|
||||
self.player.pause()
|
||||
|
||||
def unpause(self):
|
||||
if self.player is not None:
|
||||
self.player.unpause()
|
||||
|
||||
def reload(self):
|
||||
return
|
||||
|
||||
def read_video(self):
|
||||
return None
|
||||
|
||||
def video_ready(self):
|
||||
return 1
|
||||
+17
-34
@@ -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)
|
||||
@@ -790,20 +792,6 @@ class Channel(object):
|
||||
return renpysound.video_ready(self.number)
|
||||
|
||||
|
||||
# Use unconditional imports so these files get compiled during the build
|
||||
# process.
|
||||
|
||||
|
||||
try:
|
||||
from renpy.audio.androidhw import AndroidVideoChannel
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
from renpy.audio.ioshw import IOSVideoChannel
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# A list of channels we know about.
|
||||
all_channels = [ ]
|
||||
|
||||
@@ -880,12 +868,7 @@ def register_channel(name,
|
||||
if not force and not renpy.game.context().init_phase and (" " not in name):
|
||||
raise Exception("Can't register channel outside of init phase.")
|
||||
|
||||
if renpy.android and renpy.config.hw_video and name == "movie":
|
||||
c = AndroidVideoChannel(name, default_loop=loop, file_prefix=file_prefix, file_suffix=file_suffix)
|
||||
elif renpy.ios and renpy.config.hw_video and name == "movie":
|
||||
c = IOSVideoChannel(name, default_loop=loop, file_prefix=file_prefix, file_suffix=file_suffix)
|
||||
else:
|
||||
c = Channel(name, loop, stop_on_mute, tight, file_prefix, file_suffix, buffer_queue, movie=movie, framedrop=framedrop)
|
||||
c = Channel(name, loop, stop_on_mute, tight, file_prefix, file_suffix, buffer_queue, movie=movie, framedrop=framedrop)
|
||||
|
||||
c.mixer = mixer
|
||||
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
from __future__ import division, absolute_import, with_statement, print_function, unicode_literals
|
||||
from renpy.compat import PY2, basestring, bchr, bord, chr, open, pystr, range, round, str, tobytes, unicode # *
|
||||
|
||||
|
||||
|
||||
import renpy
|
||||
import pyobjus # type: ignore
|
||||
|
||||
from renpy.audio.audio import MusicContext
|
||||
|
||||
VideoPlayer = pyobjus.autoclass("VideoPlayer")
|
||||
|
||||
|
||||
class IOSVideoChannel(object):
|
||||
|
||||
def __init__(self, name, file_prefix="", file_suffix="", default_loop=None):
|
||||
|
||||
# A list of queued filenames.
|
||||
self.queue = [ ]
|
||||
|
||||
# The filename that's currently playing.
|
||||
self.filename = None
|
||||
|
||||
# The videoplayer that's currently playing.
|
||||
self.player = None
|
||||
|
||||
# The name assigned to this channel. This is used to look up
|
||||
# information about the channel in the MusicContext object.
|
||||
self.name = name
|
||||
|
||||
# The name of the mixer this channel uses. Set below, as there's
|
||||
# no good default.
|
||||
self.mixer = None
|
||||
|
||||
# The time the music in this channel was last changed.
|
||||
self.last_changed = 0
|
||||
|
||||
# The callback that is called if the queue becomes empty.
|
||||
self.callback = None
|
||||
|
||||
# Ignored.
|
||||
self.synchro_start = False
|
||||
self.wait_stop = False
|
||||
self.loop = [ ]
|
||||
|
||||
# A prefix and suffix that are used to create the full filenames.
|
||||
self.file_prefix = file_prefix
|
||||
self.file_suffix = file_suffix
|
||||
|
||||
if default_loop is None:
|
||||
# By default, should we loop the music?
|
||||
self.default_loop = True
|
||||
# Was this set explicitly?
|
||||
self.default_loop_set = False
|
||||
|
||||
else:
|
||||
self.default_loop = default_loop
|
||||
self.default_loop_set = True
|
||||
|
||||
def get_context(self):
|
||||
"""
|
||||
Returns the MusicContext corresponding to this channel, taken from
|
||||
the context object. Allocates a MusicContext if none exists.
|
||||
"""
|
||||
|
||||
mcd = renpy.game.context().music
|
||||
|
||||
rv = mcd.get(self.name)
|
||||
if rv is None:
|
||||
rv = mcd[self.name] = MusicContext()
|
||||
|
||||
return rv
|
||||
|
||||
context = property(get_context)
|
||||
|
||||
def copy_context(self):
|
||||
"""
|
||||
Copies the MusicContext associated with this channel, updates the
|
||||
ExecutionContext to point to the copy, and returns the copy.
|
||||
"""
|
||||
|
||||
mcd = renpy.game.context().music
|
||||
|
||||
ctx = self.get_context().copy()
|
||||
mcd[self.name] = ctx
|
||||
return ctx
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Starts playing the first video in the queue.
|
||||
"""
|
||||
|
||||
if not self.queue:
|
||||
return
|
||||
|
||||
filename = self.queue.pop(0)
|
||||
with renpy.loader.load(filename, directory="audio") as f:
|
||||
real_fn = f.name
|
||||
|
||||
self.filename = filename
|
||||
self.player = VideoPlayer.alloc().initWithFile_(real_fn)
|
||||
|
||||
def stop(self):
|
||||
|
||||
if self.player is not None:
|
||||
self.player.stop()
|
||||
self.player = None
|
||||
|
||||
self.filename = None
|
||||
|
||||
def get_playing(self):
|
||||
|
||||
if self.player is None:
|
||||
return None
|
||||
|
||||
if self.player.isPlaying():
|
||||
return self.filename
|
||||
|
||||
def periodic(self):
|
||||
|
||||
# This should be set from something that checks to see if our
|
||||
# mixer is muted.
|
||||
force_stop = self.context.force_stop
|
||||
|
||||
if force_stop:
|
||||
self.dequeue()
|
||||
self.stop()
|
||||
return
|
||||
|
||||
if self.player:
|
||||
self.player.periodic()
|
||||
|
||||
if self.get_playing():
|
||||
return
|
||||
|
||||
if self.queue:
|
||||
self.start()
|
||||
|
||||
def dequeue(self, even_tight=False):
|
||||
"""
|
||||
Clears the queued music, except for a first item that has
|
||||
not been started.
|
||||
"""
|
||||
|
||||
if self.get_playing():
|
||||
self.queue = [ ]
|
||||
else:
|
||||
self.queue = self.queue[:1]
|
||||
|
||||
def interact(self):
|
||||
"""
|
||||
Called (mostly) once per interaction.
|
||||
"""
|
||||
|
||||
self.periodic()
|
||||
|
||||
def fadeout(self, secs):
|
||||
"""
|
||||
Causes the playing music to be faded out for the given number
|
||||
of seconds. Also clears any queued music.
|
||||
"""
|
||||
|
||||
self.stop()
|
||||
self.queue = [ ]
|
||||
|
||||
def enqueue(self, filenames, loop=True, synchro_start=False, fadein=0, tight=None, relative_volume=1.0):
|
||||
self.queue.extend(filenames)
|
||||
|
||||
def pause(self):
|
||||
if self.player is not None:
|
||||
self.player.pause()
|
||||
|
||||
def unpause(self):
|
||||
if self.player is not None:
|
||||
self.player.unpause()
|
||||
|
||||
def set_volume(self, volume):
|
||||
pass
|
||||
|
||||
def get_pos(self):
|
||||
pass
|
||||
|
||||
def set_pan(self, pan, delay):
|
||||
pass
|
||||
|
||||
def set_secondary_volume(self, volume, delay):
|
||||
pass
|
||||
|
||||
def reload(self):
|
||||
return
|
||||
|
||||
def read_video(self):
|
||||
return None
|
||||
|
||||
def video_ready(self):
|
||||
return 1
|
||||
@@ -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
|
||||
|
||||
+80
-5
@@ -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"):
|
||||
@@ -264,10 +342,7 @@ You may be using a system install of python. Please run {0}.sh,
|
||||
renpy.config.gamedir = gamedir
|
||||
renpy.config.args = [ ] # type: ignore
|
||||
|
||||
if renpy.android:
|
||||
renpy.config.logdir = os.environ['ANDROID_PUBLIC']
|
||||
else:
|
||||
renpy.config.logdir = basedir
|
||||
renpy.config.logdir = renpy.__main__.path_to_logdir(basedir)
|
||||
|
||||
if not os.path.exists(renpy.config.logdir):
|
||||
os.makedirs(renpy.config.logdir, 0o777)
|
||||
|
||||
@@ -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.
|
||||
"""
|
||||
|
||||
|
||||
@@ -698,10 +700,8 @@ init -1500 python:
|
||||
amount = delta * adjustment.step
|
||||
elif self.amount == "page":
|
||||
amount = delta * adjustment.page
|
||||
elif isinstance(self.amount, float) and not isinstance(self.amount, absolute):
|
||||
amount = delta * self.amount * adjustment.range
|
||||
else:
|
||||
amount = delta * self.amount
|
||||
amount = absolute.compute_raw(delta*self.amount, adjustment.range)
|
||||
|
||||
if self.delay == 0.0:
|
||||
adjustment.change(adjustment.value + amount)
|
||||
@@ -876,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):
|
||||
|
||||
+22
-12
@@ -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),
|
||||
@@ -345,11 +347,14 @@ init -1500 python in build:
|
||||
dmg
|
||||
A Macintosh DMG containing the files.
|
||||
app-zip
|
||||
A zip file containing a macintosh application.
|
||||
A zip file containing a macintosh application. This format
|
||||
doesn't support the Ren'Py updater.
|
||||
app-directory
|
||||
A directory containing the mac app.
|
||||
A directory containing the mac app. This format
|
||||
doesn't support the Ren'Py updater.
|
||||
app-dmg
|
||||
A macintosh drive image containing a dmg. (Mac only.)
|
||||
A macintosh drive image containing a dmg. (Mac only.) This format
|
||||
doesn't support the Ren'Py updater.
|
||||
bare-zip
|
||||
A zip file without :var:`build.directory_name`
|
||||
prepended.
|
||||
@@ -457,15 +462,15 @@ init -1500 python in build:
|
||||
itch_project = None
|
||||
|
||||
# Maps from files to itch.io channels.
|
||||
itch_channels = [
|
||||
( "*-all.zip", "win-osx-linux" ),
|
||||
( "*-market.zip", "win-osx-linux" ),
|
||||
( "*-pc.zip", "win-linux" ),
|
||||
( "*-win.zip", "win" ),
|
||||
( "*-mac.zip", "osx" ),
|
||||
( "*-linux.tar.bz2", "linux" ),
|
||||
( "*-release.apk", "android" ),
|
||||
]
|
||||
itch_channels = {
|
||||
"*-all.zip" : "win-osx-linux",
|
||||
"*-market.zip" : "win-osx-linux",
|
||||
"*-pc.zip" : "win-linux",
|
||||
"*-win.zip" : "win",
|
||||
"*-mac.zip" : "osx",
|
||||
"*-linux.tar.bz2" : "linux",
|
||||
"*-release.apk" : "android",
|
||||
}
|
||||
|
||||
# Should we include the old Ren'Py themes?
|
||||
include_old_themes = True
|
||||
@@ -503,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():
|
||||
@@ -586,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:
|
||||
|
||||
@@ -208,7 +208,6 @@ init -1100 python:
|
||||
if version <= (7, 3, 5):
|
||||
config.side_image_requires_attributes = False
|
||||
config.window_functions_set_auto = False
|
||||
config.hw_video = True
|
||||
config.who_what_sub_compat = 0
|
||||
|
||||
if version <= (7, 4, 0):
|
||||
@@ -233,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
|
||||
@@ -293,8 +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()
|
||||
|
||||
|
||||
|
||||
@@ -111,6 +111,13 @@ init -1500 python in iap:
|
||||
|
||||
return
|
||||
|
||||
def request_review(self):
|
||||
"""
|
||||
Called to request that the user review the application.
|
||||
"""
|
||||
|
||||
return False
|
||||
|
||||
class AndroidBackend(object):
|
||||
"""
|
||||
The IAP backend that is used when IAP is supported.
|
||||
@@ -181,6 +188,9 @@ init -1500 python in iap:
|
||||
identifier = self.identifier(p)
|
||||
return self.store.getPrice(identifier)
|
||||
|
||||
def request_review(self):
|
||||
return self.store.requestReview()
|
||||
|
||||
def init(self):
|
||||
restore(False)
|
||||
|
||||
@@ -293,6 +303,10 @@ init -1500 python in iap:
|
||||
|
||||
return rv
|
||||
|
||||
def request_review(self):
|
||||
self.helper.requestReview()
|
||||
return True
|
||||
|
||||
def init(self):
|
||||
self.helper.validateProductIdentifiersInBackground()
|
||||
|
||||
@@ -541,6 +555,21 @@ init -1500 python in iap:
|
||||
|
||||
return backend.get_store_name()
|
||||
|
||||
def request_review():
|
||||
"""
|
||||
:doc: iap
|
||||
|
||||
When called, the app store is asked to request a review from the user.
|
||||
This returns true if the request was successful, and false if the
|
||||
request was not. Note that a successful request does not mean that
|
||||
the user will be asked to review the app, as app stores determine
|
||||
if the user is actually asked.
|
||||
|
||||
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),
|
||||
)
|
||||
|
||||
@@ -15,7 +15,7 @@ python early in layeredimage:
|
||||
FIXED_PROPERTIES = renpy.sl2.slproperties.position_property_names + renpy.sl2.slproperties.box_property_names
|
||||
|
||||
# This is the default value for predict_all given to conditions.
|
||||
predict_all = False
|
||||
predict_all = None
|
||||
|
||||
def format_function(what, name, group, variant, attribute, image, image_format, **kwargs):
|
||||
"""
|
||||
@@ -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)
|
||||
|
||||
@@ -272,7 +272,11 @@ init -1500 python:
|
||||
if value == "fullscreen":
|
||||
return SetField(_preferences, "fullscreen", True)
|
||||
elif value == "window":
|
||||
return __DisplayAction(1.0)
|
||||
if renpy.variant("web"):
|
||||
# Only fullscreen and non-fullscreen modes for Web
|
||||
return SetField(_preferences, "fullscreen", False)
|
||||
else:
|
||||
return __DisplayAction(1.0)
|
||||
elif value == "any window":
|
||||
return SetField(_preferences, "fullscreen", False)
|
||||
elif value == "toggle":
|
||||
|
||||
@@ -61,6 +61,10 @@ init -1150 python in bubble:
|
||||
# The property group names, in order.
|
||||
properties_order = [ ]
|
||||
|
||||
# If not None, a function that takes the character's image tag, and returns
|
||||
# the list of property names that are allowed for that image tag.
|
||||
properties_callback = None
|
||||
|
||||
# A map from property name to the (left, top, right, bottom) number of pixels
|
||||
# areas with that property are expanded by. If a property is not in this
|
||||
# map, None is tried.
|
||||
@@ -154,11 +158,19 @@ init -1150 python in bubble:
|
||||
int(default_area[3] * ygrid)
|
||||
]
|
||||
|
||||
return {
|
||||
rv = {
|
||||
"area" : default_area_rect,
|
||||
"properties" : properties_order[0]
|
||||
}
|
||||
|
||||
|
||||
if properties_callback is not None:
|
||||
rv["properties"] = properties_callback(image_tag)[0]
|
||||
else:
|
||||
rv["properties"] = properties_order[0]
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
def expand_area(self, area, properties_key):
|
||||
"""
|
||||
This is called to expand the area of a bubble. It is given the
|
||||
@@ -234,14 +246,19 @@ init -1150 python in bubble:
|
||||
|
||||
current = tag_properties[self.image_tag]["properties"]
|
||||
|
||||
if properties_callback is not None:
|
||||
properties = properties_callback(self.image_tag)
|
||||
else:
|
||||
properties = properties_order
|
||||
|
||||
try:
|
||||
idx = properties_order.index(current)
|
||||
idx = properties.index(current)
|
||||
except ValueError:
|
||||
idx = 0
|
||||
|
||||
idx = (idx + 1) % len(properties_order)
|
||||
idx = (idx + 1) % len(properties)
|
||||
|
||||
db[self.tlid]["properties"] = properties_order[idx]
|
||||
db[self.tlid]["properties"] = properties[idx]
|
||||
renpy.rollback(checkpoints=0, force=True, greedy=True)
|
||||
|
||||
def alternate(self):
|
||||
@@ -346,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
|
||||
|
||||
@@ -24,8 +24,6 @@ init -1500 python:
|
||||
self.points = []
|
||||
|
||||
for p in points:
|
||||
length = len(p)
|
||||
|
||||
if isinstance(p[-1], float):
|
||||
length = len(p) - 1
|
||||
point = [ p[-1] ]
|
||||
@@ -62,11 +60,7 @@ init -1500 python:
|
||||
self.initialized = None
|
||||
|
||||
def init_values(self, sizes):
|
||||
def to_abs_(value, size):
|
||||
if isinstance(value, float):
|
||||
return value * size
|
||||
else:
|
||||
return value
|
||||
to_abs_ = absolute.compute_raw
|
||||
|
||||
def coord_(c):
|
||||
|
||||
|
||||
@@ -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,
|
||||
@@ -874,7 +887,7 @@ init -1499 python in achievement:
|
||||
renpy.write_log("Initialized steam.")
|
||||
|
||||
except Exception as e:
|
||||
renpy.write_log("Faled to initialize steam: %r", e)
|
||||
renpy.write_log("Failed to initialize steam: %r", e)
|
||||
steam = None
|
||||
steamapi = None
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -205,6 +205,5 @@ if PY2:
|
||||
|
||||
# Generated by scripts/relative_imports.py, do not edit below this line.
|
||||
if 1 == 0:
|
||||
from . import dictviews
|
||||
from . import fixes
|
||||
from . import pickle
|
||||
|
||||
+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
|
||||
|
||||
@@ -72,6 +72,7 @@ if 1 == 0:
|
||||
from . import behavior
|
||||
from . import controller
|
||||
from . import core
|
||||
from . import displayable
|
||||
from . import dragdrop
|
||||
from . import emulator
|
||||
from . import error
|
||||
@@ -93,6 +94,7 @@ if 1 == 0:
|
||||
from . import pgrender
|
||||
from . import predict
|
||||
from . import presplash
|
||||
from . import quaternion
|
||||
from . import render
|
||||
from . import scale
|
||||
from . import screen
|
||||
|
||||
@@ -94,7 +94,7 @@ def get_poi(state):
|
||||
height = renpy.config.screen_height
|
||||
|
||||
placement = (d.xpos, d.ypos, d.xanchor, d.yanchor, d.xoffset, d.yoffset, True)
|
||||
xplacement, yplacement = renpy.display.core.place(width, height, width, height, placement)
|
||||
xplacement, yplacement = renpy.display.displayable.place(width, height, width, height, placement)
|
||||
|
||||
return (xplacement + width / 2, yplacement + height / 2, d.zpos + z11)
|
||||
|
||||
@@ -392,13 +392,13 @@ cdef class RenderTransform:
|
||||
ysize = state.ysize
|
||||
|
||||
if xsize is not None:
|
||||
if (type(xsize) is float) and renpy.config.relative_transform_size:
|
||||
xsize *= self.widtho
|
||||
if renpy.config.relative_transform_size:
|
||||
xsize = absolute.compute_raw(xsize, self.widtho)
|
||||
self.widtho = xsize
|
||||
|
||||
if ysize is not None:
|
||||
if (type(ysize) is float) and renpy.config.relative_transform_size:
|
||||
ysize *= self.heighto
|
||||
if renpy.config.relative_transform_size:
|
||||
ysize = absolute.compute_raw(ysize, self.heighto)
|
||||
self.heighto = ysize
|
||||
|
||||
self.cr = render(child, self.widtho, self.heighto, st - self.transform.child_st_base, at)
|
||||
@@ -644,7 +644,7 @@ cdef class RenderTransform:
|
||||
zrotate = state.zrotate or 0
|
||||
|
||||
placement = (state.xpos, state.ypos, state.xanchor, state.yanchor, state.xoffset, state.yoffset, True)
|
||||
xplacement, yplacement = renpy.display.core.place(width, height, width, height, placement)
|
||||
xplacement, yplacement = renpy.display.displayable.place(width, height, width, height, placement)
|
||||
|
||||
if poi:
|
||||
start_pos = (xplacement + width / 2, yplacement + height / 2, state.zpos + z11)
|
||||
@@ -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
|
||||
@@ -753,18 +753,13 @@ cdef class RenderTransform:
|
||||
manchory = height / 2.0
|
||||
|
||||
else:
|
||||
manchorx, manchory = state.matrixanchor
|
||||
|
||||
if type(manchorx) is float:
|
||||
manchorx *= width
|
||||
if type(manchory) is float:
|
||||
manchory *= height
|
||||
manchorx, manchory = map(absolute.compute_raw, state.matrixanchor, (width, height))
|
||||
|
||||
m = Matrix.offset(-manchorx, -manchory, 0.0)
|
||||
|
||||
if poi:
|
||||
placement = self.transform.get_placement()
|
||||
xplacement, yplacement = renpy.display.core.place(self.widtho, self.heighto, width, height, placement)
|
||||
xplacement, yplacement = renpy.display.displayable.place(self.widtho, self.heighto, width, height, placement)
|
||||
start_pos = (xplacement + manchorx, yplacement + manchory, state.zpos)
|
||||
|
||||
a, b, c = ( float(e - s) for s, e in zip(start_pos, poi) )
|
||||
@@ -833,12 +828,7 @@ cdef class RenderTransform:
|
||||
manchory = self.height / 2.0
|
||||
|
||||
else:
|
||||
manchorx, manchory = state.matrixanchor
|
||||
|
||||
if type(manchorx) is float:
|
||||
manchorx *= self.width
|
||||
if type(manchory) is float:
|
||||
manchory *= self.height
|
||||
manchorx, manchory = map(absolute.compute_raw, state.matrixanchor, (self.width, self.height))
|
||||
|
||||
m = Matrix.offset(-manchorx, -manchory, 0.0)
|
||||
m = mt * m
|
||||
|
||||
@@ -55,7 +55,7 @@ class State(object):
|
||||
Position object, that modifies the position of the image.
|
||||
"""
|
||||
|
||||
if image and not isinstance(image, renpy.display.core.Displayable):
|
||||
if image and not isinstance(image, renpy.display.displayable.Displayable):
|
||||
image = renpy.easy.displayable(image)
|
||||
|
||||
self.name = name
|
||||
@@ -125,7 +125,7 @@ class Edge(object):
|
||||
sma.edges.setdefault(self.old, []).append(self)
|
||||
|
||||
|
||||
class SMAnimation(renpy.display.core.Displayable):
|
||||
class SMAnimation(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
This creates a state-machine animation. Such an animation is
|
||||
created by randomly traversing the edges between states in a
|
||||
@@ -308,7 +308,7 @@ class SMAnimation(renpy.display.core.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):
|
||||
@@ -322,7 +322,7 @@ def Animation(*args, **kwargs):
|
||||
return TransitionAnimation(*newargs, **kwargs)
|
||||
|
||||
|
||||
class TransitionAnimation(renpy.display.core.Displayable):
|
||||
class TransitionAnimation(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
A displayable that draws an animation with each frame separated
|
||||
by a transition.
|
||||
@@ -409,7 +409,7 @@ class TransitionAnimation(renpy.display.core.Displayable):
|
||||
return self.images
|
||||
|
||||
|
||||
class Blink(renpy.display.core.Displayable):
|
||||
class Blink(renpy.display.displayable.Displayable):
|
||||
|
||||
def __init__(self, image, on=0.5, off=0.5, rise=0.5, set=0.5, # @ReservedAssignment
|
||||
high=1.0, low=0.0, offset=0.0, anim_timebase=False, **properties):
|
||||
|
||||
@@ -799,7 +799,7 @@ class SayBehavior(renpy.display.layout.Null):
|
||||
return None
|
||||
|
||||
|
||||
class DismissBehavior(renpy.display.core.Displayable):
|
||||
class DismissBehavior(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
This is used to implement the dismiss screen language statement.
|
||||
"""
|
||||
@@ -1313,7 +1313,7 @@ def input_post_per_interact():
|
||||
i.caret_pos = len(content)
|
||||
|
||||
|
||||
class CaretBlink(renpy.display.core.Displayable):
|
||||
class CaretBlink(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
A displayable that renders the caret.
|
||||
"""
|
||||
@@ -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:
|
||||
@@ -2062,7 +2075,7 @@ class Adjustment(renpy.object.Object):
|
||||
return 0
|
||||
|
||||
|
||||
class Bar(renpy.display.core.Displayable):
|
||||
class Bar(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
Implements a bar that can display an integer value, and respond
|
||||
to clicks on that value.
|
||||
@@ -2114,7 +2127,9 @@ class Bar(renpy.display.core.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:
|
||||
@@ -2582,7 +2597,7 @@ class Timer(renpy.display.layout.Null):
|
||||
return run(self.function, *self.args, **self.kwargs)
|
||||
|
||||
|
||||
class MouseArea(renpy.display.core.Displayable):
|
||||
class MouseArea(renpy.display.displayable.Displayable):
|
||||
|
||||
# The offset between st and at.
|
||||
at_st_offset = 0
|
||||
@@ -2644,7 +2659,7 @@ class MouseArea(renpy.display.core.Displayable):
|
||||
run(self.unhovered)
|
||||
|
||||
|
||||
class OnEvent(renpy.display.core.Displayable):
|
||||
class OnEvent(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
This is a displayable that runs an action in response to a transform
|
||||
event. It's used to implement the screen language on statement.
|
||||
@@ -2836,7 +2851,7 @@ class AreaPicker(renpy.display.layout.Container):
|
||||
|
||||
|
||||
|
||||
class WebInput(renpy.display.core.Displayable):
|
||||
class WebInput(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
A displayable meant to pull input from an input tag in the web browser.
|
||||
"""
|
||||
|
||||
+29
-611
@@ -19,9 +19,6 @@
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
# This file contains code for initializing and managing the display
|
||||
# window.
|
||||
|
||||
from __future__ import division, absolute_import, with_statement, print_function, unicode_literals
|
||||
from renpy.compat import PY2, basestring, bchr, bord, chr, open, pystr, range, round, str, tobytes, unicode # *
|
||||
|
||||
@@ -39,6 +36,8 @@ import atexit
|
||||
import pygame_sdl2 as pygame
|
||||
import renpy
|
||||
|
||||
from renpy.display.displayable import Displayable, DisplayableArguments, place
|
||||
|
||||
import_time = time.time()
|
||||
|
||||
try:
|
||||
@@ -211,7 +210,7 @@ class absolute(float):
|
||||
This represents an absolute float coordinate.
|
||||
"""
|
||||
|
||||
slots = ()
|
||||
__slots__ = ()
|
||||
|
||||
def __repr__(self):
|
||||
return "absolute({})".format(float.__repr__(self))
|
||||
@@ -219,6 +218,28 @@ class absolute(float):
|
||||
def __divmod__(self, value):
|
||||
return self//value, self%value
|
||||
|
||||
def __rdivmod__(self, value):
|
||||
return value//self, value%self
|
||||
|
||||
@staticmethod
|
||||
def compute_raw(value, room):
|
||||
"""
|
||||
Converts a position from one of the many supported position types
|
||||
into an absolute number of pixels, without regard for the return type.
|
||||
"""
|
||||
if isinstance(value, (absolute, int)):
|
||||
return value
|
||||
elif isinstance(value, float):
|
||||
return value * room
|
||||
raise TypeError("Value {} of type {} not recognized as a position.".format(value, type(value)))
|
||||
|
||||
@staticmethod
|
||||
def compute(value, room):
|
||||
"""
|
||||
Does the same, but converts the result to the absolute type.
|
||||
"""
|
||||
return absolute(absolute.compute_raw(value, room))
|
||||
|
||||
for fn in (
|
||||
'__coerce__', # PY2
|
||||
'__div__', # PY2
|
||||
@@ -247,7 +268,7 @@ for fn in (
|
||||
'__pos__',
|
||||
'__pow__',
|
||||
'__radd__',
|
||||
'__rdivmod__',
|
||||
# '__rdivmod__', # special-cased above, tuple of floats
|
||||
'__rfloordiv__',
|
||||
'__rmod__',
|
||||
'__rmul__',
|
||||
@@ -273,603 +294,6 @@ for fn in (
|
||||
del absolute_wrap, fn, f # type: ignore
|
||||
|
||||
|
||||
def place(width, height, sw, sh, placement):
|
||||
"""
|
||||
Performs the Ren'Py placement algorithm.
|
||||
|
||||
`width`, `height`
|
||||
The width and height of the area the image will be
|
||||
placed in.
|
||||
|
||||
`size`
|
||||
The size of the image to be placed.
|
||||
|
||||
`placement`
|
||||
The tuple returned by Displayable.get_placement().
|
||||
"""
|
||||
|
||||
xpos, ypos, xanchor, yanchor, xoffset, yoffset, _subpixel = placement
|
||||
|
||||
if xpos is None:
|
||||
xpos = 0
|
||||
if ypos is None:
|
||||
ypos = 0
|
||||
if xanchor is None:
|
||||
xanchor = 0
|
||||
if yanchor is None:
|
||||
yanchor = 0
|
||||
if xoffset is None:
|
||||
xoffset = 0
|
||||
if yoffset is None:
|
||||
yoffset = 0
|
||||
|
||||
# We need to use type, since isinstance(absolute(0), float).
|
||||
if xpos.__class__ is float:
|
||||
xpos *= width
|
||||
|
||||
if xanchor.__class__ is float:
|
||||
xanchor *= sw
|
||||
|
||||
x = xpos + xoffset - xanchor
|
||||
|
||||
if ypos.__class__ is float:
|
||||
ypos *= height
|
||||
|
||||
if yanchor.__class__ is float:
|
||||
yanchor *= sh
|
||||
|
||||
y = ypos + yoffset - yanchor
|
||||
|
||||
return x, y
|
||||
|
||||
|
||||
class DisplayableArguments(renpy.object.Object):
|
||||
"""
|
||||
Represents a set of arguments that can be passed to a duplicated
|
||||
displayable.
|
||||
"""
|
||||
|
||||
# The name of the displayable without any arguments.
|
||||
name = () # type: Tuple
|
||||
|
||||
# Arguments supplied.
|
||||
args = () # type: Tuple
|
||||
|
||||
# The style prefix in play. This is used by DynamicImage to figure
|
||||
# out the prefix list to apply.
|
||||
prefix = None # Optional[str]
|
||||
|
||||
# True if lint is in use.
|
||||
lint = False
|
||||
|
||||
def copy(self, **kwargs):
|
||||
"""
|
||||
Returns a copy of this object with the various fields set to the
|
||||
values they were given in kwargs.
|
||||
"""
|
||||
|
||||
rv = DisplayableArguments()
|
||||
rv.__dict__.update(self.__dict__)
|
||||
rv.__dict__.update(kwargs)
|
||||
|
||||
return rv
|
||||
|
||||
def extraneous(self):
|
||||
if renpy.config.developer and renpy.config.report_extraneous_attributes:
|
||||
raise Exception("Image '{}' does not accept attributes '{}'.".format(
|
||||
" ".join(self.name),
|
||||
" ".join(self.args),
|
||||
))
|
||||
|
||||
|
||||
default_style = renpy.style.Style("default")
|
||||
|
||||
|
||||
class Displayable(renpy.object.Object):
|
||||
"""
|
||||
The base class for every object in Ren'Py that can be
|
||||
displayed to the screen.
|
||||
|
||||
Drawables will be serialized to a savegame file. Therefore, they
|
||||
shouldn't store non-serializable things (like pygame surfaces) in
|
||||
their fields.
|
||||
"""
|
||||
|
||||
# Some invariants about method call order:
|
||||
#
|
||||
# per_interact is called before render.
|
||||
# render is called before event.
|
||||
#
|
||||
# get_placement can be called at any time, so can't
|
||||
# assume anything.
|
||||
|
||||
# If True this displayable can accept focus.
|
||||
# If False, it can't, but it keeps its place in the focus order.
|
||||
# If None, it does not have a place in the focus order.
|
||||
focusable = None
|
||||
|
||||
# This is the focus named assigned by the focus code.
|
||||
full_focus_name = None
|
||||
|
||||
# A role ('selected_' or '' that prefixes the style).
|
||||
role = ''
|
||||
|
||||
# The event we'll pass on to our parent transform.
|
||||
transform_event = None
|
||||
|
||||
# Can we change our look in response to transform_events?
|
||||
transform_event_responder = False
|
||||
|
||||
# The main displayable, if this displayable is the root of a composite
|
||||
# displayable. (This is used by SL to figure out where to add children
|
||||
# to.) If None, it is itself.
|
||||
_main = None
|
||||
|
||||
# A list of the children that make up this composite displayable.
|
||||
_composite_parts = [ ]
|
||||
|
||||
# The location the displayable was created at, if known.
|
||||
_location = None
|
||||
|
||||
# Does this displayable use the scope?
|
||||
_uses_scope = False
|
||||
|
||||
# Arguments supplied to this displayable.
|
||||
_args = DisplayableArguments()
|
||||
|
||||
# Set to true of the displayable is duplicatable (has a non-trivial
|
||||
# duplicate method), or one of its children is.
|
||||
_duplicatable = False
|
||||
|
||||
# Does this displayable require clipping?
|
||||
_clipping = False
|
||||
|
||||
# Does this displayable have a tooltip?
|
||||
_tooltip = None
|
||||
|
||||
# Should hbox and vbox skip this displayable?
|
||||
_box_skip = False
|
||||
|
||||
# If not None, this should be a (width, height) tuple that overrides the
|
||||
# amount of space offered to the displayable.
|
||||
_offer_size = None
|
||||
|
||||
# If true, this displayable will be treated as draggable in its own right.
|
||||
# This is used by viewport to decide if a drag is meant for the viewport
|
||||
# or for its child.
|
||||
_draggable = False
|
||||
|
||||
# Used by a transition (or transition-like object) to determine how long to
|
||||
# delay for.
|
||||
delay = None # type: float|None
|
||||
|
||||
def __ne__(self, o):
|
||||
return not (self == o)
|
||||
|
||||
def __init__(self, focus=None, default=False, style='default', _args=None, tooltip=None, default_focus=False, **properties):
|
||||
|
||||
global default_style
|
||||
|
||||
if (style == "default") and (not properties):
|
||||
self.style = default_style
|
||||
else:
|
||||
self.style = renpy.style.Style(style, properties) # @UndefinedVariable
|
||||
|
||||
self.focus_name = focus
|
||||
self.default = default or default_focus
|
||||
self._tooltip = tooltip
|
||||
|
||||
if _args is not None:
|
||||
self._args = _args
|
||||
|
||||
def _copy(self, args=None):
|
||||
"""
|
||||
Makes a shallow copy of the displayable. If `args` is provided,
|
||||
replaces the arguments with the stored copy.
|
||||
"""
|
||||
|
||||
rv = copy.copy(self)
|
||||
|
||||
if args is not None:
|
||||
rv._args = args
|
||||
|
||||
return rv
|
||||
|
||||
def _duplicate(self, args):
|
||||
"""
|
||||
Makes a duplicate copy of the following kids of displayables:
|
||||
|
||||
* Displayables that can accept arguments.
|
||||
* Displayables that maintain state that should be reset before being
|
||||
shown to the user.
|
||||
* Containers that contain (including transitively) one of the other
|
||||
kinds of displayables.
|
||||
|
||||
Displayables that contain state that can be manipulated by the user
|
||||
are never copied.
|
||||
|
||||
This should call _unique on children that have been copied before
|
||||
setting its own _duplicatable flag.
|
||||
"""
|
||||
|
||||
if args and args.args:
|
||||
args.extraneous()
|
||||
|
||||
return self
|
||||
|
||||
def _get_tooltip(self):
|
||||
"""
|
||||
Returns the tooltip of this displayable.
|
||||
"""
|
||||
|
||||
return self._tooltip
|
||||
|
||||
def _in_current_store(self):
|
||||
"""
|
||||
Returns a version of this displayable that will not change as it is
|
||||
rendered.
|
||||
"""
|
||||
|
||||
return self
|
||||
|
||||
def _unique(self):
|
||||
"""
|
||||
This is called when a displayable is "unique", meaning there will
|
||||
only be one reference to it, ever, from the tree of displayables.
|
||||
"""
|
||||
|
||||
self._duplicatable = False
|
||||
|
||||
return
|
||||
|
||||
def parameterize(self, name, parameters):
|
||||
"""
|
||||
Obsolete alias for _duplicate.
|
||||
"""
|
||||
|
||||
a = self._args.copy(name=name, args=parameters)
|
||||
return self._duplicate(a)
|
||||
|
||||
def _equals(self, o):
|
||||
"""
|
||||
This is a utility method that can be called by a Displayable's
|
||||
__eq__ method, to compare displayables for type and displayable
|
||||
component equality.
|
||||
"""
|
||||
|
||||
if type(self) is not type(o):
|
||||
return False
|
||||
|
||||
if self.focus_name != o.focus_name:
|
||||
return False
|
||||
|
||||
if self.style != o.style:
|
||||
return False
|
||||
|
||||
if self.default != o.default:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _repr_info(self):
|
||||
return None
|
||||
|
||||
def __repr__(self):
|
||||
rep = object.__repr__(self)
|
||||
reprinfo = self._repr_info()
|
||||
if reprinfo is None:
|
||||
return rep
|
||||
if reprinfo and not ((reprinfo[0] == '(') and (reprinfo[-1] == ')')):
|
||||
reprinfo = "".join(("(", reprinfo, ")"))
|
||||
parto = rep.rpartition(" at ")
|
||||
return " ".join((parto[0],
|
||||
reprinfo,
|
||||
"at",
|
||||
parto[2]))
|
||||
|
||||
def find_focusable(self, callback, focus_name):
|
||||
|
||||
focus_name = self.focus_name or focus_name
|
||||
|
||||
if self.focusable:
|
||||
callback(self, focus_name)
|
||||
elif self.focusable is not None:
|
||||
callback(None, focus_name)
|
||||
|
||||
for i in self.visit():
|
||||
if i is None:
|
||||
continue
|
||||
|
||||
i.find_focusable(callback, focus_name)
|
||||
|
||||
def focus(self, default=False):
|
||||
"""
|
||||
Called to indicate that this widget has the focus.
|
||||
"""
|
||||
|
||||
self.set_style_prefix(self.role + "hover_", True)
|
||||
|
||||
if not default:
|
||||
renpy.exports.play(self.style.hover_sound)
|
||||
|
||||
def unfocus(self, default=False):
|
||||
"""
|
||||
Called to indicate that this widget has become unfocused.
|
||||
"""
|
||||
|
||||
self.set_style_prefix(self.role + "idle_", True)
|
||||
|
||||
def is_focused(self):
|
||||
|
||||
if renpy.display.focus.grab and renpy.display.focus.grab is not self:
|
||||
return
|
||||
|
||||
return renpy.game.context().scene_lists.focused is self
|
||||
|
||||
def set_style_prefix(self, prefix, root):
|
||||
"""
|
||||
Called to set the style prefix of this widget and its child
|
||||
widgets, if any.
|
||||
|
||||
`root` - True if this is the root of a style tree, False if this
|
||||
has been passed on to a child.
|
||||
"""
|
||||
|
||||
if prefix == self.style.prefix:
|
||||
return
|
||||
|
||||
self.style.set_prefix(prefix)
|
||||
renpy.display.render.redraw(self, 0)
|
||||
|
||||
def render(self, width, height, st, at):
|
||||
"""
|
||||
Called to display this displayable. This is called with width
|
||||
and height parameters, which give the largest width and height
|
||||
that this drawable can be drawn to without overflowing some
|
||||
bounding box. It's also given two times. It returns a Surface
|
||||
that is the current image of this drawable.
|
||||
|
||||
@param st: The time since this widget was first shown, in seconds.
|
||||
@param at: The time since a similarly named widget was first shown,
|
||||
in seconds.
|
||||
"""
|
||||
|
||||
raise Exception("Render not implemented.")
|
||||
|
||||
def event(self, ev, x, y, st):
|
||||
"""
|
||||
Called to report than an event has occured. Ev is the raw
|
||||
pygame event object representing that event. If the event
|
||||
involves the mouse, x and y are the translation of the event
|
||||
into the coordinates of this displayable. st is the time this
|
||||
widget has been shown for.
|
||||
|
||||
@returns A value that should be returned from Interact, or None if
|
||||
no value is appropriate.
|
||||
"""
|
||||
|
||||
return None
|
||||
|
||||
def get_placement(self):
|
||||
"""
|
||||
Returns a style object containing placement information for
|
||||
this Displayable. Children are expected to overload this
|
||||
to return something more sensible.
|
||||
"""
|
||||
|
||||
return self.style.get_placement()
|
||||
|
||||
def visit_all(self, callback, seen=None):
|
||||
"""
|
||||
Calls the callback on this displayable, and then on all children
|
||||
of this displayable.
|
||||
"""
|
||||
|
||||
if seen is None:
|
||||
seen = set()
|
||||
|
||||
for d in self.visit():
|
||||
|
||||
if d is None:
|
||||
continue
|
||||
|
||||
id_d = id(d)
|
||||
if id_d in seen:
|
||||
continue
|
||||
|
||||
seen.add(id_d)
|
||||
d.visit_all(callback, seen)
|
||||
|
||||
callback(self)
|
||||
|
||||
def visit(self):
|
||||
"""
|
||||
Called to ask the displayable to return a list of its children
|
||||
(including children taken from styles). For convenience, this
|
||||
list may also include None values.
|
||||
"""
|
||||
|
||||
return [ ]
|
||||
|
||||
def per_interact(self):
|
||||
"""
|
||||
Called once per widget per interaction.
|
||||
"""
|
||||
|
||||
return None
|
||||
|
||||
def predict_one(self):
|
||||
"""
|
||||
Called to ask this displayable to call the callback with all
|
||||
the images it may want to load.
|
||||
"""
|
||||
|
||||
return
|
||||
|
||||
def predict_one_action(self):
|
||||
"""
|
||||
Called to ask this displayable to cause image prediction
|
||||
to occur for images that may be loaded by its actions.
|
||||
"""
|
||||
|
||||
return
|
||||
|
||||
def place(self, dest, x, y, width, height, surf, main=True):
|
||||
"""
|
||||
This places a render (which must be of this displayable)
|
||||
within a bounding area. Returns an (x, y) tuple giving the location
|
||||
the displayable was placed at.
|
||||
|
||||
`dest`
|
||||
If not None, the `surf` will be blitted to `dest` at the
|
||||
computed coordinates.
|
||||
|
||||
`x`, `y`, `width`, `height`
|
||||
The bounding area.
|
||||
|
||||
`surf`
|
||||
The render to place.
|
||||
|
||||
`main`
|
||||
This is passed to Render.blit().
|
||||
"""
|
||||
|
||||
placement = self.get_placement()
|
||||
subpixel = placement[6]
|
||||
|
||||
xpos, ypos = place(width, height, surf.width, surf.height, placement)
|
||||
|
||||
xpos += x
|
||||
ypos += y
|
||||
|
||||
pos = (xpos, ypos)
|
||||
|
||||
if dest is not None:
|
||||
if subpixel:
|
||||
dest.subpixel_blit(surf, pos, main, main, None)
|
||||
else:
|
||||
dest.blit(surf, pos, main, main, None)
|
||||
|
||||
return pos
|
||||
|
||||
def set_transform_event(self, event):
|
||||
"""
|
||||
Sets the transform event of this displayable to event.
|
||||
"""
|
||||
|
||||
if event == self.transform_event:
|
||||
return
|
||||
|
||||
self.transform_event = event
|
||||
|
||||
if self.transform_event_responder:
|
||||
renpy.display.render.redraw(self, 0)
|
||||
|
||||
def _handles_event(self, event):
|
||||
"""
|
||||
Returns True if the displayable handles event, False otherwise.
|
||||
"""
|
||||
|
||||
return False
|
||||
|
||||
def _hide(self, st, at, kind):
|
||||
"""
|
||||
Returns None if this displayable is ready to be hidden, or
|
||||
a replacement displayable if it doesn't want to be hidden
|
||||
quite yet.
|
||||
|
||||
Kind may be "hide", "replace", or "cancel", with the latter
|
||||
being called when the hide is being hidden itself because
|
||||
another displayable is shown.
|
||||
"""
|
||||
|
||||
return None
|
||||
|
||||
def _show(self):
|
||||
"""
|
||||
No longer used.
|
||||
"""
|
||||
|
||||
def _target(self):
|
||||
"""
|
||||
If this displayable is part of a chain of one or more references,
|
||||
returns the ultimate target of those references. Otherwise, returns
|
||||
the displayable.
|
||||
"""
|
||||
|
||||
return self
|
||||
|
||||
def _change_transform_child(self, child):
|
||||
"""
|
||||
If this is a transform, makes a copy of the transform and sets
|
||||
the child of the innermost transform to this. Otherwise,
|
||||
simply returns child.
|
||||
"""
|
||||
|
||||
return child
|
||||
|
||||
def _clear(self):
|
||||
"""
|
||||
Clears out the children of this displayable, if any.
|
||||
"""
|
||||
|
||||
return
|
||||
|
||||
def _tts_common(self, default_alt=None, reverse=False):
|
||||
|
||||
rv = [ ]
|
||||
|
||||
if reverse:
|
||||
order = -1
|
||||
else:
|
||||
order = 1
|
||||
|
||||
speech = ""
|
||||
|
||||
for i in self.visit()[::order]:
|
||||
if i is not None:
|
||||
speech = i._tts()
|
||||
|
||||
if speech.strip():
|
||||
if isinstance(speech, renpy.display.tts.TTSDone):
|
||||
rv = [ speech ]
|
||||
else:
|
||||
rv.append(speech)
|
||||
|
||||
|
||||
rv = ": ".join(rv)
|
||||
rv = rv.replace("::", ":")
|
||||
rv = rv.replace(": :", ":")
|
||||
|
||||
alt = self.style.alt
|
||||
|
||||
if alt is None:
|
||||
alt = default_alt
|
||||
|
||||
if alt is not None:
|
||||
rv = renpy.substitutions.substitute(alt, scope={ "text" : rv })[0]
|
||||
|
||||
rv = type(speech)(rv)
|
||||
|
||||
return rv
|
||||
|
||||
def _tts(self):
|
||||
"""
|
||||
Returns the self-voicing text of this displayable and all of its
|
||||
children that cannot take focus. If the displayable can take focus,
|
||||
returns the empty string.
|
||||
"""
|
||||
|
||||
return self._tts_common()
|
||||
|
||||
def _tts_all(self):
|
||||
"""
|
||||
Returns the self-voicing text of this displayable and all of its
|
||||
children that cannot take focus.
|
||||
"""
|
||||
return self._tts_common()
|
||||
|
||||
|
||||
class SceneListEntry(renpy.object.Object):
|
||||
"""
|
||||
Represents a scene list entry. Since this was replacing a tuple,
|
||||
@@ -935,9 +359,6 @@ class SceneLists(renpy.object.Object):
|
||||
if i not in self.camera_list:
|
||||
self.camera_list[i] = (None, [ ])
|
||||
|
||||
self.layer_at_list.setdefault(None, (None, [ ]))
|
||||
self.camera_list.setdefault(None, (None, [ ]))
|
||||
|
||||
def after_upgrade(self, version):
|
||||
|
||||
if version < 1:
|
||||
@@ -1053,9 +474,6 @@ class SceneLists(renpy.object.Object):
|
||||
self.music = None
|
||||
self.focused = None
|
||||
|
||||
self.layer_at_list[None] = (None, [ ])
|
||||
self.camera_list[None] = (None, [ ])
|
||||
|
||||
def replace_transient(self, prefix="hide"): # type: (str|None) -> None
|
||||
"""
|
||||
Replaces the contents of the transient display list with
|
||||
@@ -2615,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
|
||||
@@ -3976,7 +3394,7 @@ class Interface(object):
|
||||
old_root.layers[layer] = d
|
||||
old_root.add(d)
|
||||
|
||||
trans = instantiate_transition(None, old_root, renpy.defaultstore.At(layers_root, *scene_lists.layer_at_list[None][1], *scene_lists.camera_list[None][1]))
|
||||
trans = instantiate_transition(None, old_root, layers_root)
|
||||
|
||||
if not isinstance(trans, Displayable):
|
||||
raise Exception("Expected transition to return a displayable, not a {!r}".format(trans))
|
||||
@@ -4002,7 +3420,7 @@ class Interface(object):
|
||||
focus_roots.append(pb)
|
||||
|
||||
else:
|
||||
root_widget.add(renpy.defaultstore.At(layers_root, *scene_lists.layer_at_list[None][1], *scene_lists.camera_list[None][1]))
|
||||
root_widget.add(layers_root)
|
||||
|
||||
# Add top_layers to the root_widget.
|
||||
for layer in renpy.config.top_layers:
|
||||
|
||||
@@ -0,0 +1,620 @@
|
||||
# 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
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
from __future__ import division, absolute_import, with_statement, print_function, unicode_literals
|
||||
from renpy.compat import PY2, basestring, bchr, bord, chr, open, pystr, range, round, str, tobytes, unicode # *
|
||||
|
||||
import copy
|
||||
|
||||
import renpy
|
||||
|
||||
def place(width, height, sw, sh, placement):
|
||||
"""
|
||||
Performs the Ren'Py placement algorithm.
|
||||
|
||||
`width`, `height`
|
||||
The width and height of the area the image will be
|
||||
placed in.
|
||||
|
||||
`sw`, `sh`
|
||||
The size of the image to be placed.
|
||||
|
||||
`placement`
|
||||
The tuple returned by Displayable.get_placement().
|
||||
"""
|
||||
|
||||
xpos, ypos, xanchor, yanchor, xoffset, yoffset, _subpixel = placement
|
||||
|
||||
compute_raw = renpy.display.core.absolute.compute_raw
|
||||
|
||||
if xpos is None:
|
||||
xpos = 0
|
||||
if ypos is None:
|
||||
ypos = 0
|
||||
if xanchor is None:
|
||||
xanchor = 0
|
||||
if yanchor is None:
|
||||
yanchor = 0
|
||||
if xoffset is None:
|
||||
xoffset = 0
|
||||
if yoffset is None:
|
||||
yoffset = 0
|
||||
|
||||
xpos = compute_raw(xpos, width)
|
||||
|
||||
xanchor = compute_raw(xanchor, sw)
|
||||
|
||||
x = xpos + xoffset - xanchor
|
||||
|
||||
ypos = compute_raw(ypos, height)
|
||||
|
||||
yanchor = compute_raw(yanchor, sh)
|
||||
|
||||
y = ypos + yoffset - yanchor
|
||||
|
||||
return x, y
|
||||
|
||||
|
||||
class DisplayableArguments(renpy.object.Object):
|
||||
"""
|
||||
Represents a set of arguments that can be passed to a duplicated
|
||||
displayable.
|
||||
"""
|
||||
|
||||
# The name of the displayable without any arguments.
|
||||
name = () # type: tuple
|
||||
|
||||
# Arguments supplied.
|
||||
args = () # type: tuple
|
||||
|
||||
# The style prefix in play. This is used by DynamicImage to figure
|
||||
# out the prefix list to apply.
|
||||
prefix = None # Optional[str]
|
||||
|
||||
# True if lint is in use.
|
||||
lint = False
|
||||
|
||||
def copy(self, **kwargs):
|
||||
"""
|
||||
Returns a copy of this object with the various fields set to the
|
||||
values they were given in kwargs.
|
||||
"""
|
||||
|
||||
rv = DisplayableArguments()
|
||||
rv.__dict__.update(self.__dict__)
|
||||
rv.__dict__.update(kwargs)
|
||||
|
||||
return rv
|
||||
|
||||
def extraneous(self):
|
||||
if renpy.config.developer and renpy.config.report_extraneous_attributes:
|
||||
raise Exception("Image '{}' does not accept attributes '{}'.".format(
|
||||
" ".join(self.name),
|
||||
" ".join(self.args),
|
||||
))
|
||||
|
||||
|
||||
default_style = renpy.style.Style("default")
|
||||
|
||||
|
||||
class Displayable(renpy.object.Object):
|
||||
"""
|
||||
The base class for every object in Ren'Py that can be
|
||||
displayed to the screen.
|
||||
|
||||
Drawables will be serialized to a savegame file. Therefore, they
|
||||
shouldn't store non-serializable things (like pygame surfaces) in
|
||||
their fields.
|
||||
"""
|
||||
|
||||
# Some invariants about method call order:
|
||||
#
|
||||
# per_interact is called before render.
|
||||
# render is called before event.
|
||||
#
|
||||
# get_placement can be called at any time, so can't
|
||||
# assume anything.
|
||||
|
||||
# If True this displayable can accept focus.
|
||||
# If False, it can't, but it keeps its place in the focus order.
|
||||
# If None, it does not have a place in the focus order.
|
||||
focusable = None
|
||||
|
||||
# This is the focus named assigned by the focus code.
|
||||
full_focus_name = None
|
||||
|
||||
# A role ('selected_' or '' that prefixes the style).
|
||||
role = ''
|
||||
|
||||
# The event we'll pass on to our parent transform.
|
||||
transform_event = None
|
||||
|
||||
# Can we change our look in response to transform_events?
|
||||
transform_event_responder = False
|
||||
|
||||
# The main displayable, if this displayable is the root of a composite
|
||||
# displayable. (This is used by SL to figure out where to add children
|
||||
# to.) If None, it is itself.
|
||||
_main = None
|
||||
|
||||
# A list of the children that make up this composite displayable.
|
||||
_composite_parts = [ ]
|
||||
|
||||
# The location the displayable was created at, if known.
|
||||
_location = None
|
||||
|
||||
# Does this displayable use the scope?
|
||||
_uses_scope = False
|
||||
|
||||
# Arguments supplied to this displayable.
|
||||
_args = DisplayableArguments()
|
||||
|
||||
# Set to true of the displayable is duplicatable (has a non-trivial
|
||||
# duplicate method), or one of its children is.
|
||||
_duplicatable = False
|
||||
|
||||
# Does this displayable require clipping?
|
||||
_clipping = False
|
||||
|
||||
# Does this displayable have a tooltip?
|
||||
_tooltip = None
|
||||
|
||||
# Should hbox and vbox skip this displayable?
|
||||
_box_skip = False
|
||||
|
||||
# If not None, this should be a (width, height) tuple that overrides the
|
||||
# amount of space offered to the displayable.
|
||||
_offer_size = None
|
||||
|
||||
# If true, this displayable will be treated as draggable in its own right.
|
||||
# This is used by viewport to decide if a drag is meant for the viewport
|
||||
# or for its child.
|
||||
_draggable = False
|
||||
|
||||
# Used by a transition (or transition-like object) to determine how long to
|
||||
# delay for.
|
||||
delay = None # type: float|None
|
||||
|
||||
def __ne__(self, o):
|
||||
return not (self == o)
|
||||
|
||||
def __init__(self, focus=None, default=False, style='default', _args=None, tooltip=None, default_focus=False, **properties):
|
||||
|
||||
global default_style
|
||||
|
||||
if (style == "default") and (not properties):
|
||||
self.style = default_style
|
||||
else:
|
||||
self.style = renpy.style.Style(style, properties) # @UndefinedVariable
|
||||
|
||||
self.focus_name = focus
|
||||
self.default = default or default_focus
|
||||
self._tooltip = tooltip
|
||||
|
||||
if _args is not None:
|
||||
self._args = _args
|
||||
|
||||
def _copy(self, args=None):
|
||||
"""
|
||||
Makes a shallow copy of the displayable. If `args` is provided,
|
||||
replaces the arguments with the stored copy.
|
||||
"""
|
||||
|
||||
rv = copy.copy(self)
|
||||
|
||||
if args is not None:
|
||||
rv._args = args
|
||||
|
||||
return rv
|
||||
|
||||
def _duplicate(self, args):
|
||||
"""
|
||||
Makes a duplicate copy of the following kids of displayables:
|
||||
|
||||
* Displayables that can accept arguments.
|
||||
* Displayables that maintain state that should be reset before being
|
||||
shown to the user.
|
||||
* Containers that contain (including transitively) one of the other
|
||||
kinds of displayables.
|
||||
|
||||
Displayables that contain state that can be manipulated by the user
|
||||
are never copied.
|
||||
|
||||
This should call _unique on children that have been copied before
|
||||
setting its own _duplicatable flag.
|
||||
"""
|
||||
|
||||
if args and args.args:
|
||||
args.extraneous()
|
||||
|
||||
return self
|
||||
|
||||
def _get_tooltip(self):
|
||||
"""
|
||||
Returns the tooltip of this displayable.
|
||||
"""
|
||||
|
||||
return self._tooltip
|
||||
|
||||
def _in_current_store(self):
|
||||
"""
|
||||
Returns a version of this displayable that will not change as it is
|
||||
rendered.
|
||||
"""
|
||||
|
||||
return self
|
||||
|
||||
def _unique(self):
|
||||
"""
|
||||
This is called when a displayable is "unique", meaning there will
|
||||
only be one reference to it, ever, from the tree of displayables.
|
||||
"""
|
||||
|
||||
self._duplicatable = False
|
||||
|
||||
return
|
||||
|
||||
def parameterize(self, name, parameters):
|
||||
"""
|
||||
Obsolete alias for _duplicate.
|
||||
"""
|
||||
|
||||
a = self._args.copy(name=name, args=parameters)
|
||||
return self._duplicate(a)
|
||||
|
||||
def _equals(self, o):
|
||||
"""
|
||||
This is a utility method that can be called by a Displayable's
|
||||
__eq__ method, to compare displayables for type and displayable
|
||||
component equality.
|
||||
"""
|
||||
|
||||
if type(self) is not type(o):
|
||||
return False
|
||||
|
||||
if self.focus_name != o.focus_name:
|
||||
return False
|
||||
|
||||
if self.style != o.style:
|
||||
return False
|
||||
|
||||
if self.default != o.default:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _repr_info(self):
|
||||
return None
|
||||
|
||||
def __repr__(self):
|
||||
rep = object.__repr__(self)
|
||||
reprinfo = self._repr_info()
|
||||
if reprinfo is None:
|
||||
return rep
|
||||
if reprinfo and not ((reprinfo[0] == '(') and (reprinfo[-1] == ')')):
|
||||
reprinfo = "".join(("(", reprinfo, ")"))
|
||||
parto = rep.rpartition(" at ")
|
||||
return " ".join((parto[0],
|
||||
reprinfo,
|
||||
"at",
|
||||
parto[2]))
|
||||
|
||||
def find_focusable(self, callback, focus_name):
|
||||
|
||||
focus_name = self.focus_name or focus_name
|
||||
|
||||
if self.focusable:
|
||||
callback(self, focus_name)
|
||||
elif self.focusable is not None:
|
||||
callback(None, focus_name)
|
||||
|
||||
for i in self.visit():
|
||||
if i is None:
|
||||
continue
|
||||
|
||||
i.find_focusable(callback, focus_name)
|
||||
|
||||
def focus(self, default=False):
|
||||
"""
|
||||
Called to indicate that this widget has the focus.
|
||||
"""
|
||||
|
||||
self.set_style_prefix(self.role + "hover_", True)
|
||||
|
||||
if not default:
|
||||
renpy.exports.play(self.style.hover_sound)
|
||||
|
||||
def unfocus(self, default=False):
|
||||
"""
|
||||
Called to indicate that this widget has become unfocused.
|
||||
"""
|
||||
|
||||
self.set_style_prefix(self.role + "idle_", True)
|
||||
|
||||
def is_focused(self):
|
||||
|
||||
if renpy.display.focus.grab and renpy.display.focus.grab is not self:
|
||||
return
|
||||
|
||||
return renpy.game.context().scene_lists.focused is self
|
||||
|
||||
def set_style_prefix(self, prefix, root):
|
||||
"""
|
||||
Called to set the style prefix of this widget and its child
|
||||
widgets, if any.
|
||||
|
||||
`root` - True if this is the root of a style tree, False if this
|
||||
has been passed on to a child.
|
||||
"""
|
||||
|
||||
if prefix == self.style.prefix:
|
||||
return
|
||||
|
||||
self.style.set_prefix(prefix)
|
||||
renpy.display.render.redraw(self, 0)
|
||||
|
||||
def render(self, width, height, st, at):
|
||||
"""
|
||||
Called to display this displayable. This is called with width
|
||||
and height parameters, which give the largest width and height
|
||||
that this drawable can be drawn to without overflowing some
|
||||
bounding box. It's also given two times. It returns a Surface
|
||||
that is the current image of this drawable.
|
||||
|
||||
@param st: The time since this widget was first shown, in seconds.
|
||||
@param at: The time since a similarly named widget was first shown,
|
||||
in seconds.
|
||||
"""
|
||||
|
||||
raise Exception("Render not implemented.")
|
||||
|
||||
def event(self, ev, x, y, st):
|
||||
"""
|
||||
Called to report than an event has occured. Ev is the raw
|
||||
pygame event object representing that event. If the event
|
||||
involves the mouse, x and y are the translation of the event
|
||||
into the coordinates of this displayable. st is the time this
|
||||
widget has been shown for.
|
||||
|
||||
@returns A value that should be returned from Interact, or None if
|
||||
no value is appropriate.
|
||||
"""
|
||||
|
||||
return None
|
||||
|
||||
def get_placement(self):
|
||||
"""
|
||||
Returns a style object containing placement information for
|
||||
this Displayable. Children are expected to overload this
|
||||
to return something more sensible.
|
||||
"""
|
||||
|
||||
return self.style.get_placement()
|
||||
|
||||
def visit_all(self, callback, seen=None):
|
||||
"""
|
||||
Calls the callback on this displayable, and then on all children
|
||||
of this displayable.
|
||||
"""
|
||||
|
||||
if seen is None:
|
||||
seen = set()
|
||||
|
||||
for d in self.visit():
|
||||
|
||||
if d is None:
|
||||
continue
|
||||
|
||||
id_d = id(d)
|
||||
if id_d in seen:
|
||||
continue
|
||||
|
||||
seen.add(id_d)
|
||||
d.visit_all(callback, seen)
|
||||
|
||||
callback(self)
|
||||
|
||||
def visit(self):
|
||||
"""
|
||||
Called to ask the displayable to return a list of its children
|
||||
(including children taken from styles). For convenience, this
|
||||
list may also include None values.
|
||||
"""
|
||||
|
||||
return [ ]
|
||||
|
||||
def per_interact(self):
|
||||
"""
|
||||
Called once per widget per interaction.
|
||||
"""
|
||||
|
||||
return None
|
||||
|
||||
def predict_one(self):
|
||||
"""
|
||||
Called to ask this displayable to call the callback with all
|
||||
the images it may want to load.
|
||||
"""
|
||||
|
||||
return
|
||||
|
||||
def predict_one_action(self):
|
||||
"""
|
||||
Called to ask this displayable to cause image prediction
|
||||
to occur for images that may be loaded by its actions.
|
||||
"""
|
||||
|
||||
return
|
||||
|
||||
def place(self, dest, x, y, width, height, surf, main=True):
|
||||
"""
|
||||
This places a render (which must be of this displayable)
|
||||
within a bounding area. Returns an (x, y) tuple giving the location
|
||||
the displayable was placed at.
|
||||
|
||||
`dest`
|
||||
If not None, the `surf` will be blitted to `dest` at the
|
||||
computed coordinates.
|
||||
|
||||
`x`, `y`, `width`, `height`
|
||||
The bounding area.
|
||||
|
||||
`surf`
|
||||
The render to place.
|
||||
|
||||
`main`
|
||||
This is passed to Render.blit().
|
||||
"""
|
||||
|
||||
placement = self.get_placement()
|
||||
subpixel = placement[6]
|
||||
|
||||
xpos, ypos = place(width, height, surf.width, surf.height, placement)
|
||||
|
||||
xpos += x
|
||||
ypos += y
|
||||
|
||||
pos = (xpos, ypos)
|
||||
|
||||
if dest is not None:
|
||||
if subpixel:
|
||||
dest.subpixel_blit(surf, pos, main, main, None)
|
||||
else:
|
||||
dest.blit(surf, pos, main, main, None)
|
||||
|
||||
return pos
|
||||
|
||||
def set_transform_event(self, event):
|
||||
"""
|
||||
Sets the transform event of this displayable to event.
|
||||
"""
|
||||
|
||||
if event == self.transform_event:
|
||||
return
|
||||
|
||||
self.transform_event = event
|
||||
|
||||
if self.transform_event_responder:
|
||||
renpy.display.render.redraw(self, 0)
|
||||
|
||||
def _handles_event(self, event):
|
||||
"""
|
||||
Returns True if the displayable handles event, False otherwise.
|
||||
"""
|
||||
|
||||
return False
|
||||
|
||||
def _hide(self, st, at, kind):
|
||||
"""
|
||||
Returns None if this displayable is ready to be hidden, or
|
||||
a replacement displayable if it doesn't want to be hidden
|
||||
quite yet.
|
||||
|
||||
Kind may be "hide", "replace", or "cancel", with the latter
|
||||
being called when the hide is being hidden itself because
|
||||
another displayable is shown.
|
||||
"""
|
||||
|
||||
return None
|
||||
|
||||
def _show(self):
|
||||
"""
|
||||
No longer used.
|
||||
"""
|
||||
|
||||
def _target(self):
|
||||
"""
|
||||
If this displayable is part of a chain of one or more references,
|
||||
returns the ultimate target of those references. Otherwise, returns
|
||||
the displayable.
|
||||
"""
|
||||
|
||||
return self
|
||||
|
||||
def _change_transform_child(self, child):
|
||||
"""
|
||||
If this is a transform, makes a copy of the transform and sets
|
||||
the child of the innermost transform to this. Otherwise,
|
||||
simply returns child.
|
||||
"""
|
||||
|
||||
return child
|
||||
|
||||
def _clear(self):
|
||||
"""
|
||||
Clears out the children of this displayable, if any.
|
||||
"""
|
||||
|
||||
return
|
||||
|
||||
def _tts_common(self, default_alt=None, reverse=False):
|
||||
|
||||
rv = [ ]
|
||||
|
||||
if reverse:
|
||||
order = -1
|
||||
else:
|
||||
order = 1
|
||||
|
||||
speech = ""
|
||||
|
||||
for i in self.visit()[::order]:
|
||||
if i is not None:
|
||||
speech = i._tts()
|
||||
|
||||
if speech.strip():
|
||||
if isinstance(speech, renpy.display.tts.TTSDone):
|
||||
rv = [ speech ]
|
||||
else:
|
||||
rv.append(speech)
|
||||
|
||||
|
||||
rv = ": ".join(rv)
|
||||
rv = rv.replace("::", ":")
|
||||
rv = rv.replace(": :", ":")
|
||||
|
||||
alt = self.style.alt
|
||||
|
||||
if alt is None:
|
||||
alt = default_alt
|
||||
|
||||
if alt is not None:
|
||||
rv = renpy.substitutions.substitute(alt, scope={ "text" : rv })[0]
|
||||
|
||||
rv = type(speech)(rv)
|
||||
|
||||
return rv
|
||||
|
||||
def _tts(self):
|
||||
"""
|
||||
Returns the self-voicing text of this displayable and all of its
|
||||
children that cannot take focus. If the displayable can take focus,
|
||||
returns the empty string.
|
||||
"""
|
||||
|
||||
return self._tts_common()
|
||||
|
||||
def _tts_all(self):
|
||||
"""
|
||||
Returns the self-voicing text of this displayable and all of its
|
||||
children that cannot take focus.
|
||||
"""
|
||||
return self._tts_common()
|
||||
+14
-22
@@ -58,7 +58,7 @@ def default_drop_allowable(drop, drags):
|
||||
return True
|
||||
|
||||
|
||||
class Drag(renpy.display.core.Displayable, renpy.revertable.RevertableObject):
|
||||
class Drag(renpy.display.displayable.Displayable, renpy.revertable.RevertableObject):
|
||||
"""
|
||||
:doc: drag_drop class
|
||||
:args: (d=None, drag_name=None, draggable=True, droppable=True, drag_raise=True, dragging=None, dragged=None, dropped=None, drag_handle=(0.0, 0.0, 1.0, 1.0), drag_joined=..., clicked=None, hovered=None, unhovered=None, mouse_drop=False, **properties)
|
||||
@@ -159,9 +159,7 @@ class Drag(renpy.display.core.Displayable, renpy.revertable.RevertableObject):
|
||||
|
||||
`drag_handle`
|
||||
A (x, y, width, height) tuple, giving the position of the drag
|
||||
handle within the child. In this tuple, integers are considered
|
||||
to be a literal number of pixels, while floats are relative to
|
||||
the size of the child.
|
||||
handle within the child. This tuple takes :term:`positions <position>`.
|
||||
|
||||
`drag_joined`
|
||||
This is called with the current Drag as an argument. It's
|
||||
@@ -382,7 +380,7 @@ class Drag(renpy.display.core.Displayable, renpy.revertable.RevertableObject):
|
||||
self.target_at_delay = 0
|
||||
|
||||
# The displayable we were last dropping on.
|
||||
self.last_drop = None # type: renpy.display.core.Displayable|None
|
||||
self.last_drop = None # type: renpy.display.displayable.Displayable|None
|
||||
|
||||
# Did we move over the course of this drag?
|
||||
self.drag_moved = False
|
||||
@@ -426,11 +424,11 @@ class Drag(renpy.display.core.Displayable, renpy.revertable.RevertableObject):
|
||||
linear move.
|
||||
"""
|
||||
|
||||
if (type(x) is float) and self.parent_width is not None:
|
||||
x = int(x * self.parent_width)
|
||||
if self.parent_width is not None:
|
||||
x = int(absolute.compute_raw(x, self.parent_width))
|
||||
|
||||
if (type(y) is float) and self.parent_height is not None:
|
||||
y = int(y * self.parent_height)
|
||||
if self.parent_height is not None:
|
||||
y = int(absolute.compute_raw(y, self.parent_height))
|
||||
|
||||
self.target_x = x
|
||||
self.target_y = y
|
||||
@@ -459,6 +457,7 @@ class Drag(renpy.display.core.Displayable, renpy.revertable.RevertableObject):
|
||||
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
|
||||
@@ -597,7 +596,7 @@ class Drag(renpy.display.core.Displayable, renpy.revertable.RevertableObject):
|
||||
self.target_at = at + self.target_at_delay
|
||||
self.target_at_delay = 0
|
||||
redraw(self, 0)
|
||||
elif at >= self.target_at:
|
||||
elif self.target_at <= at or self.target_at <= self.at:
|
||||
# Snap complete
|
||||
self.x = self.target_x
|
||||
self.y = self.target_y
|
||||
@@ -611,18 +610,10 @@ class Drag(renpy.display.core.Displayable, renpy.revertable.RevertableObject):
|
||||
if self.draggable or self.clicked is not None:
|
||||
|
||||
fx, fy, fw, fh = self.drag_handle
|
||||
|
||||
if isinstance(fx, float):
|
||||
fx = int(fx * cw)
|
||||
|
||||
if isinstance(fy, float):
|
||||
fy = int(fy * ch)
|
||||
|
||||
if isinstance(fw, float):
|
||||
fw = int(fw * cw)
|
||||
|
||||
if isinstance(fh, float):
|
||||
fh = int(fh * ch)
|
||||
fx = int(absolute.compute_raw(fx, cw))
|
||||
fy = int(absolute.compute_raw(fy, ch))
|
||||
fw = int(absolute.compute_raw(fw, cw))
|
||||
fh = int(absolute.compute_raw(fh, ch))
|
||||
|
||||
mask = self.style.focus_mask
|
||||
|
||||
@@ -946,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
|
||||
|
||||
+80
-45
@@ -622,7 +622,7 @@ def free_memory():
|
||||
cache.clear()
|
||||
|
||||
|
||||
class ImageBase(renpy.display.core.Displayable):
|
||||
class ImageBase(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
This is the base class for all of the various kinds of images that
|
||||
we can possibly have.
|
||||
@@ -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.
|
||||
"""
|
||||
|
||||
"""
|
||||
@@ -2004,7 +2039,7 @@ def image(arg, loose=False, **properties):
|
||||
elif loose:
|
||||
return arg
|
||||
|
||||
if isinstance(arg, renpy.display.core.Displayable):
|
||||
if isinstance(arg, renpy.display.displayable.Displayable):
|
||||
raise Exception("Expected an image, but got a general displayable.")
|
||||
else:
|
||||
raise Exception("Could not construct image from argument.")
|
||||
|
||||
@@ -319,7 +319,7 @@ def wrap_render(child, w, h, st, at):
|
||||
return rv
|
||||
|
||||
|
||||
class ImageReference(renpy.display.core.Displayable):
|
||||
class ImageReference(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
ImageReference objects are used to reference images by their name,
|
||||
which is a tuple of strings corresponding to the name used to define
|
||||
@@ -348,7 +348,7 @@ class ImageReference(renpy.display.core.Displayable):
|
||||
super(ImageReference, self).__init__(**properties)
|
||||
|
||||
self.name = name
|
||||
self.target = None # type: renpy.display.core.Displayable|None
|
||||
self.target = None # type: renpy.display.displayable.Displayable|None
|
||||
|
||||
def _repr_info(self):
|
||||
return repr(self.name)
|
||||
@@ -379,7 +379,7 @@ class ImageReference(renpy.display.core.Displayable):
|
||||
|
||||
name = self.name
|
||||
|
||||
if isinstance(name, renpy.display.core.Displayable):
|
||||
if isinstance(name, renpy.display.displayable.Displayable):
|
||||
self.target = name
|
||||
return True
|
||||
|
||||
@@ -452,7 +452,7 @@ class ImageReference(renpy.display.core.Displayable):
|
||||
rv = self._copy(args)
|
||||
rv.target = None
|
||||
|
||||
if isinstance(rv.name, renpy.display.core.Displayable):
|
||||
if isinstance(rv.name, renpy.display.displayable.Displayable):
|
||||
if rv.name._duplicatable:
|
||||
rv.name = rv.name._duplicate(args)
|
||||
|
||||
@@ -542,7 +542,7 @@ class ImageReference(renpy.display.core.Displayable):
|
||||
return [ self.target ]
|
||||
|
||||
|
||||
class DynamicImage(renpy.display.core.Displayable):
|
||||
class DynamicImage(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
:doc: disp_imagelike
|
||||
:args: (name)
|
||||
@@ -555,10 +555,10 @@ class DynamicImage(renpy.display.core.Displayable):
|
||||
nosave = [ 'raw_target' ]
|
||||
|
||||
# The target that this image currently resolves to.
|
||||
target = None # type: renpy.display.core.Displayable|None
|
||||
target = None # type: renpy.display.displayable.Displayable|None
|
||||
|
||||
# The raw target that the image resolves to, before it has been parameterized.
|
||||
raw_target = None # type: renpy.display.core.Displayable|None
|
||||
raw_target = None # type: renpy.display.displayable.Displayable|None
|
||||
|
||||
# Have we been locked, so we never change?
|
||||
locked = False
|
||||
@@ -688,7 +688,7 @@ class DynamicImage(renpy.display.core.Displayable):
|
||||
if not update:
|
||||
return True
|
||||
|
||||
raw_target = target # type: renpy.display.core.Displayable
|
||||
raw_target = target # type: renpy.display.displayable.Displayable
|
||||
old_target = self.target
|
||||
|
||||
if raw_target._duplicatable:
|
||||
|
||||
@@ -31,7 +31,7 @@ from renpy.display.render import render, Render
|
||||
from renpy.display.matrix import Matrix2D
|
||||
|
||||
|
||||
class Solid(renpy.display.core.Displayable):
|
||||
class Solid(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
:doc: disp_imagelike
|
||||
|
||||
@@ -75,6 +75,12 @@ class Solid(renpy.display.core.Displayable):
|
||||
|
||||
rv = Render(width, height)
|
||||
|
||||
if width and height:
|
||||
minw, minh = renpy.display.draw.draw_to_virt.transform(1, 1)
|
||||
|
||||
width = max(width, minw)
|
||||
height = max(height, minh)
|
||||
|
||||
if color is None or width <= 0 or height <= 0:
|
||||
return rv
|
||||
|
||||
@@ -139,7 +145,7 @@ class Borders(object):
|
||||
)
|
||||
|
||||
|
||||
class Frame(renpy.display.core.Displayable):
|
||||
class Frame(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
:doc: disp_imagelike
|
||||
:args: (image, left=0, top=0, right=None, bottom=None, *, tile=False, **properties)
|
||||
@@ -273,6 +279,23 @@ class Frame(renpy.display.core.Displayable):
|
||||
width = max(self.style.xminimum, width)
|
||||
height = max(self.style.yminimum, height)
|
||||
|
||||
|
||||
# The size of the final displayable.
|
||||
if self.tile:
|
||||
|
||||
dw = int(width)
|
||||
dh = int(height)
|
||||
else:
|
||||
dw = width
|
||||
dh = height
|
||||
|
||||
|
||||
if width and height:
|
||||
minw, minh = renpy.display.draw.draw_to_virt.transform(1, 1)
|
||||
|
||||
width = max(width, minw)
|
||||
height = max(height, minh)
|
||||
|
||||
image = self.style.child or self.image
|
||||
crend = render(image, width, height, st, at)
|
||||
|
||||
@@ -280,9 +303,6 @@ class Frame(renpy.display.core.Displayable):
|
||||
sw = int(sw)
|
||||
sh = int(sh)
|
||||
|
||||
dw = int(width)
|
||||
dh = int(height)
|
||||
|
||||
bw = self.left + self.right
|
||||
bh = self.top + self.bottom
|
||||
|
||||
@@ -585,7 +605,7 @@ class Frame(renpy.display.core.Displayable):
|
||||
return rv
|
||||
|
||||
|
||||
class FileCurrentScreenshot(renpy.display.core.Displayable):
|
||||
class FileCurrentScreenshot(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
:doc: file_action_function
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ from renpy.display.render import render
|
||||
cached = set()
|
||||
|
||||
|
||||
class ImageMapCrop(renpy.display.core.Displayable):
|
||||
class ImageMapCrop(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
This handles the cropping of uncached imagemap components.
|
||||
"""
|
||||
@@ -55,7 +55,7 @@ class ImageMapCrop(renpy.display.core.Displayable):
|
||||
return cr.subsurface(self.rect)
|
||||
|
||||
|
||||
class ImageCacheCrop(renpy.display.core.Displayable):
|
||||
class ImageCacheCrop(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
This handles the cropping of an imagemap component.
|
||||
"""
|
||||
|
||||
+37
-39
@@ -31,16 +31,11 @@ import renpy
|
||||
from renpy.display.render import render, Render
|
||||
|
||||
|
||||
def scale(num, base):
|
||||
"""
|
||||
If num is a float, multiplies it by base and returns that. Otherwise,
|
||||
returns num unchanged.
|
||||
"""
|
||||
|
||||
if type(num) is float:
|
||||
return num * base
|
||||
else:
|
||||
return num
|
||||
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):
|
||||
@@ -58,7 +53,7 @@ def xyminimums(style, width, height):
|
||||
if (type(xmaximum) is float) and xmaximum and renpy.config.adjust_minimums:
|
||||
xminimum = xminimum / xmaximum
|
||||
|
||||
xminimum = xminimum * width
|
||||
xminimum = compute_raw(xminimum, width)
|
||||
|
||||
if type(yminimum) is float:
|
||||
ymaximum = style.ymaximum
|
||||
@@ -66,12 +61,12 @@ def xyminimums(style, width, height):
|
||||
if (type(ymaximum) is float) and ymaximum and renpy.config.adjust_minimums:
|
||||
yminimum = yminimum / ymaximum
|
||||
|
||||
yminimum = yminimum * height
|
||||
yminimum = compute_raw(yminimum, height)
|
||||
|
||||
return xminimum, yminimum
|
||||
|
||||
|
||||
class Null(renpy.display.core.Displayable):
|
||||
class Null(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
:doc: disp_imagelike
|
||||
:name: Null
|
||||
@@ -101,7 +96,7 @@ class Null(renpy.display.core.Displayable):
|
||||
return rv
|
||||
|
||||
|
||||
class Container(renpy.display.core.Displayable):
|
||||
class Container(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
This is the base class for containers that can have one or more
|
||||
children.
|
||||
@@ -128,7 +123,7 @@ class Container(renpy.display.core.Displayable):
|
||||
def __init__(self, *args, **properties):
|
||||
|
||||
self.children = self._list_type() # type: list
|
||||
self.child = None # type: renpy.display.core.Displayable|None
|
||||
self.child = None # type: renpy.display.displayable.Displayable|None
|
||||
self.offsets = self._list_type() # type: list[tuple[int, int]]
|
||||
|
||||
for i in args:
|
||||
@@ -214,6 +209,8 @@ class Container(renpy.display.core.Displayable):
|
||||
if child._duplicatable:
|
||||
self._duplicatable = True
|
||||
|
||||
renpy.display.render.invalidate(self)
|
||||
|
||||
def _clear(self):
|
||||
self.child = None
|
||||
self.children = self._list_type()
|
||||
@@ -467,13 +464,13 @@ class Grid(Container):
|
||||
yspacing = self.style.spacing
|
||||
|
||||
if renpy.config.relative_spacing:
|
||||
xspacing = renpy.display.layout.scale(xspacing, width)
|
||||
yspacing = renpy.display.layout.scale(yspacing, height)
|
||||
xspacing = compute_raw(xspacing, width)
|
||||
yspacing = compute_raw(yspacing, height)
|
||||
|
||||
left_margin = scale(self.style.left_margin, width)
|
||||
right_margin = scale(self.style.right_margin, width)
|
||||
top_margin = scale(self.style.top_margin, height)
|
||||
bottom_margin = scale(self.style.bottom_margin, height)
|
||||
left_margin = compute_raw(self.style.left_margin, width)
|
||||
right_margin = compute_raw(self.style.right_margin, width)
|
||||
top_margin = compute_raw(self.style.top_margin, height)
|
||||
bottom_margin = compute_raw(self.style.bottom_margin, height)
|
||||
|
||||
# For convenience and speed.
|
||||
cols = self.cols
|
||||
@@ -815,11 +812,11 @@ class MultiBox(Container):
|
||||
|
||||
minx = self.style.xminimum
|
||||
if minx is not None:
|
||||
width = max(width, scale(minx, width))
|
||||
width = max(width, compute_raw(minx, width))
|
||||
|
||||
miny = self.style.yminimum
|
||||
if miny is not None:
|
||||
height = max(height, scale(miny, height))
|
||||
height = max(height, compute_raw(miny, height))
|
||||
|
||||
if self.first and adjust_times:
|
||||
self.update_times()
|
||||
@@ -1302,17 +1299,17 @@ class Window(Container):
|
||||
width = max(xminimum, width)
|
||||
height = max(yminimum, height)
|
||||
|
||||
left_margin = scale(style.left_margin, width)
|
||||
left_padding = scale(style.left_padding, width)
|
||||
left_margin = compute_raw(style.left_margin, width)
|
||||
left_padding = compute_raw(style.left_padding, width)
|
||||
|
||||
right_margin = scale(style.right_margin, width)
|
||||
right_padding = scale(style.right_padding, width)
|
||||
right_margin = compute_raw(style.right_margin, width)
|
||||
right_padding = compute_raw(style.right_padding, width)
|
||||
|
||||
top_margin = scale(style.top_margin, height)
|
||||
top_padding = scale(style.top_padding, height)
|
||||
top_margin = compute_raw(style.top_margin, height)
|
||||
top_padding = compute_raw(style.top_padding, height)
|
||||
|
||||
bottom_margin = scale(style.bottom_margin, height)
|
||||
bottom_padding = scale(style.bottom_padding, height)
|
||||
bottom_margin = compute_raw(style.bottom_margin, height)
|
||||
bottom_padding = compute_raw(style.bottom_padding, height)
|
||||
|
||||
# c for combined.
|
||||
cxmargin = left_margin + right_margin
|
||||
@@ -1426,7 +1423,7 @@ def dynamic_displayable_compat(st, at, expr):
|
||||
return child, None
|
||||
|
||||
|
||||
class DynamicDisplayable(renpy.display.core.Displayable):
|
||||
class DynamicDisplayable(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
:doc: disp_dynamic
|
||||
|
||||
@@ -1975,7 +1972,7 @@ class Side(Container):
|
||||
return rv
|
||||
|
||||
|
||||
class Alpha(renpy.display.core.Displayable):
|
||||
class Alpha(renpy.display.displayable.Displayable):
|
||||
|
||||
def __init__(self, start, end, time, child=None, repeat=False, bounce=False,
|
||||
anim_timebase=False, time_warp=None, **properties):
|
||||
@@ -2078,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()
|
||||
@@ -2390,11 +2387,8 @@ class NearRect(Container):
|
||||
layout_y = py - ch
|
||||
|
||||
# Initial x positioning - using a variant of the layout algorithm.
|
||||
if isinstance(xpos, float):
|
||||
xpos = xpos * pw
|
||||
|
||||
if isinstance(xanchor, float):
|
||||
xanchor = xanchor * cw
|
||||
xpos = compute_raw(xpos, pw)
|
||||
xanchor = compute_raw(xanchor, cw)
|
||||
|
||||
layout_x = px + xpos - xanchor
|
||||
|
||||
@@ -2430,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.
|
||||
@@ -2442,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.
|
||||
|
||||
@@ -47,7 +47,7 @@ class Texture(object):
|
||||
return Texture(d, self.focus, self.main, self.fit)
|
||||
|
||||
|
||||
class Model(renpy.display.core.Displayable):
|
||||
class Model(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
:doc: model_displayable class
|
||||
|
||||
|
||||
@@ -319,6 +319,7 @@ class Revolver(object):
|
||||
self.child = child
|
||||
|
||||
def __call__(self, t, rect):
|
||||
absolute = renpy.display.core.absolute
|
||||
|
||||
(w, h, cw, ch) = rect
|
||||
|
||||
@@ -328,10 +329,7 @@ class Revolver(object):
|
||||
if x is None:
|
||||
x = 0
|
||||
|
||||
if isinstance(x, float):
|
||||
return int(x * r)
|
||||
else:
|
||||
return x
|
||||
return absolute.compute_raw(x, r)
|
||||
|
||||
if self.pos is None:
|
||||
pos = self.child.get_placement()
|
||||
@@ -370,7 +368,7 @@ class Revolver(object):
|
||||
nx = nx - xcor + xaround
|
||||
ny = ny - ycor + yaround
|
||||
|
||||
return (renpy.display.core.absolute(nx), renpy.display.core.absolute(ny), 0, 0)
|
||||
return (absolute(nx), absolute(ny), 0, 0)
|
||||
|
||||
|
||||
def Revolve(start, end, time, child, around=(0.5, 0.5), cor=(0.5, 0.5), pos=None, **properties):
|
||||
@@ -408,7 +406,7 @@ def zoom_render(crend, x, y, w, h, zw, zh, bilinear):
|
||||
return rv
|
||||
|
||||
|
||||
class ZoomCommon(renpy.display.core.Displayable):
|
||||
class ZoomCommon(renpy.display.displayable.Displayable):
|
||||
|
||||
def __init__(self,
|
||||
time, child,
|
||||
@@ -577,7 +575,7 @@ class SizeZoom(ZoomCommon):
|
||||
return 0, 0, width, height, zw, zh
|
||||
|
||||
|
||||
class RotoZoom(renpy.display.core.Displayable):
|
||||
class RotoZoom(renpy.display.displayable.Displayable):
|
||||
|
||||
transform = None
|
||||
|
||||
|
||||
@@ -345,7 +345,7 @@ def OldMoveTransition(delay, old_widget=None, new_widget=None, factory=None, ent
|
||||
# New Move Transition (since 6.14)
|
||||
|
||||
|
||||
class MoveInterpolate(renpy.display.core.Displayable):
|
||||
class MoveInterpolate(renpy.display.displayable.Displayable):
|
||||
"""
|
||||
This displayable has two children. It interpolates between the positions
|
||||
of its two children to place them on the screen.
|
||||
@@ -457,7 +457,7 @@ def MoveTransition(delay, old_widget=None, new_widget=None, enter=None, leave=No
|
||||
|
||||
As only layers have tags, MoveTransitions can only be applied to a single
|
||||
layer or all layers at once, using the :ref:`with statement <with-statement>`.
|
||||
It will not work in other contexts such as :ref:`ATL <expression-statement>`,
|
||||
It will not work in other contexts such as :ref:`ATL <expression-atl-statement>`,
|
||||
:func:`ComposeTransition`, or other ways of applying transitions.
|
||||
|
||||
`delay`
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user