Compare commits
161 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e8e73ae2c2 | |||
| fb803ea05c | |||
| 04d9ea3016 | |||
| 1f26d829d1 | |||
| d9b30cf8a1 | |||
| ebcc0ce7dc | |||
| 8a2c6d4dc4 | |||
| 5c400fdbc9 | |||
| 34a011e79f | |||
| 7545b87364 | |||
| 72f0535fb3 | |||
| e776a4c3f5 | |||
| e439957896 | |||
| dffbe3521c | |||
| 1fad559548 | |||
| e6fec6ea50 | |||
| a427a6611b | |||
| 9932e4e892 | |||
| e702a15785 | |||
| cf7910687f | |||
| 2a6c719678 | |||
| 9f08129084 | |||
| 40e81d44a4 | |||
| 40ac61bf23 | |||
| 16372810e5 | |||
| f2a76c5872 | |||
| 3350ffa713 | |||
| bf0634f738 | |||
| 13f9b3d04c | |||
| 1d96e7a16f | |||
| df524315a8 | |||
| 7c082f0fb5 | |||
| 6d637237ff | |||
| 2f6556052a | |||
| 9a6448dd94 | |||
| 54f62730a2 | |||
| ca7c78dbcd | |||
| 16fb5ffb85 | |||
| aea1129812 | |||
| 46320ddcd4 | |||
| a65cdd6f0e | |||
| 9d275e9349 | |||
| 534fb0f9f6 | |||
| 005be375fc | |||
| b2d5dd87fa | |||
| a6b99364aa | |||
| e5c2e7f88c | |||
| 907ba19bea | |||
| 74d8c5c398 | |||
| f65c1a1af3 | |||
| b740e723a3 | |||
| 5bdb9b5459 | |||
| d70d6b8f26 | |||
| 44cdc09745 | |||
| 1291c3cb26 | |||
| 493edfbb82 | |||
| ec6277588d | |||
| 3f057f159a | |||
| 420e167d41 | |||
| 2828f3b517 | |||
| c378247a39 | |||
| 9ab745db85 | |||
| f92022308d | |||
| 4f0e882d97 | |||
| 46f62133d1 | |||
| 77889607f3 | |||
| f92dcd8068 | |||
| 815a3999b4 | |||
| 11c9b8f34b | |||
| cfe892405e | |||
| d2b5f2fdc9 | |||
| 8c4387bae4 | |||
| bfb3955e9e | |||
| 1498f625df | |||
| 9523222b37 | |||
| 9c197abca3 | |||
| d3dbc40749 | |||
| 6362ad8550 | |||
| 04a73d2ad4 | |||
| ddfcf67486 | |||
| 8f5a32deeb | |||
| 5155c08f54 | |||
| 7511c3ff42 | |||
| e4ff027499 | |||
| 1cccdbdaaf | |||
| 1a100a0006 | |||
| 2863bb2d88 | |||
| 14f653f0a5 | |||
| 93fe44fa94 | |||
| e73c750ef1 | |||
| b929fa838c | |||
| d25bd3c01f | |||
| 1a9870ad5a | |||
| fbbc8e0e34 | |||
| cc77af4d9f | |||
| 65ff148898 | |||
| 24ad7c9a01 | |||
| 6ce6a877a5 | |||
| 65bceeddba | |||
| 77cb945b9f | |||
| eab3d2dab4 | |||
| 4e3d66b975 | |||
| a192a352e1 | |||
| c4e9a64b1e | |||
| 533cbe10e3 | |||
| c492f2f8e6 | |||
| 9249c6ffd4 | |||
| 98609fe72f | |||
| 03bf276e15 | |||
| d52fa74ffd | |||
| 15d2e037fe | |||
| 2da6f8cab0 | |||
| 8c331b336e | |||
| 8e90d2f6ab | |||
| 01c9e137e0 | |||
| 47418aa92d | |||
| c998b456a1 | |||
| bb34b38432 | |||
| ca1871863b | |||
| 9475a0f4b8 | |||
| 606a196e52 | |||
| bcfdb73715 | |||
| f8673861aa | |||
| 1947db8acd | |||
| 260b8f5f8d | |||
| e5f58fe935 | |||
| cfee63b880 | |||
| ee817a32f4 | |||
| 8039704b0d | |||
| c0ff662d60 | |||
| bfd6f2c9a8 | |||
| faa9155c88 | |||
| 444d002304 | |||
| 2f56256a82 | |||
| 16cba5ef11 | |||
| 53b9879a7a | |||
| a628b2dba3 | |||
| e45a3b18ae | |||
| a3b8f75a8d | |||
| a516bda700 | |||
| 44e23a9665 | |||
| 45e5535e89 | |||
| 652a06c1a8 | |||
| e48cd58b2c | |||
| 8f2c27979a | |||
| 0d7e5ec9e5 | |||
| d89c89f368 | |||
| ec48202e27 | |||
| 245576331b | |||
| d426c80761 | |||
| 303dadf377 | |||
| f50be5712d | |||
| ea76473e81 | |||
| 6662238338 | |||
| c33f5c4d3d | |||
| 54fb3d9be8 | |||
| 5c0f86c19e | |||
| 1e5c227909 | |||
| 6c8996207b | |||
| cd8b496876 | |||
| 2379d75173 |
+1
-1
@@ -112,7 +112,7 @@ link in a nightly build, or compile the modules as described above. You'll
|
||||
also need the `Sphinx <https://www.sphinx-doc.org>`_ documentation generator.
|
||||
If you have pip working, install Sphinx using::
|
||||
|
||||
pip install -U sphinx sphinx-bootstrap-theme
|
||||
pip install -U sphinx sphinx_rtd_theme sphinx_rtd_dark_mode
|
||||
|
||||
Once Sphinx is installed, change into the ``sphinx`` directory inside the
|
||||
Ren'Py checkout and run::
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"start_a170b500": {
|
||||
"area": [
|
||||
53,
|
||||
120,
|
||||
426,
|
||||
120
|
||||
]
|
||||
},
|
||||
"start_f41f55d7": {
|
||||
"area": [
|
||||
106,
|
||||
60,
|
||||
426,
|
||||
120
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 6.7 KiB |
+39
-12
@@ -1409,12 +1409,12 @@ style nvl_button_text:
|
||||
|
||||
## Bubble screen ###############################################################
|
||||
##
|
||||
## 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
|
||||
## 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.
|
||||
##
|
||||
## https://www.renpy.org/doc/html/screen_special.html#bubble
|
||||
## https://www.renpy.org/doc/html/bubble.html#bubble-screen
|
||||
|
||||
screen bubble(who, what):
|
||||
style_prefix "bubble"
|
||||
@@ -1434,22 +1434,18 @@ screen bubble(who, what):
|
||||
text what:
|
||||
id "what"
|
||||
|
||||
|
||||
style bubble_window is empty
|
||||
style bubble_namebox is empty
|
||||
style bubble_who is default
|
||||
style bubble_what is default
|
||||
|
||||
|
||||
style bubble_window:
|
||||
background Frame("gui/bubble.png", 15, 15, 15, 15)
|
||||
padding (15, 10)
|
||||
xpadding 30
|
||||
top_padding 5
|
||||
bottom_padding 5
|
||||
|
||||
style bubble_namebox:
|
||||
background Frame("gui/bubble.png", 15, 15, 15, 15)
|
||||
pos (-15, -5)
|
||||
anchor (0.0, 1.0)
|
||||
padding (15, 10)
|
||||
xalign 0.5
|
||||
|
||||
style bubble_who:
|
||||
xalign 0.5
|
||||
@@ -1458,10 +1454,41 @@ style bubble_who:
|
||||
|
||||
style bubble_what:
|
||||
align (0.5, 0.5)
|
||||
textalign 0.5
|
||||
text_align 0.5
|
||||
layout "subtitle"
|
||||
color "#000"
|
||||
|
||||
define bubble.frame = Frame("gui/bubble.png", 55, 55, 55, 95)
|
||||
|
||||
define bubble.properties = {
|
||||
"bottom_left" : {
|
||||
"window_background" : Transform(bubble.frame, xzoom=1, yzoom=1),
|
||||
"window_bottom_padding" : 27,
|
||||
},
|
||||
|
||||
"bottom_right" : {
|
||||
"window_background" : Transform(bubble.frame, xzoom=-1, yzoom=1),
|
||||
"window_bottom_padding" : 27,
|
||||
},
|
||||
|
||||
"top_left" : {
|
||||
"window_background" : Transform(bubble.frame, xzoom=1, yzoom=-1),
|
||||
"window_top_padding" : 27,
|
||||
},
|
||||
|
||||
"top_right" : {
|
||||
"window_background" : Transform(bubble.frame, xzoom=-1, yzoom=-1),
|
||||
"window_top_padding" : 27,
|
||||
},
|
||||
}
|
||||
|
||||
define bubble.expand_area = {
|
||||
"bottom_left" : (0, 0, 0, 22),
|
||||
"bottom_right" : (0, 0, 0, 22),
|
||||
"top_left" : (0, 22, 0, 0),
|
||||
"top_right" : (0, 22, 0, 0),
|
||||
}
|
||||
|
||||
|
||||
|
||||
################################################################################
|
||||
|
||||
@@ -31,8 +31,10 @@ init python:
|
||||
ANDROID_NO_BUNDLE = 6
|
||||
ANDROID_OK = 7
|
||||
|
||||
JDK_REQUIREMENT=8
|
||||
|
||||
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 8 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://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot}download and install the JDK{/a}, 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.")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
|
||||
# This file contains strings used by RAPT, so the Ren'Py translation framework
|
||||
# can find them. It's automatically generated by rapt/update_translations.py, and
|
||||
# hence should not be changed by hand.
|
||||
@@ -63,11 +63,12 @@ init python hide:
|
||||
__("I found an android.keystore file in the rapt directory. Do you want to use this file?")
|
||||
__("I can create an application signing key for you. This key is required to create Universal APK for sideloading and stores other than Google Play.\n\nDo you want to create a key?")
|
||||
__("I will create the key in the android.keystore file.\n\nYou need to back this file up. If you lose it, you will not be able to upgrade your application.\n\nYou also need to keep the key safe. If evil people get this file, they could make fake versions of your application, and potentially steal your users' data.\n\nWill you make a backup of android.keystore, and keep it in a safe place?")
|
||||
__("\n\nSaying 'No' will prevent key creation.")
|
||||
__("Could not create android.keystore. Is keytool in your path?")
|
||||
__("I've finished creating android.keystore. Please back it up, and keep it in a safe place.")
|
||||
__("I found a bundle.keystore file in the rapt directory. Do you want to use this file?")
|
||||
__("I can create a bundle signing key for you. This key is required to build an Android App Bundle (AAB) for upload to Google Play.\n\nDo you want to create a key?")
|
||||
__("I will create the key in the bundle.keystore file.\n\nYou need to back this file up. If you lose it, you will not be able to upgrade your application.\n\nYou also need to keep the key safe. If evil people get this file, they could make fake versions of your application, and potentially steal your users' data.\n\nWill you make a backup of bundle.keystore, and keep it in a safe place?")
|
||||
__("\n\nSaying 'No' will prevent key creation.")
|
||||
__("Could not create bundle.keystore. Is keytool in your path?")
|
||||
__("I've opened the directory containing android.keystore and bundle.keystore. Please back them up, and keep them in a safe place.")
|
||||
__("It looks like you're ready to start packaging games.")
|
||||
|
||||
@@ -214,7 +214,8 @@ class CodeGenerator(object):
|
||||
'gui.insensitive_color' : repr(self.p.insensitive_color.hexcode),
|
||||
'gui.text_color' : repr(self.p.text_color.hexcode),
|
||||
'gui.interface_text_color' : repr(self.p.text_color.hexcode),
|
||||
'gui.choice_text_color' : repr(self.p.choice_color.hexcode),
|
||||
'gui.choice_button_text_idle_color' : repr(self.p.idle_color.hexcode),
|
||||
'gui.choice_button_text_insensitive_color' : repr(self.p.insensitive_color.hexcode),
|
||||
}
|
||||
|
||||
self.update_defines(replacements, language_defines[self.p.language])
|
||||
|
||||
@@ -67,8 +67,8 @@ class GuiParameters(object):
|
||||
if light:
|
||||
|
||||
self.selected_color = Color("#555555")
|
||||
self.idle_color = Color("#aaaaaa")
|
||||
self.idle_small_color = Color("#888888")
|
||||
self.idle_color = Color("#707070")
|
||||
self.idle_small_color = Color("#606060")
|
||||
self.text_color = Color("#404040")
|
||||
self.choice_color = Color("#cccccc")
|
||||
|
||||
|
||||
@@ -200,7 +200,15 @@ screen common:
|
||||
add SPACER
|
||||
|
||||
for v, l in choices:
|
||||
textbutton l action SetScreenVariable("selected", v)
|
||||
textbutton l:
|
||||
action SetScreenVariable("selected", v)
|
||||
selected_background REVERSE_IDLE
|
||||
selected_hover_background REVERSE_HOVER
|
||||
xpadding 20
|
||||
size_group "choice"
|
||||
text_selected_idle_color REVERSE_TEXT
|
||||
text_selected_hover_color REVERSE_TEXT
|
||||
text_xalign 0.5
|
||||
|
||||
if selected is not None:
|
||||
$ continue_ = Return(selected)
|
||||
|
||||
+4
-22
@@ -97,32 +97,14 @@ label itch:
|
||||
files = [ ]
|
||||
|
||||
for fn in os.listdir(destination):
|
||||
fn = os.path.join(destination, fn)
|
||||
|
||||
if fn.endswith("-all.zip"):
|
||||
channel = "win-osx-linux"
|
||||
|
||||
elif fn.endswith("-market.zip"):
|
||||
channel = "win-osx-linux"
|
||||
|
||||
elif fn.endswith("-pc.zip"):
|
||||
channel = "win-linux"
|
||||
|
||||
elif fn.endswith("-win.zip"):
|
||||
channel = "win"
|
||||
|
||||
elif fn.endswith("-mac.zip"):
|
||||
channel = "osx"
|
||||
|
||||
elif fn.endswith("-linux.tar.bz2"):
|
||||
channel = "linux"
|
||||
|
||||
elif fn.endswith("-release.apk"):
|
||||
channel = "android"
|
||||
|
||||
for pattern, channel in reversed(build['itch_channels']):
|
||||
if fnmatch.fnmatch(fn, pattern):
|
||||
break
|
||||
else:
|
||||
continue
|
||||
|
||||
fn = os.path.join(destination, fn)
|
||||
files.append((fn, channel))
|
||||
|
||||
if not os.path.exists(destination):
|
||||
|
||||
@@ -449,3 +449,6 @@ python early:
|
||||
# Since the launcher can be run directly or can be run from the SDK directory,
|
||||
# uneliding files needs to be handled slightly differently.
|
||||
define config.alternate_unelide_path = os.path.join(config.basedir, "launcher")
|
||||
|
||||
# Disable skipping.
|
||||
define config.allow_skipping = False
|
||||
|
||||
@@ -248,8 +248,8 @@ screen preferences():
|
||||
add HALF_SPACER
|
||||
|
||||
textbutton _("Default theme") style "l_checkbox" action [SetField(persistent, "theme", None), RestartAtPreferences() ]
|
||||
textbutton _("Dark theme") style "l_checkbox" action [SetField(persistent, "theme", "dark", None), RestartAtPreferences()]
|
||||
textbutton _("Custom theme") style "l_checkbox" action [SetField(persistent, "theme", "custom", None), RestartAtPreferences()]
|
||||
textbutton _("Dark theme") style "l_checkbox" action [SetField(persistent, "theme", "dark"), RestartAtPreferences()]
|
||||
textbutton _("Custom theme") style "l_checkbox" action [SetField(persistent, "theme", "custom"), RestartAtPreferences()]
|
||||
|
||||
add SPACER
|
||||
|
||||
|
||||
@@ -897,3 +897,11 @@ translate french strings:
|
||||
# renpy/common/00webvideo.rpy:5
|
||||
old "Touch to play the video."
|
||||
new "Touchez pour jouer la vidéo."
|
||||
|
||||
# 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 "Cette sauvegarde a été créée sur un autre appareil. Des faux fichiers de sauvegarde peuvent être utilisés pour endommager votre ordinateur. Faites-vous confiance au créateur de cette sauvegarde et à tous ceux qui auraient pu modifier le fichier ?"
|
||||
|
||||
# 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 "Faites-vous confiance à l'appareil sur lequel cette sauvegarde a été créée ? Vous ne devriez dire oui que si vous en êtes l'unique utilisateur ."
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
|
||||
# _developer/developer.rpym:532
|
||||
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}✔ image prédite (bon){/color}\n{color=#fcc}✘ image prédite (mauvais){/color}\n{color=#fff}Glissez pour bouger.{/color}"
|
||||
new "\n{color=#cfc}✔ image prédite (bien){/color}\n{color=#fcc}✘ image non prédite (pas bien){/color}\n{color=#fff}Glissez pour déplacer.{/color}"
|
||||
|
||||
# _developer/inspector.rpym:38
|
||||
old "Displayable Inspector"
|
||||
|
||||
@@ -455,3 +455,7 @@
|
||||
# gui/game/gui.rpy:441
|
||||
old "## Change the size and spacing of various things."
|
||||
new "## Changer la taille et l'espacement de diverses choses."
|
||||
|
||||
# gui/game/gui.rpy:14
|
||||
old "## Enable checks for invalid or unstable properties in screens or transforms"
|
||||
new "## Active la vérification de propriétés invalides ou instables dans les screens et transforms"
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
# android.rpy:32
|
||||
old "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."
|
||||
new "RAPT a été installé, mais vous devez installer le kit de développement Android pour pouvoir compiler les paquets Android. Choisissez « installer le kit de développement et créer les clés » pour cela."
|
||||
new "RAPT a été installé, mais vous devez installer le kit de développement Android pour pouvoir compiler les paquets Android, avec « Installer le SDK »."
|
||||
|
||||
# android.rpy:33
|
||||
old "RAPT has been installed, but a key hasn't been configured. Please create a new key, or restore android.keystore."
|
||||
@@ -65,11 +65,11 @@
|
||||
|
||||
# android.rpy:39
|
||||
old "Attempts to emulate a televison-based Android console, like the OUYA or Fire TV.\n\nController input is mapped to the arrow keys, Enter is mapped to the select button, Escape is mapped to the menu button, and PageUp is mapped to the back button."
|
||||
new "Tentative d’émulation d'une console télé basée sur Android, comme OUYA ou Fire TV.\n\n Le contrôleur est émulé par les touches fléchées, le bouton select par la touche Entrée, le bouton menu par la touche Echap, et le bouton retour par la touche PageUp."
|
||||
new "Tentative d’émulation d'une console télé basée sur Android, comme OUYA ou Fire TV.\n\nLa manette est émulée par les touches fléchées, le bouton select par la touche Entrée, le bouton menu par la touche Echap, et le bouton retour par la touche PageUp."
|
||||
|
||||
# android.rpy:41
|
||||
old "Downloads and installs the Android SDK and supporting packages. Optionally, generates the keys required to sign the package."
|
||||
new "Télécharge et installe le kit de développement Android et les paquets supportés. Optionnellement, génère les clés requises pour signer le paquet."
|
||||
new "Télécharge et installe le kit de développement Android et les paquets requis. Optionnellement, génère les clés requises pour signer le paquet."
|
||||
|
||||
# android.rpy:42
|
||||
old "Configures the package name, version, and other information about this project."
|
||||
@@ -1192,8 +1192,8 @@
|
||||
new "Le nom de fichier ne peut pas être vide."
|
||||
|
||||
# game/android.rpy:31
|
||||
old "A 64-bit/x64 Java 8 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://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot}download and install the JDK{/a}, then restart the Ren'Py launcher."
|
||||
new "Un kit de développement Java 8 64-bit/x64 est requis pour construire des paquets Android depuis Windows. Le JDK est différent du JRE, donc il est possible d'avoir Java sans avoir le JDK.\n\n{a=https://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot}Téléchargez et installez le JDK{/a}, puis redémarrez Ren'Py."
|
||||
old "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."
|
||||
new "Un kit de développement Java [JDK_REQUIREMENT] 64-bit/x64 est requis pour construire des paquets Android depuis Windows. Le JDK est différent du JRE, donc il est possible d'avoir Java sans avoir le JDK.\n\n{a=https://www.renpy.org/jdk/[JDK_REQUIREMENT]}Téléchargez et installez le JDK{/a}, puis redémarrez Ren'Py."
|
||||
|
||||
# game/android.rpy:50
|
||||
old "Selects the Debug build, which can be accessed through Android Studio. Changing between debug and release builds requires an uninstall from your device."
|
||||
@@ -2086,3 +2086,47 @@
|
||||
# game/web.rpy:484
|
||||
old "Creating package..."
|
||||
new "Création du package..."
|
||||
|
||||
# game/android.rpy:37
|
||||
old "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."
|
||||
new "RAPT a été installé, mais aucune clé n’a été configurée. Veuillez générer de nouvelles clés, ou copier android.keystore and bundle.keystore dans le dossier racine du projet."
|
||||
|
||||
# game/android.rpy:44
|
||||
old "Attempts to emulate a televison-based Android console.\n\nController input is mapped to the arrow keys, Enter is mapped to the select button, Escape is mapped to the menu button, and PageUp is mapped to the back button."
|
||||
new "Tentative d’émulation d'une console télé basée sur Android.\n\nLa manette est émulée par les touches fléchées, le bouton select par la touche Entrée, le bouton menu par la touche Echap, et le bouton retour par la touche PageUp."
|
||||
|
||||
# game/android.rpy:46
|
||||
old "Downloads and installs the Android SDK and supporting packages."
|
||||
new "Télécharge et installe le kit de développement Android et les paquets requis."
|
||||
|
||||
# game/android.rpy:47
|
||||
old "Generates the keys required to sign the package."
|
||||
new "Génère les clés requises pour signer le package."
|
||||
|
||||
# game/android.rpy:381
|
||||
old "Install SDK"
|
||||
new "Installer le SDK"
|
||||
|
||||
# game/android.rpy:385
|
||||
old "Generate Keys"
|
||||
new "Générer les clés"
|
||||
|
||||
# game/androidstrings.rpy:32
|
||||
old "How much RAM (in GB) do you want to allocate to Gradle?\nThis must be a positive integer number."
|
||||
new "Combien de RAM (in GB) voulez-vous allouer à Gradle ?\nEntrez un nombre entier positif."
|
||||
|
||||
# game/androidstrings.rpy:33
|
||||
old "The RAM size must contain only numbers and be positive."
|
||||
new "La taille de RAM ne doit contenir que des chiffres et être positive."
|
||||
|
||||
# game/androidstrings.rpy:63
|
||||
old "I found an android.keystore file in the rapt directory. Do you want to use this file?"
|
||||
new "J'ai trouvé un fichier android.keystore dans le dossier \"rapt\". Voulez-vous l'utiliser ?"
|
||||
|
||||
# game/androidstrings.rpy:66
|
||||
old "\n\nSaying 'No' will prevent key creation."
|
||||
new "\n\nRépondre \"Non\" empêchera la création de clés."
|
||||
|
||||
# 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 ?"
|
||||
|
||||
@@ -731,3 +731,11 @@
|
||||
# gui/game/screens.rpy:1404
|
||||
old "## https://www.renpy.org/doc/html/screen_special.html#bubble"
|
||||
new "## https://www.renpy.org/doc/html/screen_special.html#bubble"
|
||||
|
||||
# gui/game/screens.rpy:676
|
||||
old "Upload Sync"
|
||||
new "Uploader Sync"
|
||||
|
||||
# gui/game/screens.rpy:680
|
||||
old "Download Sync"
|
||||
new "Télécharger Sync"
|
||||
|
||||
@@ -1199,15 +1199,13 @@
|
||||
old "Proceed"
|
||||
new "Fortsetzen"
|
||||
|
||||
translate german strings:
|
||||
|
||||
# game/add_file.rpy:37
|
||||
old "The file name may not be empty."
|
||||
new "Der Dateiname darf nicht leer sein."
|
||||
|
||||
# game/android.rpy:35
|
||||
old "A 64-bit/x64 Java 8 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://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot}download and install the JDK{/a}, then restart the Ren'Py launcher."
|
||||
new "A 64-bit/x64 Java 8 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://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot}download and install the JDK{/a}, then restart the Ren'Py launcher."
|
||||
old "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."
|
||||
new "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."
|
||||
|
||||
# game/android.rpy:38
|
||||
old "RAPT has been installed, but a bundle key hasn't been configured. Please create a new key, or restore bundle.keystore."
|
||||
@@ -1932,4 +1930,3 @@ translate german strings:
|
||||
# game/web.rpy:348
|
||||
old "Before packaging web apps, you'll need to download RenPyWeb, Ren'Py's web support. Would you like to download RenPyWeb now?"
|
||||
new "Before packaging web apps, you'll need to download RenPyWeb, Ren'Py's web support. Would you like to download RenPyWeb now?"
|
||||
|
||||
|
||||
@@ -1320,8 +1320,8 @@
|
||||
new "ファイル名が与えられていません。"
|
||||
|
||||
# game/android.rpy:31
|
||||
old "A 64-bit/x64 Java 8 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=http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html}download and install the JDK{/a}, then restart the Ren'Py launcher."
|
||||
new "ウィンドウズでアンドロイドパッケージをビルドするには、 64-bit/x64 Java 8 Development Kit が必要になります。 JDK は JRE とは違うもので、Java には含まれていません。\n\n{a=http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html}JDK{/a}をダンロード、インストールしてから Ren'Py ランチャーを再起動してください。"
|
||||
old "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."
|
||||
new "ウィンドウズでアンドロイドパッケージをビルドするには、 64-bit/x64 Java 8 Development Kit が必要になります。 JDK は JRE とは違うもので、Java には含まれていません。\n\n{a=https://www.renpy.org/jdk/[JDK_REQUIREMENT]}JDK{/a}をダンロード、インストールしてから Ren'Py ランチャーを再起動してください。"
|
||||
|
||||
# game/android.rpy:50
|
||||
old "Selects the Debug build, which can be accessed through Android Studio. Changing between debug and release builds requires an uninstall from your device."
|
||||
@@ -2094,4 +2094,3 @@
|
||||
# game/web.rpy:344
|
||||
old "We will restore support in a future release of Ren'Py 8. Until then, please use Ren'Py 7 for web support."
|
||||
new "Ren'Py 8 の将来のリリースでサポートを復活させます。それまではRen'Py 7 をウェブプラットフォームに使用ください。"
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
new "안드로이드 패키지를 만드려면, RAPT 파일을 내려받고 렌파이 디렉터리 안에 압축 해제하세요. 그다음 렌파이를 재시작하세요."
|
||||
|
||||
# game/android.rpy:31
|
||||
old "A 64-bit/x64 Java 8 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=http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html}download and install the JDK{/a}, then restart the Ren'Py launcher."
|
||||
new "윈도우에서 안드로이드 패키지를 만드려면 64비트 JDK가 필요합니다. JDK는 JRE와 다르므로 PC에 JDK가 없는 자바가 설치되어 있을 수 있습니다.\n\n{a=http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html}JDK를 내려받아 설치한 뒤{/a}, 렌파이를 재시작해주세요."
|
||||
old "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."
|
||||
new "윈도우에서 안드로이드 패키지를 만드려면 64비트 JDK가 필요합니다. JDK는 JRE와 다르므로 PC에 JDK가 없는 자바가 설치되어 있을 수 있습니다.\n\n{a=https://www.renpy.org/jdk/[JDK_REQUIREMENT]}JDK를 내려받아 설치한 뒤{/a}, 렌파이를 재시작해주세요."
|
||||
|
||||
# game/android.rpy:32
|
||||
old "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."
|
||||
|
||||
@@ -38,8 +38,8 @@ translate piglatin strings:
|
||||
new "Otay uildbay Ndroidaay ackagespay, easeplay ownloadday Aptray, unzipay itay, anday aceplay itay intoay hetay Enray'Ypay irectoryday. Hentay estartray hetay Enray'Ypay auncherlay."
|
||||
|
||||
# game/android.rpy:35
|
||||
old "A 64-bit/x64 Java 8 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://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot}download and install the JDK{/a}, then restart the Ren'Py launcher."
|
||||
new "Aay 46ay-itbay/64xay Avajay 8ay Evelopmentday Itkay isay equiredray otay uildbay Ndroidaay ackagespay onay Indowsway. Hetay Dkjay isay ifferentday omfray hetay Rejay, osay itay'say ossiblepay ouyay avehay Avajay ithoutway avinghay hetay Dkjay.\n\nLeasepay {a=https://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot}ownloadday anday installay hetay Dkjay{/a}, hentay estartray hetay Enray'Ypay auncherlay."
|
||||
old "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."
|
||||
new "Aay 46ay-itbay/64xay Avajay 8ay Evelopmentday Itkay isay equiredray otay uildbay Ndroidaay ackagespay onay Indowsway. Hetay Dkjay isay ifferentday omfray hetay Rejay, osay itay'say ossiblepay ouyay avehay Avajay ithoutway avinghay hetay Dkjay.\n\nLeasepay {a=https://www.renpy.org/jdk/[JDK_REQUIREMENT]}ownloadday anday installay hetay Dkjay{/a}, hentay estartray hetay Enray'Ypay auncherlay."
|
||||
|
||||
# game/android.rpy:36
|
||||
old "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."
|
||||
@@ -1812,4 +1812,3 @@ translate piglatin strings:
|
||||
# game/web.rpy:348
|
||||
old "Before packaging web apps, you'll need to download RenPyWeb, Ren'Py's web support. Would you like to download RenPyWeb now?"
|
||||
new "Eforebay ackagingpay ebway appsay, ouyay'llay eednay otay ownloadday Enpywebray, Enray'Ypay'say ebway upportsay. Ouldway ouyay ikelay otay ownloadday Enpywebray ownay?"
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@
|
||||
new "Aby zbudować pakiet dla Androida, pobierz RAPT, rozpakuj i umieść w katalogu Ren'Py. Następnie uruchom ponownie program Ren'py."
|
||||
|
||||
# game/android.rpy:31
|
||||
old "A 64-bit/x64 Java 8 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://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot}download and install the JDK{/a}, then restart the Ren'Py launcher."
|
||||
new "Tworzenie pakietów dla systemu Android w systemie Windows wymaga pakietu 64-bit/x64 Java Development Kit. JDK różni się od JRE, istnieje możliwość, że posiadasz Javę bez JDK.\n\n{a=https://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot}Pobierz i zainstaluj JDK{/a}, a następnie uruchom ponownie program Ren'py."
|
||||
old "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."
|
||||
new "Tworzenie pakietów dla systemu Android w systemie Windows wymaga pakietu 64-bit/x64 Java Development Kit. JDK różni się od JRE, istnieje możliwość, że posiadasz Javę bez JDK.\n\n{a=https://www.renpy.org/jdk/[JDK_REQUIREMENT]}Pobierz i zainstaluj JDK{/a}, a następnie uruchom ponownie program Ren'py."
|
||||
|
||||
# game/android.rpy:32
|
||||
old "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."
|
||||
|
||||
@@ -40,8 +40,8 @@
|
||||
new "Чтобы построить Android-пакет, пожалуйста, загрузите RAPT, разархивируйте его, и поместите в директорию Ren'Py. Затем перезагрузите лаунчер Ren'Py."
|
||||
|
||||
# android.rpy:31
|
||||
old "A 64-bit/x64 Java 8 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=http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html}download and install the JDK{/a}, then restart the Ren'Py launcher."
|
||||
new "Чтобы построить Android-пакеты на Windows требуется 64-битный инструментарий разработки Java 8. JDK отличен от JRE, и возможно, у вас есть Java без JDK.\n\nПожалуйста, {a=http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html}загрузите и установите JDK{/a}, и перезапустите лаунчер Ren'Py."
|
||||
old "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."
|
||||
new "Чтобы построить Android-пакеты на Windows требуется 64-битный инструментарий разработки Java 8. JDK отличен от JRE, и возможно, у вас есть Java без JDK.\n\nПожалуйста, {a=https://www.renpy.org/jdk/[JDK_REQUIREMENT]}загрузите и установите JDK{/a}, и перезапустите лаунчер Ren'Py."
|
||||
|
||||
# android.rpy:32
|
||||
old "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."
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
new "要构建安卓应用包,请下载 RAPT,并解压到 Ren'Py 目录中。之后重启 Ren'Py。"
|
||||
|
||||
# game/android.rpy:35
|
||||
old "A 64-bit/x64 Java 8 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://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot}download and install the JDK{/a}, then restart the Ren'Py launcher."
|
||||
new "在 Windows 中构建安卓应用包需要 64 位/x64 Java 8 开发套件(JDK)。JDK 不同于 JRE,所以您可能已安装过 Java 但尚未安装 JDK。\n\n请{a=https://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot}下载并安装 JDK{/a},然后重启 Ren'Py。"
|
||||
old "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."
|
||||
new "在 Windows 中构建安卓应用包需要 64 位/x64 Java 8 开发套件(JDK)。JDK 不同于 JRE,所以您可能已安装过 Java 但尚未安装 JDK。\n\n请{a=https://www.renpy.org/jdk/[JDK_REQUIREMENT]}下载并安装 JDK{/a},然后重启 Ren'Py。"
|
||||
|
||||
# game/android.rpy:36
|
||||
old "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."
|
||||
|
||||
@@ -431,3 +431,7 @@
|
||||
# gui.rpy:457
|
||||
old "## NVL-mode."
|
||||
new "## Modo-NVL"
|
||||
|
||||
# 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."
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
new "Para construir un paquete para Android, por favor descarga RAPT, descomprímelo y colócalo dentro de la carpeta de Ren'Py. Después reinicia el lanzador de Ren'Py."
|
||||
|
||||
# game/android.rpy:31
|
||||
old "A 64-bit/x64 Java 8 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://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot}download and install the JDK{/a}, then restart the Ren'Py launcher."
|
||||
new "Se requiere un kit de desarrollo Java 8 de 64 bits/x64 para construir paquetes de Android en Windows. El JDK es diferente del JRE, por lo que es posible que tengas Java sin el JDK.\n\nPor favor, {a=https://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot}descarga e instala el JDK{/a}, luego reinicia el lanzador de Ren'Py."
|
||||
old "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."
|
||||
new "Se requiere un kit de desarrollo Java 8 de 64 bits/x64 para construir paquetes de Android en Windows. El JDK es diferente del JRE, por lo que es posible que tengas Java sin el JDK.\n\nPor favor, {a=https://www.renpy.org/jdk/[JDK_REQUIREMENT]}descarga e instala el JDK{/a}, luego reinicia el lanzador de Ren'Py."
|
||||
|
||||
# android.rpy:32
|
||||
old "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."
|
||||
@@ -2060,7 +2060,7 @@ translate spanish strings:
|
||||
# game/preferences.rpy:327
|
||||
old "{#in language font}Start using Ren'Py in [lang_name]"
|
||||
new "{font=fonts/Roboto-Light.ttf}Empieza a usar Ren'Py en [lang_name]{/font}"
|
||||
|
||||
|
||||
# game/distribute_gui.rpy:231
|
||||
old "(DLC)"
|
||||
new "(DLC)"
|
||||
@@ -2072,3 +2072,47 @@ translate spanish strings:
|
||||
# game/web.rpy:484
|
||||
old "Creating package..."
|
||||
new "Creando paquete..."
|
||||
|
||||
# game/android.rpy:39
|
||||
old "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."
|
||||
new "RAPT ha sido instalado, pero no se ha configurado una clave. Por favor, genera nuevas claves, o copia android.keystore y bundle.keystore al directorio base."
|
||||
|
||||
# game/android.rpy:46
|
||||
old "Attempts to emulate a televison-based Android console.\n\nController input is mapped to the arrow keys, Enter is mapped to the select button, Escape is mapped to the menu button, and PageUp is mapped to the back button."
|
||||
new "Intenta emular una consola Android basada en televisión.\n\nLa entrada del controlador se asigna a las teclas de flecha, Enter se asigna al botón de selección, Escape se asigna al botón de menú y PageUp se asigna al botón de retroceso."
|
||||
|
||||
# game/android.rpy:48
|
||||
old "Downloads and installs the Android SDK and supporting packages."
|
||||
new "Descarga e instala el SDK de Android y los paquetes de soporte."
|
||||
|
||||
# game/android.rpy:49
|
||||
old "Generates the keys required to sign the package."
|
||||
new "Genera las claves necesarias para firmar el paquete."
|
||||
|
||||
# game/android.rpy:383
|
||||
old "Install SDK"
|
||||
new "Instalar SDK"
|
||||
|
||||
# game/android.rpy:387
|
||||
old "Generate Keys"
|
||||
new "Generar claves"
|
||||
|
||||
# game/androidstrings.rpy:32
|
||||
old "How much RAM (in GB) do you want to allocate to Gradle?\nThis must be a positive integer number."
|
||||
new "¿Cuánta RAM (en GB) quiere asignar a Gradle?\nDebe ser un número entero positivo."
|
||||
|
||||
# game/androidstrings.rpy:33
|
||||
old "The RAM size must contain only numbers and be positive."
|
||||
new "El tamaño de la RAM debe contener sólo números y ser positivo."
|
||||
|
||||
# game/androidstrings.rpy:63
|
||||
old "I found an android.keystore file in the rapt directory. Do you want to use this file?"
|
||||
new "He encontrado un archivo android.keystore en el directorio rapt. ¿Quieres usar este archivo?"
|
||||
|
||||
# game/androidstrings.rpy:66
|
||||
old "\n\nSaying 'No' will prevent key creation."
|
||||
new "\n\nDecir 'No' impedirá la creación de claves."
|
||||
|
||||
# game/androidstrings.rpy:69
|
||||
old "I found a bundle.keystore file in the rapt directory. Do you want to use this file?"
|
||||
new "He encontrado un archivo bundle.keystore en el directorio rapt. ¿Quieres usar este archivo?"
|
||||
|
||||
@@ -692,3 +692,11 @@ translate spanish strings:
|
||||
# gui/game/screens.rpy:1404
|
||||
old "## https://www.renpy.org/doc/html/screen_special.html#bubble"
|
||||
new "## https://www.renpy.org/doc/html/screen_special.html#bubble"
|
||||
|
||||
# gui/game/screens.rpy:676
|
||||
old "Upload Sync"
|
||||
new "Subir Sync"
|
||||
|
||||
# gui/game/screens.rpy:680
|
||||
old "Download Sync"
|
||||
new "Descargar Sync"
|
||||
|
||||
@@ -40,8 +40,8 @@
|
||||
new "Android paketleri derlemek için lütfen RAPT'ı indirin, açın ve Ren'Py klasörüne atın. Ardından Ren'Py'ı yeniden başlatın."
|
||||
|
||||
# android.rpy:31
|
||||
old "A 64-bit/x64 Java 8 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=http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html}download and install the JDK{/a}, then restart the Ren'Py launcher."
|
||||
new "Windows'ta Android paketleri derlemek için bir 64-bit/x64 Java 8 Development Kit gerekir. JDK, JRE'den farklıdır, bu yüzden JDK olmayan bir Java kurulumunuz olabilir.\n\nLütfen JDK'yı {a=http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html}indirin ve kurun{/a}, ardından Ren'Py'ı yeniden başlatın."
|
||||
old "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."
|
||||
new "Windows'ta Android paketleri derlemek için bir 64-bit/x64 Java 8 Development Kit gerekir. JDK, JRE'den farklıdır, bu yüzden JDK olmayan bir Java kurulumunuz olabilir.\n\nLütfen JDK'yı {a=https://www.renpy.org/jdk/[JDK_REQUIREMENT]}indirin ve kurun{/a}, ardından Ren'Py'ı yeniden başlatın."
|
||||
|
||||
# android.rpy:32
|
||||
old "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."
|
||||
|
||||
@@ -38,8 +38,8 @@ translate ukrainian strings:
|
||||
new "Щоб створити пакети Android, завантажте RAPT, розархівуйте його та помістіть у каталог Ren'Py. Потім перезапустіть лаунчер Ren'Py."
|
||||
|
||||
# game/android.rpy:35
|
||||
old "A 64-bit/x64 Java 8 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://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot}download and install the JDK{/a}, then restart the Ren'Py launcher."
|
||||
new "Для створення пакетів Android у Windows потрібен 64-розрядний/x64 Java 8 Development Kit. JDK відрізняється від JRE, тому можливо, що у вас є Java без JDK.\n\nБудь ласка, {a=https://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot}завантажте та встановіть JDK{/a}, а потім перезапустіть лаунчер Ren'Py."
|
||||
old "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."
|
||||
new "Для створення пакетів Android у Windows потрібен 64-розрядний/x64 Java 8 Development Kit. JDK відрізняється від JRE, тому можливо, що у вас є Java без JDK.\n\nБудь ласка, {a=https://www.renpy.org/jdk/[JDK_REQUIREMENT]}завантажте та встановіть JDK{/a}, а потім перезапустіть лаунчер Ren'Py."
|
||||
|
||||
# game/android.rpy:36
|
||||
old "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."
|
||||
|
||||
@@ -21,6 +21,7 @@ renpy.styledata.style_selected_activate_functions gen/renpy.styledata.style_sele
|
||||
renpy.display.matrix gen/renpy.display.matrix.c
|
||||
renpy.display.render gen/renpy.display.render.c
|
||||
renpy.display.accelerator gen/renpy.display.accelerator.c
|
||||
renpy.display.quaternion gen/renpy.display.quaternion.c
|
||||
renpy.uguu.gl gen/renpy.uguu.gl.c
|
||||
renpy.uguu.uguu gen/renpy.uguu.uguu.c
|
||||
renpy.gl.gldraw gen/renpy.gl.gldraw.c
|
||||
|
||||
+22
-16
@@ -824,27 +824,33 @@ static SurfaceQueueEntry *decode_video_frame(MediaState *ms) {
|
||||
|
||||
SDL_Surface *sample = rgba_surface;
|
||||
|
||||
ms->sws = sws_getCachedContext(
|
||||
ms->sws,
|
||||
if (ms->sws == NULL) {
|
||||
ms->sws = sws_getContext(
|
||||
ms->video_decode_frame->width,
|
||||
ms->video_decode_frame->height,
|
||||
ms->video_decode_frame->format,
|
||||
|
||||
ms->video_decode_frame->width,
|
||||
ms->video_decode_frame->height,
|
||||
ms->video_decode_frame->format,
|
||||
ms->video_decode_frame->width,
|
||||
ms->video_decode_frame->height,
|
||||
get_pixel_format(sample),
|
||||
|
||||
ms->video_decode_frame->width,
|
||||
ms->video_decode_frame->height,
|
||||
get_pixel_format(rgba_surface),
|
||||
SWS_POINT | SWS_FULL_CHR_H_INP | SWS_FULL_CHR_H_INT,
|
||||
|
||||
SWS_POINT,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (!ms->sws) {
|
||||
ms->video_finished = 1;
|
||||
return NULL;
|
||||
if (!ms->sws) {
|
||||
ms->video_finished = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sws_setColorspaceDetails(ms->sws,
|
||||
sws_getCoefficients(SWS_CS_DEFAULT), 0,
|
||||
sws_getCoefficients(SWS_CS_DEFAULT), 0,
|
||||
0, 1 << 16, 1 << 16);
|
||||
}
|
||||
|
||||
SurfaceQueueEntry *rv = av_malloc(sizeof(SurfaceQueueEntry));
|
||||
|
||||
@@ -350,6 +350,13 @@ static inline void mix_sample(struct Channel *c, short left_in, short right_in,
|
||||
*right_out += right * volume;
|
||||
}
|
||||
|
||||
|
||||
/** If not NULL, this can be replaced with a function that will be called
|
||||
to generate audio. The functtio is called with a consistion of 2*length
|
||||
shorts, and should fill the buffer with audio data. */
|
||||
void (*RPS_generate_audio_c_function)(float *stream, int length) = NULL;
|
||||
|
||||
|
||||
static void callback(void *userdata, Uint8 *stream, int length) {
|
||||
|
||||
// Convert the length to samples.
|
||||
@@ -360,6 +367,10 @@ static void callback(void *userdata, Uint8 *stream, int length) {
|
||||
|
||||
memset(mix_buffer, 0, length * 2 * sizeof(float));
|
||||
|
||||
if (RPS_generate_audio_c_function) {
|
||||
RPS_generate_audio_c_function(mix_buffer, length);
|
||||
}
|
||||
|
||||
for (int channel = 0; channel < num_channels; channel++) {
|
||||
|
||||
// The number of samples that have been mixed.
|
||||
|
||||
@@ -58,4 +58,7 @@ void RPS_periodic(void);
|
||||
|
||||
char *RPS_get_error(void);
|
||||
|
||||
extern void (*RPS_generate_audio_c_function)(float *stream, int length);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -165,6 +165,7 @@ for p in generate_styles.prefixes:
|
||||
cython("renpy.display.matrix")
|
||||
cython("renpy.display.render", libs=[ 'z', 'm' ])
|
||||
cython("renpy.display.accelerator", libs=sdl + [ 'z', 'm' ])
|
||||
cython("renpy.display.quaternion", libs=[ 'm' ])
|
||||
|
||||
cython("renpy.uguu.gl", libs=sdl)
|
||||
cython("renpy.uguu.uguu", libs=sdl)
|
||||
|
||||
+5
-2
@@ -91,7 +91,7 @@ if PY2:
|
||||
version_tuple = VersionTuple(7, 6, 0, vc_version)
|
||||
|
||||
# The name of this version.
|
||||
version_name = "TBD"
|
||||
version_name = "To Boldly Go"
|
||||
|
||||
else:
|
||||
|
||||
@@ -99,7 +99,7 @@ else:
|
||||
version_tuple = VersionTuple(8, 1, 0, vc_version)
|
||||
|
||||
# The name of this version.
|
||||
version_name = "TBD"
|
||||
version_name = "Where No One Has Gone Before"
|
||||
|
||||
# A string giving the version number only (8.0.1.123), with a suffix if needed.
|
||||
version_only = ".".join(str(i) for i in version_tuple)
|
||||
@@ -263,6 +263,8 @@ name_blacklist = {
|
||||
"renpy.webloader.queue_lock",
|
||||
"renpy.persistent.MP_instances",
|
||||
"renpy.exports.sdl_dll",
|
||||
"renpy.sl2.slast.serial",
|
||||
"renpy.gl2.gl2draw.default_position",
|
||||
}
|
||||
|
||||
class Backup(_object):
|
||||
@@ -514,6 +516,7 @@ def import_all():
|
||||
import renpy.display.tts
|
||||
import renpy.display.gesture
|
||||
import renpy.display.model
|
||||
import renpy.display.quaternion
|
||||
|
||||
import renpy.display.error
|
||||
|
||||
|
||||
+169
-16
@@ -126,7 +126,7 @@ def interpolate(t, a, b, type): # @ReservedAssignment
|
||||
"""
|
||||
|
||||
# Deal with booleans, nones, etc.
|
||||
if b is None or isinstance(b, (bool, basestring, renpy.display.matrix.Matrix)):
|
||||
if b is None or isinstance(b, (bool, basestring, renpy.display.matrix.Matrix, renpy.display.transform.Camera)):
|
||||
if t >= 1.0:
|
||||
return b
|
||||
else:
|
||||
@@ -228,7 +228,7 @@ def get_catmull_rom_value(t, p_1, p0, p1, p2):
|
||||
+(t - 1) * t * t * p2) / 2)
|
||||
|
||||
|
||||
# A list of atl transforms that may need to be compile.
|
||||
# A list of atl transforms that may need to be compiled.
|
||||
compile_queue = [ ]
|
||||
|
||||
|
||||
@@ -241,11 +241,29 @@ def compile_all():
|
||||
global compile_queue
|
||||
|
||||
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")
|
||||
|
||||
|
||||
# This is the context used when compiling an ATL statement. It stores the
|
||||
# scopes that are used to evaluate the various expressions in the statement,
|
||||
@@ -267,6 +285,37 @@ 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):
|
||||
|
||||
# Ignore the arguments given to ATL Transitions, which
|
||||
# will change each time an interaction restarts. (See #4167.)
|
||||
if i in ("new_widget", "old_widget"):
|
||||
continue
|
||||
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
except Exception:
|
||||
return True
|
||||
|
||||
|
||||
# This is intended to be subclassed by ATLTransform. It takes care of
|
||||
# managing ATL execution, which allows ATLTransform itself to not care
|
||||
# much about the contents of this file.
|
||||
@@ -386,13 +435,12 @@ class ATLTransformBase(renpy.object.Object):
|
||||
elif t.atl is not self.atl:
|
||||
return
|
||||
|
||||
# Important to do it this way, so we use __eq__. The exception handling
|
||||
# optimistically assumes that uncomparable objects are the same.
|
||||
try:
|
||||
if not (t.context == self.context):
|
||||
# Only take the execution state if the contexts haven't changed in
|
||||
# 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()):
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.done = t.done
|
||||
self.block = t.block
|
||||
@@ -649,6 +697,13 @@ 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.
|
||||
|
||||
|
||||
@@ -706,6 +761,11 @@ 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):
|
||||
|
||||
super(RawBlock, self).__init__(loc)
|
||||
@@ -767,6 +827,20 @@ class RawBlock(RawStatement):
|
||||
|
||||
self.constant = constant
|
||||
|
||||
def find_loaded_variables(self):
|
||||
|
||||
if self.loaded_variable_cache is not None:
|
||||
return self.loaded_variable_cache
|
||||
|
||||
variables = set()
|
||||
|
||||
for i in self.statements:
|
||||
variables.update(i.find_loaded_variables())
|
||||
|
||||
self.loaded_variable_cache = variables
|
||||
|
||||
return variables
|
||||
|
||||
|
||||
# A compiled ATL block.
|
||||
class Block(Statement):
|
||||
@@ -1087,6 +1161,26 @@ class RawMultipurpose(RawStatement):
|
||||
|
||||
self.constant = constant
|
||||
|
||||
def find_loaded_variables(self):
|
||||
rv = set()
|
||||
|
||||
rv.update(find_loaded_variables(self.warp_function))
|
||||
rv.update(find_loaded_variables(self.duration))
|
||||
rv.update(find_loaded_variables(self.circles))
|
||||
|
||||
for _name, expr in self.properties:
|
||||
rv.update(find_loaded_variables(expr))
|
||||
|
||||
for _name, exprs in self.splines:
|
||||
for expr in exprs:
|
||||
rv.update(find_loaded_variables(expr))
|
||||
|
||||
for expr, withexpr in self.expressions:
|
||||
rv.update(find_loaded_variables(expr))
|
||||
rv.update(find_loaded_variables(withexpr))
|
||||
|
||||
return rv
|
||||
|
||||
def predict(self, ctx):
|
||||
|
||||
for i, _j in self.expressions:
|
||||
@@ -1122,6 +1216,9 @@ class RawContainsExpr(RawStatement):
|
||||
def mark_constant(self, analysis):
|
||||
self.constant = analysis.is_constant_expr(self.expression)
|
||||
|
||||
def find_loaded_variables(self):
|
||||
return find_loaded_variables(self.expression)
|
||||
|
||||
|
||||
# This allows us to have multiple ATL transforms as children.
|
||||
class RawChild(RawStatement):
|
||||
@@ -1156,6 +1253,14 @@ class RawChild(RawStatement):
|
||||
|
||||
self.constant = constant
|
||||
|
||||
def find_loaded_variables(self):
|
||||
rv = set()
|
||||
|
||||
for i in self.children:
|
||||
rv.update(i.find_loaded_variables())
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
# This changes the child of this statement, optionally with a transition.
|
||||
class Child(Statement):
|
||||
@@ -1244,6 +1349,7 @@ class Interpolation(Statement):
|
||||
newts.take_state(trans.state)
|
||||
|
||||
has_angle = False
|
||||
has_radius = False
|
||||
|
||||
for k, v in self.properties:
|
||||
setattr(newts, k, v)
|
||||
@@ -1252,6 +1358,9 @@ class Interpolation(Statement):
|
||||
newts.last_angle = v
|
||||
has_angle = True
|
||||
|
||||
elif k == "radius":
|
||||
has_radius = True
|
||||
|
||||
# Now, the things we change linearly are in the difference
|
||||
# between the new and old states.
|
||||
linear = trans.state.diff(newts)
|
||||
@@ -1262,7 +1371,7 @@ class Interpolation(Statement):
|
||||
revdir = self.revolution
|
||||
circles = self.circles
|
||||
|
||||
if (revdir or (has_angle and renpy.config.automatic_polar_motion)) and (newts.xaround is not None):
|
||||
if (revdir or ((has_angle or has_radius) and renpy.config.automatic_polar_motion)) and (newts.xaround is not None):
|
||||
|
||||
# Remove various irrelevant motions.
|
||||
for i in [ 'xpos', 'ypos',
|
||||
@@ -1302,13 +1411,22 @@ class Interpolation(Statement):
|
||||
|
||||
startangle += circles * 360
|
||||
|
||||
if has_radius:
|
||||
linear["radius"] = (startradius, endradius)
|
||||
|
||||
# Store the revolution.
|
||||
revolution = (startangle, endangle, startradius, endradius)
|
||||
if has_angle:
|
||||
revolution = (startangle, endangle)
|
||||
|
||||
else:
|
||||
|
||||
last_angle = trans.state.last_angle or trans.state.angle
|
||||
revolution = (last_angle, newts.last_angle, trans.state.radius, newts.radius)
|
||||
if has_angle:
|
||||
last_angle = trans.state.last_angle or trans.state.angle
|
||||
revolution = (last_angle, newts.last_angle)
|
||||
|
||||
if has_radius:
|
||||
linear["radius"] = (trans.state.radius, newts.radius)
|
||||
|
||||
|
||||
# Figure out the splines.
|
||||
for name, values in self.splines:
|
||||
@@ -1331,11 +1449,12 @@ class Interpolation(Statement):
|
||||
if old is None:
|
||||
old = (0.0, 0.0, 0.0)
|
||||
if new is not None:
|
||||
value = renpy.display.accelerator.quaternion_slerp(complete, old, new)
|
||||
value = renpy.display.quaternion.euler_slerp(complete, old, new)
|
||||
elif complete >= 1:
|
||||
value = None
|
||||
else:
|
||||
value = old
|
||||
|
||||
else:
|
||||
value = interpolate(complete, old, new, PROPERTIES[k])
|
||||
|
||||
@@ -1343,14 +1462,12 @@ class Interpolation(Statement):
|
||||
|
||||
# Handle the revolution.
|
||||
if revolution is not None:
|
||||
startangle, endangle, startradius, endradius = revolution
|
||||
startangle, endangle = revolution[:2]
|
||||
|
||||
angle = interpolate(complete, startangle, endangle, float)
|
||||
trans.state.last_angle = angle
|
||||
trans.state.angle = angle
|
||||
|
||||
trans.state.radius = interpolate(complete, startradius, endradius, float)
|
||||
|
||||
# Handle any splines we might have.
|
||||
for name, values in splines:
|
||||
value = interpolate_spline(complete, values)
|
||||
@@ -1388,6 +1505,8 @@ 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):
|
||||
|
||||
@@ -1426,6 +1545,14 @@ 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):
|
||||
|
||||
@@ -1506,6 +1633,15 @@ class RawChoice(RawStatement):
|
||||
|
||||
self.constant = constant
|
||||
|
||||
def find_loaded_variables(self):
|
||||
rv = set()
|
||||
|
||||
for chance, block in self.choices:
|
||||
rv.update(find_loaded_variables(chance))
|
||||
rv.update(block.find_loaded_variables())
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
class Choice(Statement):
|
||||
|
||||
@@ -1575,6 +1711,9 @@ class RawTime(RawStatement):
|
||||
def mark_constant(self, analysis):
|
||||
self.constant = analysis.is_constant_expr(self.time)
|
||||
|
||||
def find_loaded_variables(self):
|
||||
return find_loaded_variables(self.time)
|
||||
|
||||
|
||||
class Time(Statement):
|
||||
|
||||
@@ -1622,6 +1761,14 @@ 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):
|
||||
|
||||
@@ -1731,6 +1878,9 @@ class RawEvent(RawStatement):
|
||||
def mark_constant(self, analysis):
|
||||
self.constant = GLOBAL_CONST
|
||||
|
||||
def find_loaded_variables(self):
|
||||
return set()
|
||||
|
||||
|
||||
class Event(Statement):
|
||||
|
||||
@@ -1757,6 +1907,9 @@ class RawFunction(RawStatement):
|
||||
def mark_constant(self, analysis):
|
||||
self.constant = analysis.is_constant_expr(self.expr)
|
||||
|
||||
def find_loaded_variables(self):
|
||||
return find_loaded_variables(self.expr)
|
||||
|
||||
|
||||
class Function(Statement):
|
||||
|
||||
|
||||
@@ -785,6 +785,7 @@ class Channel(object):
|
||||
|
||||
return renpysound.video_ready(self.number)
|
||||
|
||||
|
||||
# Use unconditional imports so these files get compiled during the build
|
||||
# process.
|
||||
|
||||
@@ -988,6 +989,9 @@ def init():
|
||||
bufsize = 8192 # works for me
|
||||
# bufsize = 16384 # jitter/silence right after starting a sound
|
||||
|
||||
if renpy.config.sound_buffer_size is not None:
|
||||
bufsize = renpy.config.sound_buffer_size
|
||||
|
||||
if 'RENPY_SOUND_BUFSIZE' in os.environ:
|
||||
bufsize = int(os.environ['RENPY_SOUND_BUFSIZE'])
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@ or is stopped.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from libc.stdint cimport uintptr_t
|
||||
|
||||
from pygame_sdl2 cimport *
|
||||
import_pygame_sdl2()
|
||||
|
||||
@@ -84,6 +86,7 @@ cdef extern from "renpysound_core.h":
|
||||
void RPS_periodic()
|
||||
char *RPS_get_error()
|
||||
|
||||
void (*RPS_generate_audio_c_function)(float *stream, int length)
|
||||
|
||||
def check_error():
|
||||
"""
|
||||
@@ -419,6 +422,24 @@ def advance_time():
|
||||
|
||||
RPS_advance_time()
|
||||
|
||||
def set_generate_audio_c_function(fn):
|
||||
"""
|
||||
This can be use to set a C function that totally replaces the Ren'Py
|
||||
audio system.
|
||||
|
||||
The function is expected to have the signature void (*)(short *buf, int samples,
|
||||
and fill buf with samples samples of audio, where each sample consists of two
|
||||
shorts.
|
||||
"""
|
||||
|
||||
global RPS_generate_audio_c_function
|
||||
|
||||
if not isinstance(fn, int):
|
||||
import ctypes
|
||||
fn = ctypes.cast(fn, ctypes.c_void_p).value
|
||||
|
||||
RPS_generate_audio_c_function = <void (*)(float *, int)> <uintptr_t> fn
|
||||
|
||||
# Store the sample surfaces so they stay alive.
|
||||
rgb_surface = None
|
||||
rgba_surface = None
|
||||
|
||||
@@ -1258,6 +1258,11 @@ class ADVCharacter(object):
|
||||
if not isinstance(what, basestring):
|
||||
raise Exception("Character expects its what argument to be a string, got %r." % (what,))
|
||||
|
||||
if renpy.store._side_image_attributes_reset:
|
||||
renpy.store._side_image_attributes = None
|
||||
renpy.store._side_image_attributes_reset = False
|
||||
|
||||
|
||||
# Figure out multiple and final. Multiple is None if this is not a multiple
|
||||
# dialogue, or a step and the total number of steps in a multiple interaction.
|
||||
|
||||
@@ -1500,6 +1505,9 @@ def Character(name=NotSet, kind=None, **properties):
|
||||
be used to define a template character, and then copy that
|
||||
character with changes.
|
||||
|
||||
This can also be a namespace, in which case the 'character'
|
||||
variable in the namespace is used as the kind.
|
||||
|
||||
**Linked Image.**
|
||||
An image tag may be associated with a Character. This allows a
|
||||
say statement involving this character to display an image with
|
||||
@@ -1653,6 +1661,8 @@ def Character(name=NotSet, kind=None, **properties):
|
||||
if kind is None:
|
||||
kind = renpy.store.adv
|
||||
|
||||
kind = getattr(kind, "character", kind)
|
||||
|
||||
return type(kind)(name, kind=kind, **properties)
|
||||
|
||||
|
||||
|
||||
@@ -212,6 +212,14 @@ screen _accessibility():
|
||||
|
||||
null height 10
|
||||
|
||||
label _("Voice Volume")
|
||||
|
||||
null height 10
|
||||
|
||||
bar value Preference("voice volume")
|
||||
|
||||
null height 10
|
||||
|
||||
label _("Self-Voicing Volume Drop")
|
||||
|
||||
null height 10
|
||||
@@ -219,6 +227,8 @@ screen _accessibility():
|
||||
bar value Preference("self voicing volume drop")
|
||||
|
||||
|
||||
|
||||
|
||||
vbox:
|
||||
|
||||
text _("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.")
|
||||
|
||||
@@ -302,11 +302,11 @@ init -1600 python:
|
||||
renpy.restart_interaction()
|
||||
|
||||
def get_selected(self):
|
||||
if self.key not in self.dict:
|
||||
try:
|
||||
rv = self.dict[self.key]
|
||||
except (KeyError, IndexError):
|
||||
return False
|
||||
|
||||
rv = self.dict[self.key]
|
||||
|
||||
if self.true_value is not None:
|
||||
rv = (rv == self.true_value)
|
||||
|
||||
|
||||
@@ -129,6 +129,8 @@ init -1500 python:
|
||||
|
||||
config.predict_file_pages = True
|
||||
|
||||
config.file_slotname_callback = None
|
||||
|
||||
def __slotname(name, page=None, slot=False):
|
||||
|
||||
if slot:
|
||||
@@ -151,7 +153,13 @@ init -1500 python:
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return str(page) + "-" + str(name)
|
||||
page = str(page)
|
||||
name = str(name)
|
||||
|
||||
if config.file_slotname_callback is not None:
|
||||
return config.file_slotname_callback(page, name)
|
||||
else:
|
||||
return page + "-" + name
|
||||
|
||||
def __newest_slot():
|
||||
"""
|
||||
@@ -566,7 +574,7 @@ init -1500 python:
|
||||
|
||||
page = unicode(page)
|
||||
|
||||
for i in renpy.list_slots(page + "-"):
|
||||
for i in renpy.list_slots(__slotname(page, r'\d+')):
|
||||
renpy.predict(renpy.slot_screenshot(i))
|
||||
|
||||
@renpy.pure
|
||||
|
||||
@@ -414,8 +414,9 @@ init -1500 python:
|
||||
when entering the replay.
|
||||
|
||||
`locked`
|
||||
If true, this replay is locked. If false, it is unlocked. If None, the
|
||||
replay is locked if the label has not been seen in any playthrough.
|
||||
If true, this action is insensitive and will not do anything when triggered.
|
||||
If false, it will behave normally. If None, it will be locked if the label
|
||||
has not been seen in any playthrough.
|
||||
"""
|
||||
|
||||
def __init__(self, label, scope={}, locked=None):
|
||||
@@ -425,7 +426,7 @@ init -1500 python:
|
||||
|
||||
def __call__(self):
|
||||
|
||||
if self.locked:
|
||||
if not self.get_sensitive():
|
||||
return
|
||||
|
||||
if config.enter_replay_transition:
|
||||
@@ -694,7 +695,7 @@ init -1500 python:
|
||||
amount = delta * adjustment.step
|
||||
elif self.amount == "page":
|
||||
amount = delta * adjustment.page
|
||||
elif isinstance(self.amount) and not isinstance(self.amount, absolute):
|
||||
elif isinstance(self.amount, float) and not isinstance(self.amount, absolute):
|
||||
amount = delta * self.amount * adjustment.range
|
||||
else:
|
||||
amount = delta * self.amount
|
||||
|
||||
@@ -456,6 +456,17 @@ init -1500 python in build:
|
||||
# The itch.io project name.
|
||||
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" ),
|
||||
]
|
||||
|
||||
# Should we include the old Ren'Py themes?
|
||||
include_old_themes = True
|
||||
|
||||
@@ -552,6 +563,8 @@ init -1500 python in build:
|
||||
if itch_project:
|
||||
rv["itch_project"] = itch_project
|
||||
|
||||
rv["itch_channels"] = itch_channels
|
||||
|
||||
if mac_identity:
|
||||
rv["mac_identity"] = mac_identity
|
||||
rv["mac_codesign_command"] = mac_codesign_command
|
||||
|
||||
@@ -250,14 +250,14 @@ init -1100 python:
|
||||
config.atl_function_always_blocks = True
|
||||
|
||||
if version <= (7, 4, 11):
|
||||
config.timer_blocks_pause = False
|
||||
config.modal_blocks_timer = False
|
||||
config.modal_blocks_pause = False
|
||||
elif _compat_versions(version, (7, 5, 1), (8, 0, 1)):
|
||||
config.timer_blocks_pause = True
|
||||
config.modal_blocks_timer = True
|
||||
config.modal_blocks_pause = False
|
||||
elif _compat_versions(version, (7, 5, 2), (8, 0, 2)):
|
||||
config.modal_blocks_pause = True
|
||||
config.timer_blocks_pause = True
|
||||
config.modal_blocks_timer = True
|
||||
|
||||
if _compat_versions(version, (7, 5, 3), (8, 0, 3)):
|
||||
config.quadratic_volumes = True
|
||||
@@ -268,6 +268,7 @@ init -1100 python:
|
||||
config.history_current_dialogue = False
|
||||
config.scry_extend = False
|
||||
config.fadeout_audio = 0.0
|
||||
config.at_transform_compare_full_context = True
|
||||
|
||||
if version > (6, 99, 5):
|
||||
config.search_prefixes.append("images/")
|
||||
|
||||
@@ -24,7 +24,7 @@ import renpy.exports as renpy
|
||||
from renpy.minstore import _dict, _object
|
||||
|
||||
"""renpy
|
||||
init -1100 python:
|
||||
init -1200 python:
|
||||
"""
|
||||
|
||||
class _JSONDBDict(_dict):
|
||||
|
||||
@@ -54,6 +54,7 @@ init -1600 python:
|
||||
choose_renderer = ['alt_K_g', 'shift_K_g' ],
|
||||
progress_screen = [ 'alt_shift_K_p', 'meta_shift_K_p', 'K_F2' ],
|
||||
accessibility = [ 'K_a' ],
|
||||
bubble_editor = [ 'alt_K_b', 'shift_K_b' ],
|
||||
|
||||
# Accessibility.
|
||||
self_voicing = [ 'alt_K_v', 'K_v' ],
|
||||
@@ -83,6 +84,7 @@ init -1600 python:
|
||||
# Input.
|
||||
input_backspace = [ 'any_K_BACKSPACE' ],
|
||||
input_enter = [ 'K_RETURN', 'K_KP_ENTER' ],
|
||||
input_next_line = [ 'shift_K_RETURN', 'shift_K_KP_ENTER' ],
|
||||
input_left = [ 'any_K_LEFT', 'any_KP_LEFT' ],
|
||||
input_right = [ 'any_K_RIGHT', 'any_KP_RIGHT' ],
|
||||
input_up = [ 'any_K_UP', 'any_KP_UP' ],
|
||||
@@ -296,7 +298,7 @@ init -1600 python:
|
||||
dest = config.renpy_base
|
||||
|
||||
if renpy.macapp:
|
||||
dest = os.path.expanduser(b"~/Desktop")
|
||||
dest = os.path.expanduser("~/Desktop")
|
||||
|
||||
pattern = renpy.store._screenshot_pattern or config.screenshot_pattern
|
||||
|
||||
@@ -415,8 +417,7 @@ screen _progress:
|
||||
|
||||
init -1100 python:
|
||||
|
||||
# The default keymap. We might also want to put some of this into
|
||||
# the launcher.
|
||||
# The default keymap.
|
||||
_default_keymap = renpy.Keymap(
|
||||
rollback = renpy.rollback,
|
||||
screenshot = _screenshot,
|
||||
@@ -443,6 +444,7 @@ init -1100 python:
|
||||
director = director.Start(),
|
||||
performance = ToggleScreen("_performance"),
|
||||
accessibility = ToggleScreen("_accessibility"),
|
||||
bubble_editor = bubble.ToggleShown(),
|
||||
)
|
||||
|
||||
config.underlay = [ _default_keymap ]
|
||||
|
||||
@@ -438,7 +438,7 @@ python early in layeredimage:
|
||||
|
||||
class Always(Layer):
|
||||
"""
|
||||
:doc: li
|
||||
:undocumented:
|
||||
:name: Always
|
||||
|
||||
This is used for a displayable that is always shown.
|
||||
@@ -515,11 +515,14 @@ python early in layeredimage:
|
||||
displayables associated with those attribute.
|
||||
|
||||
`attributes`
|
||||
This must be a list of Attribute objects. Each Attribute object
|
||||
This must be a list of Attribute, Condition, ConditionGroup or
|
||||
:func:`renpy.Displayable` objects. Each one
|
||||
reflects a displayable that may or may not be displayed as part
|
||||
of the image. The items in this list are in back-to-front order,
|
||||
with the first item further from the viewer and the last
|
||||
closest.
|
||||
Passing a displayable directly is the equivalent of the `always`
|
||||
layeredimage statement.
|
||||
|
||||
`at`
|
||||
A transform or list of transforms that are applied to the displayable
|
||||
|
||||
@@ -167,17 +167,16 @@ init -1500 python:
|
||||
b = oldb + (b - oldb) * done
|
||||
a = olda + (a - olda) * done
|
||||
|
||||
# To properly handle premultiplied alpha, the color channels
|
||||
# have to be multiplied by the alpha channel.
|
||||
r *= a
|
||||
g *= a
|
||||
b *= a
|
||||
# Update the tint with opacity from the alpha channel.
|
||||
r = 1 - (1 - r) * a
|
||||
g = 1 - (1 - g) * a
|
||||
b = 1 - (1 - b) * a
|
||||
|
||||
# Return a Matrix.
|
||||
return Matrix([ r, 0, 0, 0,
|
||||
0, g, 0, 0,
|
||||
0, 0, b, 0,
|
||||
0, 0, 0, a ])
|
||||
0, 0, 0, 1 ])
|
||||
|
||||
class BrightnessMatrix(ColorMatrix, DictEquality):
|
||||
"""
|
||||
|
||||
@@ -27,7 +27,7 @@ default bubble.tag_properties = { }
|
||||
# on the screen now.
|
||||
default bubble.current_dialogue = [ ]
|
||||
|
||||
init -1050 python in bubble:
|
||||
init -1150 python in bubble:
|
||||
|
||||
from store import config, ADVCharacter, Character, JSONDB, Action, Frame, NoRollback
|
||||
|
||||
@@ -41,13 +41,14 @@ init -1050 python in bubble:
|
||||
# The path to the json file the bubble database is stored in.
|
||||
db_filename = "bubble.json"
|
||||
|
||||
# The number of rows and columns in the bubble database.
|
||||
# The number of rows and columns in the grid that is used to position
|
||||
# speech bubbles.
|
||||
cols = 24
|
||||
rows = 24
|
||||
|
||||
# The default window area rectangle. This is expressed as squares, where units
|
||||
# are defined by the rows and columns.
|
||||
default_area = (0, 18, 24, 6)
|
||||
default_area = (15, 1, 8, 5)
|
||||
|
||||
# The property that the area is supplied as.
|
||||
area_property = "window_area"
|
||||
@@ -55,17 +56,34 @@ init -1050 python in bubble:
|
||||
# Additional properties that the player can use to customize the bubble.
|
||||
# This is a map from a property name to a list of choices that are cycled
|
||||
# through.
|
||||
properties = {
|
||||
"default" : { }
|
||||
}
|
||||
properties = { }
|
||||
|
||||
# The property group names, in order.
|
||||
properties_order = [ "default" ]
|
||||
properties_order = [ ]
|
||||
|
||||
# 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.
|
||||
expand_area = { }
|
||||
|
||||
# This is set to the JSONDB object that stores the bubble database,
|
||||
# or None if the databse doesn't exist yet.
|
||||
# or None if the database doesn't exist yet.
|
||||
db = None
|
||||
|
||||
# This is not used directly, but is used by the default screens.rpy, and
|
||||
# so should not be set.
|
||||
frame = None
|
||||
|
||||
class ToggleShown(Action):
|
||||
def __call__(self):
|
||||
if not active and not shown.value:
|
||||
return
|
||||
|
||||
shown.value = not shown.value
|
||||
renpy.restart_interaction()
|
||||
|
||||
def get_selected(self):
|
||||
return shown.value
|
||||
|
||||
def scene_callback(layer):
|
||||
global tag_properties
|
||||
@@ -117,6 +135,9 @@ init -1050 python in bubble:
|
||||
for the given image tag.
|
||||
"""
|
||||
|
||||
if not properties_order:
|
||||
raise Exception("A speech bubble is being used, but bubble.properties has not been set.")
|
||||
|
||||
rv = { }
|
||||
|
||||
xgrid = config.screen_width / cols
|
||||
@@ -134,6 +155,29 @@ init -1050 python in bubble:
|
||||
"properties" : properties_order[0]
|
||||
}
|
||||
|
||||
def expand_area(self, area, properties_key):
|
||||
"""
|
||||
This is called to expand the area of a bubble. It is given the
|
||||
area, and the properties key, and returns a new area.
|
||||
"""
|
||||
|
||||
x, y, w, h = area
|
||||
|
||||
expand = expand_area.get(properties_key, None) or expand_area.get(None, None)
|
||||
|
||||
if expand is None:
|
||||
return area
|
||||
|
||||
left, top, right, bottom = expand
|
||||
|
||||
x = x - left
|
||||
y = y - top
|
||||
w = w + left + right
|
||||
h = h + top + bottom
|
||||
|
||||
return (x, y, w, h)
|
||||
|
||||
|
||||
def do_show(self, who, what, multiple=None, extra_properties=None):
|
||||
|
||||
if extra_properties is None:
|
||||
@@ -157,7 +201,7 @@ init -1050 python in bubble:
|
||||
properties_key = tag_properties[image_tag]["properties"]
|
||||
|
||||
extra_properties.update(properties.get(properties_key, { }))
|
||||
extra_properties[area_property] = tag_properties[image_tag]["area"]
|
||||
extra_properties[area_property] = self.expand_area(tag_properties[image_tag]["area"], properties_key)
|
||||
|
||||
return super(BubbleCharacter, self).do_show(who, what, multiple=multiple, extra_properties=extra_properties)
|
||||
|
||||
|
||||
@@ -362,7 +362,7 @@ init -1499 python in _renpysteam:
|
||||
Activates the Steam overlay, and opens the web page at `url`.
|
||||
"""
|
||||
|
||||
steamapi.SteamFriends().ActivateGameOverlayToWebPage(url.encode("utf-8"))
|
||||
steamapi.SteamFriends().ActivateGameOverlayToWebPage(url.encode("utf-8"), steamapi.k_EActivateGameOverlayToWebPageMode_Default)
|
||||
|
||||
def activate_overlay_to_store(appid, flag=None):
|
||||
"""
|
||||
|
||||
@@ -287,7 +287,7 @@ let video_start = (c) => {
|
||||
//TODO? c.playing.source.stop(context.currentTime + p.fadeout);
|
||||
//TODO? } catch (e) {
|
||||
//TODO? }
|
||||
//TODO?
|
||||
//TODO?
|
||||
//TODO? }
|
||||
|
||||
setValue(c.relative_volume.gain, p.relative_volume);
|
||||
@@ -702,10 +702,13 @@ renpyAudio.set_pan = (channel, pan, delay) => {
|
||||
linearRampToValue(control, control.value, pan, delay);
|
||||
};
|
||||
|
||||
renpyAudio.tts = (s) => {
|
||||
console.log("tts: " + s);
|
||||
renpyAudio.tts = (s, v) => {
|
||||
console.log("tts:", s, "volume:", v);
|
||||
|
||||
v = v || 1.0;
|
||||
|
||||
let u = new SpeechSynthesisUtterance(s);
|
||||
u.volume = v;
|
||||
speechSynthesis.cancel();
|
||||
speechSynthesis.speak(u);
|
||||
};
|
||||
|
||||
@@ -67,7 +67,7 @@ screen _developer:
|
||||
|
||||
if bubble.active:
|
||||
|
||||
textbutton _("Speech Bubble Editor"):
|
||||
textbutton _("Speech Bubble Editor (Shift+B)"):
|
||||
action [ SetField(bubble.shown, "value", True), Hide("_developer") ]
|
||||
|
||||
hbox:
|
||||
|
||||
@@ -179,6 +179,19 @@ if PY2:
|
||||
add_attribute(io.TextIOWrapper, "_write", io.TextIOWrapper.write)
|
||||
add_attribute(io.TextIOWrapper, "write", types.MethodType(text_write, None, io.TextIOWrapper)) # type: ignore
|
||||
|
||||
################################################################################
|
||||
# Chance the default for subprocess.Popen.
|
||||
if PY2:
|
||||
import subprocess
|
||||
class Popen(subprocess.Popen):
|
||||
def __init__(self, *args, **kwargs):
|
||||
if ("stdout" not in kwargs) and ("stderr" not in kwargs) and ("stdin" not in kwargs):
|
||||
kwargs.setdefault("close_fds", True)
|
||||
super(Popen, self).__init__(*args, **kwargs)
|
||||
|
||||
subprocess.Popen = Popen
|
||||
|
||||
|
||||
################################################################################
|
||||
# Export functions.
|
||||
|
||||
|
||||
+13
-1
@@ -97,7 +97,7 @@ savedir = None
|
||||
image_cache_size = None
|
||||
|
||||
# The size of the image cache, in megabytes.
|
||||
image_cache_size_mb = 300
|
||||
image_cache_size_mb = 400
|
||||
|
||||
# The number of statements we will analyze when doing predictive
|
||||
# loading. Please note that this is a total number of statements in a
|
||||
@@ -1373,6 +1373,18 @@ check_translate_none = True
|
||||
# Like developer, but available at the end of python early blocks.
|
||||
early_developer = False
|
||||
|
||||
# A function that returns the autosave prefix.
|
||||
autosave_prefix_callback = None
|
||||
|
||||
# Compatibility for c492f2f8e6de57b2740fa5d00566f445124caaba.
|
||||
at_transform_compare_full_context = False
|
||||
|
||||
# A list of callbacks when the display starts.
|
||||
display_start_callbacks = [ ]
|
||||
|
||||
# If not None, the size of the sound buffer, in bytes.
|
||||
sound_buffer_size = None
|
||||
|
||||
|
||||
del os
|
||||
del collections
|
||||
|
||||
@@ -163,7 +163,8 @@ ShowingSwitch = renpy.display.layout.ShowingSwitch
|
||||
AlphaMask = renpy.display.layout.AlphaMask
|
||||
Layer = renpy.display.layout.Layer
|
||||
|
||||
Transform = renpy.display.motion.Transform
|
||||
Transform = renpy.display.transform.Transform
|
||||
Camera = renpy.display.transform.Camera
|
||||
|
||||
Animation = anim.Animation
|
||||
Movie = renpy.display.video.Movie
|
||||
|
||||
+754
-657
File diff suppressed because it is too large
Load Diff
@@ -1116,7 +1116,9 @@ class Button(renpy.display.layout.Window):
|
||||
renpy.game.interface.timeout(renpy.config.longpress_duration)
|
||||
|
||||
if self.longpress_start is not None:
|
||||
if math.hypot(x - self.longpress_x, y - self.longpress_y) > renpy.config.longpress_radius:
|
||||
if ev.type == pygame.MOUSEBUTTONUP and ev.button == 1:
|
||||
self.longpress_start = None
|
||||
elif math.hypot(x - self.longpress_x, y - self.longpress_y) > renpy.config.longpress_radius:
|
||||
self.longpress_start = None
|
||||
elif st >= (self.longpress_start + renpy.config.longpress_duration):
|
||||
renpy.exports.vibrate(renpy.config.longpress_vibrate)
|
||||
@@ -1287,7 +1289,7 @@ def input_post_per_interact():
|
||||
global input_value_active
|
||||
|
||||
for i in input_values:
|
||||
if i is current_input_value:
|
||||
if i == current_input_value:
|
||||
break
|
||||
|
||||
else:
|
||||
@@ -1298,7 +1300,7 @@ def input_post_per_interact():
|
||||
|
||||
for i in inputs:
|
||||
|
||||
editable = (i.value is current_input_value) and input_value_active and i.value.editable
|
||||
editable = (i.value == current_input_value) and input_value_active and i.value.editable
|
||||
|
||||
content = i.value.get_text()
|
||||
|
||||
@@ -1365,6 +1367,7 @@ class Input(renpy.text.text.Text): # @UndefinedVariable
|
||||
edit_text = u""
|
||||
value = None
|
||||
shown = False
|
||||
multiline = False
|
||||
|
||||
st = 0
|
||||
|
||||
@@ -1384,6 +1387,7 @@ class Input(renpy.text.text.Text): # @UndefinedVariable
|
||||
value=None,
|
||||
copypaste=False,
|
||||
caret_blink=None,
|
||||
multiline=False,
|
||||
**properties):
|
||||
|
||||
super(Input, self).__init__("", style=style, replaces=replaces, substitute=False, **properties)
|
||||
@@ -1412,6 +1416,8 @@ class Input(renpy.text.text.Text): # @UndefinedVariable
|
||||
self.editable = editable
|
||||
self.pixel_width = pixel_width
|
||||
|
||||
self.multiline = multiline
|
||||
|
||||
caretprops = { 'color' : None }
|
||||
|
||||
for i, v in properties.items():
|
||||
@@ -1582,6 +1588,14 @@ class Input(renpy.text.text.Text): # @UndefinedVariable
|
||||
renpy.display.render.redraw(self, 0)
|
||||
raise renpy.display.core.IgnoreEvent()
|
||||
|
||||
elif self.multiline and map_event(ev, 'input_next_line'):
|
||||
content = self.content[:self.caret_pos] + '\n' + self.content[self.caret_pos:]
|
||||
self.caret_pos += 1
|
||||
self.update_text(content, self.editable)
|
||||
|
||||
renpy.display.render.redraw(self, 0)
|
||||
raise renpy.display.core.IgnoreEvent()
|
||||
|
||||
elif map_event(ev, "input_enter"):
|
||||
|
||||
content = self.content
|
||||
|
||||
+20
-12
@@ -84,7 +84,8 @@ controllers = { }
|
||||
axis_positions = {}
|
||||
|
||||
# The axis threshold.
|
||||
THRESHOLD = (32768 // 2)
|
||||
THRESHOLD = 8192 + 4096
|
||||
ZERO_THRESHOLD = 8192
|
||||
|
||||
# Should we ignore events?
|
||||
ignore = False
|
||||
@@ -254,21 +255,28 @@ def event(ev):
|
||||
|
||||
elif ev.type == CONTROLLERAXISMOTION:
|
||||
|
||||
if ev.value > THRESHOLD:
|
||||
pos = "pos"
|
||||
elif ev.value < -THRESHOLD:
|
||||
pos = "neg"
|
||||
else:
|
||||
pos = "zero"
|
||||
pygame_sdl2.event.pump()
|
||||
events = [ ev ] + pygame.event.get(CONTROLLERAXISMOTION)
|
||||
|
||||
old_pos = axis_positions.get((ev.which, ev.axis), None)
|
||||
for ev in events:
|
||||
|
||||
if pos == old_pos:
|
||||
return None
|
||||
old_pos = axis_positions.get((ev.which, ev.axis), None)
|
||||
|
||||
axis_positions[(ev.which, ev.axis)] = pos
|
||||
if ev.value > THRESHOLD:
|
||||
pos = "pos"
|
||||
elif ev.value < -THRESHOLD:
|
||||
pos = "neg"
|
||||
elif abs(ev.value) < ZERO_THRESHOLD:
|
||||
pos = "zero"
|
||||
else:
|
||||
pos = old_pos
|
||||
|
||||
controller_event(get_string_for_axis(ev.axis), pos)
|
||||
if pos == old_pos:
|
||||
continue
|
||||
|
||||
axis_positions[(ev.which, ev.axis)] = pos
|
||||
|
||||
controller_event(get_string_for_axis(ev.axis), pos)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
+85
-26
@@ -2017,9 +2017,6 @@ class Interface(object):
|
||||
|
||||
self.old_scene = { }
|
||||
self.transition = { }
|
||||
self.ongoing_transition = { }
|
||||
self.transition_time = { }
|
||||
self.transition_from = { }
|
||||
self.suppress_transition = False
|
||||
self.quick_quit = False
|
||||
self.force_redraw = False
|
||||
@@ -2076,6 +2073,22 @@ class Interface(object):
|
||||
# Is our audio paused?
|
||||
self.audio_paused = False
|
||||
|
||||
# Transition-related:
|
||||
|
||||
# A map from layer (or None for the root) to the uninstantiate
|
||||
# transition object that's on that layer.
|
||||
self.ongoing_transition = { }
|
||||
|
||||
# The same, but for the transition after it's been called with the
|
||||
# old and new displayables.
|
||||
self.instantiated_transition = { }
|
||||
|
||||
# The time an ongoing transition started.
|
||||
self.transition_time = { }
|
||||
|
||||
# The displayable that an ongoing transition is transitioning from.
|
||||
self.transition_from = { }
|
||||
|
||||
# Init layers.
|
||||
init_layers()
|
||||
|
||||
@@ -2360,6 +2373,7 @@ class Interface(object):
|
||||
cursors = { }
|
||||
|
||||
for key, cursor_list in renpy.config.mouse.items():
|
||||
|
||||
l = [ ]
|
||||
|
||||
for i in cursor_list:
|
||||
@@ -2373,11 +2387,18 @@ class Interface(object):
|
||||
|
||||
self.cursor_cache[key] = l
|
||||
|
||||
if ("default" not in self.cursor_cache) and (None in self.cursor_cache):
|
||||
self.cursor_cache["default"] = self.cursor_cache[None]
|
||||
|
||||
s = "Total time until interface ready: {}s.".format(time.time() - import_time)
|
||||
|
||||
if renpy.android and not renpy.config.log_to_stdout:
|
||||
print(s)
|
||||
|
||||
for i in renpy.config.display_start_callbacks:
|
||||
i()
|
||||
|
||||
|
||||
def post_init(self):
|
||||
"""
|
||||
This is called after display init, but before the window is created.
|
||||
@@ -2388,6 +2409,7 @@ class Interface(object):
|
||||
pygame.display.hint("SDL_TOUCH_MOUSE_EVENTS", "1")
|
||||
pygame.display.hint("SDL_MOUSE_TOUCH_EVENTS", "0")
|
||||
pygame.display.hint("SDL_EMSCRIPTEN_ASYNCIFY", "0")
|
||||
pygame.display.hint("SDL_IME_SHOW_UI", "1")
|
||||
|
||||
if renpy.config.mouse_focus_clickthrough:
|
||||
pygame.display.hint("SDL_MOUSE_FOCUS_CLICKTHROUGH", "1")
|
||||
@@ -2451,6 +2473,9 @@ class Interface(object):
|
||||
square_im.blit(im, ((imax - iw) // 2, (imax - ih) // 2))
|
||||
im = square_im
|
||||
|
||||
if im.get_size()[0] > 1024:
|
||||
im = renpy.display.scale.smoothscale(im, (1024, 1024))
|
||||
|
||||
pygame.display.set_icon(im)
|
||||
except renpy.webloader.DownloadNeeded:
|
||||
pass
|
||||
@@ -2919,6 +2944,7 @@ class Interface(object):
|
||||
for l in layers:
|
||||
if l is None:
|
||||
self.ongoing_transition.pop(None, None)
|
||||
self.instantiated_transition.pop(None, None)
|
||||
self.transition_time.pop(None, None)
|
||||
self.transition_from.pop(None, None)
|
||||
continue
|
||||
@@ -2928,6 +2954,7 @@ class Interface(object):
|
||||
|
||||
if (self.frame_time - start) >= delay:
|
||||
self.ongoing_transition.pop(l, None)
|
||||
self.instantiated_transition.pop(l, None)
|
||||
self.transition_time.pop(l, None)
|
||||
self.transition_from.pop(l, None)
|
||||
|
||||
@@ -3138,6 +3165,9 @@ class Interface(object):
|
||||
if interaction and (mouse_kind is None):
|
||||
mouse_kind = self.mouse
|
||||
|
||||
if mouse_kind is None:
|
||||
mouse_kind = "default"
|
||||
|
||||
if pygame.mouse.get_pressed()[0]:
|
||||
mouse_kind = "pressed_" + mouse_kind # type: ignore
|
||||
|
||||
@@ -3328,6 +3358,7 @@ class Interface(object):
|
||||
|
||||
# Stop ongoing transitions.
|
||||
self.ongoing_transition.clear()
|
||||
self.instantiated_transition.clear()
|
||||
self.transition_from.clear()
|
||||
self.transition_time.clear()
|
||||
|
||||
@@ -3613,6 +3644,9 @@ class Interface(object):
|
||||
else:
|
||||
renpy.plog(1, "end idle_frame (inexpensive)")
|
||||
|
||||
# This gets assigned below.
|
||||
take_layer_displayable = None
|
||||
|
||||
def interact_core(self,
|
||||
show_mouse=True,
|
||||
trans_pause=False,
|
||||
@@ -3674,11 +3708,14 @@ class Interface(object):
|
||||
self.suppress_transition = False
|
||||
|
||||
# Figure out transitions.
|
||||
if suppress_transition:
|
||||
if suppress_transition or renpy.game.after_rollback:
|
||||
self.ongoing_transition.clear()
|
||||
self.instantiated_transition.clear()
|
||||
self.transition_from.clear()
|
||||
self.transition_time.clear()
|
||||
else:
|
||||
|
||||
if not suppress_transition:
|
||||
|
||||
for k, t in self.transition.items():
|
||||
if k not in self.old_scene:
|
||||
continue
|
||||
@@ -3791,6 +3828,29 @@ class Interface(object):
|
||||
layers_root = renpy.display.layout.MultiBox(layout='fixed')
|
||||
layers_root.layers = { }
|
||||
|
||||
def instantiate_transition(layer, old_d, new_d):
|
||||
"""
|
||||
Create a transition that will be used to transition `layer` (which
|
||||
can be None) to `d`.
|
||||
"""
|
||||
|
||||
old_trans = self.instantiated_transition.get(layer, None)
|
||||
|
||||
trans = self.ongoing_transition[layer](
|
||||
old_widget=old_d,
|
||||
new_widget=new_d)
|
||||
|
||||
if not isinstance(trans, Displayable):
|
||||
raise Exception("Expected transition to be a displayable, not a %r" % trans)
|
||||
|
||||
if isinstance(trans, renpy.display.transform.Transform) and isinstance(old_trans, renpy.display.transform.Transform):
|
||||
trans.take_state(old_trans)
|
||||
trans.take_execution_state(old_trans)
|
||||
|
||||
self.instantiated_transition[layer] = trans
|
||||
|
||||
return trans
|
||||
|
||||
def add_layer(where, layer):
|
||||
|
||||
scene_layer = scene[layer]
|
||||
@@ -3799,12 +3859,7 @@ class Interface(object):
|
||||
if (self.ongoing_transition.get(layer, None) and
|
||||
not suppress_transition):
|
||||
|
||||
trans = self.ongoing_transition[layer](
|
||||
old_widget=self.transition_from[layer],
|
||||
new_widget=scene_layer)
|
||||
|
||||
if not isinstance(trans, Displayable):
|
||||
raise Exception("Expected transition to be a displayable, not a %r" % trans)
|
||||
trans = instantiate_transition(layer, self.transition_from[layer], scene_layer)
|
||||
|
||||
transition_time = self.transition_time.get(layer, None)
|
||||
|
||||
@@ -3835,9 +3890,7 @@ class Interface(object):
|
||||
old_root.layers[layer] = d
|
||||
old_root.add(d)
|
||||
|
||||
trans = self.ongoing_transition[None](
|
||||
old_widget=old_root,
|
||||
new_widget=layers_root)
|
||||
trans = instantiate_transition(None, old_root, layers_root)
|
||||
|
||||
if not isinstance(trans, Displayable):
|
||||
raise Exception("Expected transition to be a displayable, not a %r" % trans)
|
||||
@@ -3896,23 +3949,29 @@ class Interface(object):
|
||||
# caused by an exception propagating through this function.
|
||||
try:
|
||||
|
||||
# Insert layers into Layer displayables.
|
||||
def per_interact(i):
|
||||
if isinstance(i, renpy.display.layout.Layer):
|
||||
if i.layer in scene:
|
||||
i.layers = layer_transitions
|
||||
add_layer(i, i.layer)
|
||||
del i.layers
|
||||
else:
|
||||
i.add(null)
|
||||
# Call per-interaction code for all displayables.
|
||||
def take_layer_displayable(ld):
|
||||
"""
|
||||
This is called by a layer displayable to add the layer's
|
||||
contents to the layer displayable.
|
||||
"""
|
||||
|
||||
i.per_interact()
|
||||
if ld.layer in scene:
|
||||
ld.layers = layer_transitions
|
||||
add_layer(ld, ld.layer)
|
||||
del ld.layers
|
||||
else:
|
||||
ld.add(null)
|
||||
|
||||
self.take_layer_displayable = take_layer_displayable
|
||||
|
||||
# Call per-interaction code for all widgets.
|
||||
renpy.display.behavior.input_pre_per_interact()
|
||||
root_widget.visit_all(per_interact)
|
||||
root_widget.visit_all(lambda d : d.per_interact())
|
||||
renpy.display.behavior.input_post_per_interact()
|
||||
|
||||
self.take_layer_displayable = None
|
||||
|
||||
|
||||
# Consolidate static and layer transitions for later processing.
|
||||
if layer_transitions:
|
||||
layer_transitions.update(layers_root.layers)
|
||||
|
||||
@@ -471,8 +471,8 @@ class Drag(renpy.display.core.Displayable, renpy.revertable.RevertableObject):
|
||||
Changes the child of this drag to `d`.
|
||||
"""
|
||||
|
||||
d.per_interact()
|
||||
self.child = renpy.easy.displayable(d)
|
||||
self.child.per_interact()
|
||||
renpy.display.render.invalidate(self)
|
||||
|
||||
def top(self):
|
||||
|
||||
+29
-8
@@ -71,7 +71,15 @@ class CacheEntry(object):
|
||||
rv += self.width * self.height
|
||||
|
||||
if self.texture is not None:
|
||||
rv += self.bounds[2] * self.bounds[3]
|
||||
|
||||
has_mipmaps = getattr(self.texture, "has_mipmaps", None)
|
||||
|
||||
if has_mipmaps and has_mipmaps():
|
||||
mipmap_multiplier = 1.34
|
||||
else:
|
||||
mipmap_multiplier = 1.0
|
||||
|
||||
rv += int(self.bounds[2] * self.bounds[3] * mipmap_multiplier)
|
||||
|
||||
return rv
|
||||
|
||||
@@ -376,14 +384,9 @@ class Cache(object):
|
||||
pass
|
||||
|
||||
if not predict:
|
||||
|
||||
if render:
|
||||
return make_render(ce)
|
||||
else:
|
||||
rv = ce.texture
|
||||
rv = ce.texture
|
||||
else:
|
||||
rv = None
|
||||
|
||||
else:
|
||||
rv = ce.surf
|
||||
|
||||
@@ -394,11 +397,14 @@ class Cache(object):
|
||||
|
||||
ce.surf = None
|
||||
|
||||
if texture and render and not predict:
|
||||
return make_render(ce)
|
||||
|
||||
if (ce.surf is None) and (ce.texture is None):
|
||||
with self.lock:
|
||||
self.kill(ce)
|
||||
|
||||
# Done... return the surface.
|
||||
# Done. Return the surface or texture.
|
||||
return rv
|
||||
|
||||
# This kills off a given cache entry.
|
||||
@@ -2063,6 +2069,21 @@ def load_surface(im):
|
||||
|
||||
return cache.get(image(im))
|
||||
|
||||
def load_rgba(data, size):
|
||||
"""
|
||||
:name: renpy.load_rgba
|
||||
:doc: udd_utility
|
||||
|
||||
Loads the image data `bytes` into a texture of size `size`, and return it.
|
||||
|
||||
`data`
|
||||
Should be a bytes object containing the image data in RGBA8888 order.
|
||||
"""
|
||||
|
||||
surf = renpy.display.pgrender.surface(size, True)
|
||||
surf.from_data(data)
|
||||
return renpy.display.draw.load_texture(surf)
|
||||
|
||||
|
||||
def reset_module():
|
||||
print("Resetting cache.")
|
||||
|
||||
+16
-13
@@ -484,7 +484,7 @@ class ImageReference(renpy.display.core.Displayable):
|
||||
|
||||
def _handles_event(self, event):
|
||||
if self.target is None:
|
||||
return False
|
||||
self.find_target()
|
||||
|
||||
return self.target._handles_event(event)
|
||||
|
||||
@@ -745,7 +745,7 @@ class DynamicImage(renpy.display.core.Displayable):
|
||||
|
||||
def _handles_event(self, event):
|
||||
if self.target is None:
|
||||
return False
|
||||
self.find_target()
|
||||
|
||||
return self.target._handles_event(event)
|
||||
|
||||
@@ -1006,7 +1006,8 @@ class ShownImageInfo(renpy.object.Object):
|
||||
|
||||
if ca:
|
||||
ca_required = [ i for i in required if i not in attrs ]
|
||||
ca_optional = [ i for i in optional if i not in attrs ]
|
||||
ca_optional = [ i for i in optional if i not in attrs if i not in required ]
|
||||
|
||||
newattrs = ca(tag, ca_required, ca_optional)
|
||||
|
||||
if newattrs is None:
|
||||
@@ -1014,19 +1015,21 @@ class ShownImageInfo(renpy.object.Object):
|
||||
|
||||
attrs = attrs + newattrs
|
||||
|
||||
num_required = 0
|
||||
else:
|
||||
|
||||
for i in attrs:
|
||||
if i in required:
|
||||
num_required += 1
|
||||
num_required = 0
|
||||
|
||||
for i in attrs:
|
||||
if i in required:
|
||||
num_required += 1
|
||||
continue
|
||||
|
||||
# We don't have any not-found attributes. But we might not
|
||||
# have all of the attributes.
|
||||
|
||||
if num_required != len(required):
|
||||
continue
|
||||
|
||||
# We don't have any not-found attributes. But we might not
|
||||
# have all of the attributes.
|
||||
|
||||
if num_required != len(required):
|
||||
continue
|
||||
|
||||
len_attrs = len(set(attrs))
|
||||
|
||||
if len_attrs < max_len:
|
||||
|
||||
@@ -580,8 +580,8 @@ class Frame(renpy.display.core.Displayable):
|
||||
return rv
|
||||
|
||||
def visit(self):
|
||||
rv = [ ]
|
||||
self.style._visit_frame(rv)
|
||||
rv = [ self.image ]
|
||||
self.style._visit_frame(rv.append)
|
||||
return rv
|
||||
|
||||
|
||||
|
||||
@@ -2469,3 +2469,9 @@ class Layer(AdjustTimes):
|
||||
|
||||
seen.add(id_d)
|
||||
d.visit_all(callback, seen)
|
||||
|
||||
def per_interact(self):
|
||||
if renpy.game.interface and renpy.game.interface.take_layer_displayable:
|
||||
renpy.game.interface.take_layer_displayable(self)
|
||||
|
||||
super(Layer, self).per_interact()
|
||||
|
||||
@@ -247,7 +247,7 @@ def bilinear_scale(src, dst, sx=0, sy=0, sw=None, sh=None, dx=0, dy=0, dw=None,
|
||||
_renpy.bilinear(src, dst, sx, sy, sw, sh, dx, dy, dw, dh, precise=precise)
|
||||
|
||||
|
||||
transform = _renpy.transform
|
||||
self = _renpy.transform
|
||||
|
||||
# Note: Blend requires all surfaces to be the same size.
|
||||
blend = _renpy.blend
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
#cython: profile=False
|
||||
# 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 print_function
|
||||
|
||||
from libc.math cimport sin, cos, atan2, asin, acos
|
||||
|
||||
DEF pi = 3.14159265358979323846
|
||||
|
||||
cdef double radians(double degrees):
|
||||
return degrees * pi / 180.0
|
||||
|
||||
|
||||
cdef double degrees(double radians):
|
||||
return radians * 180.0 / pi
|
||||
|
||||
|
||||
def euler_slerp(double complete, old, new):
|
||||
"""
|
||||
Use quaternions to interpolate between two euler angles.
|
||||
"""
|
||||
|
||||
cdef double coeff1
|
||||
cdef double coeff2
|
||||
cdef double cosx_cosp
|
||||
cdef double cosz_cosp1
|
||||
cdef double cosz_cosp2
|
||||
cdef double cx
|
||||
cdef double cy
|
||||
cdef double cz
|
||||
cdef double dot
|
||||
cdef double new_q_w
|
||||
cdef double new_q_x
|
||||
cdef double new_q_y
|
||||
cdef double new_q_z
|
||||
cdef double new_x
|
||||
cdef double new_x_div_2
|
||||
cdef double new_y
|
||||
cdef double new_y_div_2
|
||||
cdef double new_z
|
||||
cdef double new_z_div_2
|
||||
cdef double old_q_mul_new_q
|
||||
cdef double old_q_w
|
||||
cdef double old_q_x
|
||||
cdef double old_q_y
|
||||
cdef double old_q_z
|
||||
cdef double old_x
|
||||
cdef double old_x_div_2
|
||||
cdef double old_y
|
||||
cdef double old_y_div_2
|
||||
cdef double old_z
|
||||
cdef double old_z_div_2
|
||||
cdef double q_w
|
||||
cdef double q_x
|
||||
cdef double q_y
|
||||
cdef double q_z
|
||||
cdef double sinx_cosp
|
||||
cdef double siny
|
||||
cdef double sinz_cosp1
|
||||
cdef double sinz_cosp2
|
||||
cdef double sout
|
||||
cdef double st
|
||||
cdef double sut
|
||||
cdef double sx
|
||||
cdef double sy
|
||||
cdef double sz
|
||||
cdef double theta
|
||||
cdef double x
|
||||
cdef double y
|
||||
cdef double z
|
||||
|
||||
if old == new:
|
||||
return new
|
||||
|
||||
#select the shorten root
|
||||
old_x, old_y, old_z = old
|
||||
old_x = old_x % 360
|
||||
old_y = old_y % 360
|
||||
old_z = old_z % 360
|
||||
new_x, new_y, new_z = new
|
||||
new_x = new_x % 360
|
||||
new_y = new_y % 360
|
||||
new_z = new_z % 360
|
||||
if new_x - old_x > 180:
|
||||
new_x = new_x - 360
|
||||
if new_y - old_y > 180:
|
||||
new_y = new_y - 360
|
||||
if new_z - old_z > 180:
|
||||
new_z = new_z - 360
|
||||
|
||||
|
||||
#z-y-x Euler angles to quaternion conversion
|
||||
#https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
|
||||
old_x_div_2 = radians(old_x) * 0.5
|
||||
old_y_div_2 = radians(old_y) * 0.5
|
||||
old_z_div_2 = radians(old_z) * 0.5
|
||||
cx = cos(old_x_div_2)
|
||||
sx = sin(old_x_div_2)
|
||||
cy = cos(old_y_div_2)
|
||||
sy = sin(old_y_div_2)
|
||||
cz = cos(old_z_div_2)
|
||||
sz = sin(old_z_div_2)
|
||||
|
||||
old_q_x = sx * cy * cz - cx * sy * sz
|
||||
old_q_y = cx * sy * cz + sx * cy * sz
|
||||
old_q_z = cx * cy * sz - sx * sy * cz
|
||||
old_q_w = cx * cy * cz + sx * sy * sz
|
||||
|
||||
new_x_div_2 = radians(new_x) * 0.5
|
||||
new_y_div_2 = radians(new_y) * 0.5
|
||||
new_z_div_2 = radians(new_z) * 0.5
|
||||
cx = cos(new_x_div_2)
|
||||
sx = sin(new_x_div_2)
|
||||
cy = cos(new_y_div_2)
|
||||
sy = sin(new_y_div_2)
|
||||
cz = cos(new_z_div_2)
|
||||
sz = sin(new_z_div_2)
|
||||
|
||||
new_q_x = sx * cy * cz - cx * sy * sz
|
||||
new_q_y = cx * sy * cz + sx * cy * sz
|
||||
new_q_z = cx * cy * sz - sx * sy * cz
|
||||
new_q_w = cx * cy * cz + sx * sy * sz
|
||||
|
||||
|
||||
#calculate new quaternion between old and new.
|
||||
old_q_mul_new_q = (old_q_x * new_q_x + old_q_y * new_q_y + old_q_z * new_q_z + old_q_w * new_q_w)
|
||||
dot = old_q_mul_new_q
|
||||
if dot > 1.0:
|
||||
dot = 1.0
|
||||
elif dot < -1.0:
|
||||
dot = -1.0
|
||||
theta = abs(acos(dot))
|
||||
|
||||
st = sin(theta)
|
||||
|
||||
sut = sin(theta * complete)
|
||||
sout = sin(theta * (1 - complete))
|
||||
|
||||
coeff1 = sout / st
|
||||
coeff2 = sut / st
|
||||
|
||||
q_x = coeff1 * old_q_x + coeff2 * new_q_x
|
||||
q_y = coeff1 * old_q_y + coeff2 * new_q_y
|
||||
q_z = coeff1 * old_q_z + coeff2 * new_q_z
|
||||
q_w = coeff1 * old_q_w + coeff2 * new_q_w
|
||||
|
||||
|
||||
#Quaternion to z-y-x Euler angles conversion
|
||||
#https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
|
||||
sinx_cosp = 2 * (q_w * q_x + q_y * q_z)
|
||||
cosx_cosp = 1 - 2 * (q_x * q_x + q_y * q_y)
|
||||
siny = 2 * (q_w * q_y - q_z * q_x)
|
||||
sinz_cosp1 = 2 * (q_x * q_y - q_w * q_z)
|
||||
cosz_cosp1 = 1 - 2 * (q_x * q_x + q_z * q_z)
|
||||
sinz_cosp2 = 2 * (q_w * q_z + q_x * q_y)
|
||||
cosz_cosp2 = 1 - 2 * (q_y * q_y + q_z * q_z)
|
||||
|
||||
if siny >= 1:
|
||||
x = 0
|
||||
y = pi/2
|
||||
z = atan2(sinz_cosp1, cosz_cosp1)
|
||||
elif siny <= -1:
|
||||
x = 0
|
||||
y = -pi/2
|
||||
z = atan2(sinz_cosp1, cosz_cosp1)
|
||||
else:
|
||||
x = atan2(sinx_cosp, cosx_cosp)
|
||||
if siny > 1.0:
|
||||
siny = 1.0
|
||||
elif siny < -1.0:
|
||||
siny = -1.0
|
||||
y = asin(siny)
|
||||
z = atan2(sinz_cosp2, cosz_cosp2)
|
||||
x = degrees(x) % 360
|
||||
y = degrees(y) % 360
|
||||
z = degrees(z) % 360
|
||||
|
||||
return (x, y, z)
|
||||
@@ -414,10 +414,7 @@ def redraw_time():
|
||||
|
||||
def redraw(d, when):
|
||||
"""
|
||||
:doc: udd_utility
|
||||
|
||||
Causes the displayable `d` to be redrawn after `when` seconds have
|
||||
elapsed.
|
||||
Documented in cdd.rst.
|
||||
"""
|
||||
|
||||
if not renpy.game.interface:
|
||||
|
||||
@@ -1002,6 +1002,9 @@ def prepare_screens():
|
||||
s.ast.unprepare_screen()
|
||||
s.ast.prepare_screen()
|
||||
|
||||
# Compile ATL in screens.
|
||||
renpy.atl.compile_all()
|
||||
|
||||
prepared = True
|
||||
|
||||
finally:
|
||||
|
||||
@@ -558,7 +558,7 @@ def draw_transformed(dest, clip, what, xo, yo, alpha, forward, reverse):
|
||||
|
||||
dest = dest.subsurface((minx, miny, maxx - minx, maxy - miny))
|
||||
|
||||
renpy.display.module.transform(
|
||||
renpy.display.module.self(
|
||||
what, dest,
|
||||
cx, cy,
|
||||
forward.xdx, forward.ydx,
|
||||
|
||||
@@ -31,9 +31,25 @@ import types # @UnresolvedImport
|
||||
|
||||
import renpy
|
||||
from renpy.display.layout import Container
|
||||
from renpy.display.accelerator import transform_render
|
||||
from renpy.display.accelerator import RenderTransform
|
||||
from renpy.atl import position, any_object, bool_or_none, float_or_none, matrix, mesh
|
||||
|
||||
|
||||
class Camera(renpy.object.Object):
|
||||
"""
|
||||
:doc: point_to_camera
|
||||
|
||||
Instances of this class can be used with point_to to point
|
||||
at the location of the camera for a particular layer.
|
||||
|
||||
`layer`
|
||||
The name of the layer.
|
||||
"""
|
||||
|
||||
def __init__(self, layer="master"):
|
||||
self.layer = layer
|
||||
|
||||
|
||||
# The null object that's used if we don't have a defined child.
|
||||
null = None
|
||||
|
||||
@@ -433,14 +449,15 @@ class Transform(Container):
|
||||
focus=None,
|
||||
default=False,
|
||||
_args=None,
|
||||
alt=None,
|
||||
|
||||
**kwargs):
|
||||
|
||||
properties = {k: kwargs.pop(k) for k in style_properties if k in kwargs}
|
||||
|
||||
self.kwargs = kwargs
|
||||
self.style_arg = style
|
||||
|
||||
super(Transform, self).__init__(style=style, focus=focus, default=default, _args=_args, alt=alt)
|
||||
super(Transform, self).__init__(style=style, focus=focus, default=default, _args=_args, **properties)
|
||||
|
||||
self.function = function
|
||||
|
||||
@@ -750,7 +767,21 @@ class Transform(Container):
|
||||
|
||||
# The render method is now defined in accelerator.pyx.
|
||||
def render(self, width, height, st, at):
|
||||
return transform_render(self, width, height, st, at)
|
||||
|
||||
# Prevent time from ticking backwards, as can happen if we replace a
|
||||
# transform but keep its state.
|
||||
if st + self.st_offset <= self.st:
|
||||
self.st_offset = self.st - st
|
||||
if at + self.at_offset <= self.at:
|
||||
self.at_offset = self.at - at
|
||||
|
||||
self.st = st = st + self.st_offset
|
||||
self.at = at = at + self.at_offset
|
||||
|
||||
# Update the state.
|
||||
self.update_state()
|
||||
|
||||
return RenderTransform(self).render(width, height, st, at)
|
||||
|
||||
def event(self, ev, x, y, st):
|
||||
|
||||
@@ -947,6 +978,9 @@ class ATLTransform(renpy.atl.ATLTransformBase, Transform):
|
||||
return repr((self.child, self.atl.loc))
|
||||
|
||||
|
||||
# Names of style properties that should be sent to the parent.
|
||||
style_properties = {'alt'}
|
||||
|
||||
# Names of transform properties, and if the property should be handled with
|
||||
# diff2 or diff4.
|
||||
all_properties = set()
|
||||
@@ -1025,7 +1059,7 @@ add_property("nearest", bool_or_none, None)
|
||||
add_property("perspective", any_object, None)
|
||||
add_property("rotate", float, None)
|
||||
add_property("rotate_pad", bool, True)
|
||||
add_property("poi", (float, float, float), None)
|
||||
add_property("point_to", any_object, None)
|
||||
add_property("orientation", (float, float, float), None)
|
||||
add_property("xrotate", float, None)
|
||||
add_property("yrotate", float, None)
|
||||
|
||||
@@ -639,6 +639,11 @@ class AlphaDissolve(Transition):
|
||||
super(AlphaDissolve, self).__init__(delay, **properties)
|
||||
|
||||
self.control = renpy.display.layout.Fixed()
|
||||
|
||||
control = renpy.easy.displayable(control)
|
||||
if control._duplicatable:
|
||||
control = control._duplicate(self._args)
|
||||
|
||||
self.control.add(control)
|
||||
|
||||
self.old_widget = renpy.easy.displayable(old_widget)
|
||||
|
||||
+18
-6
@@ -110,19 +110,29 @@ def default_tts_function(s):
|
||||
|
||||
fsencode = renpy.exports.fsencode
|
||||
|
||||
amplitude = renpy.game.preferences.get_mixer("voice")
|
||||
amplitude_100 = int(amplitude * 100)
|
||||
|
||||
|
||||
if "RENPY_TTS_COMMAND" in os.environ:
|
||||
|
||||
process = subprocess.Popen([ os.environ["RENPY_TTS_COMMAND"], fsencode(s) ])
|
||||
|
||||
elif renpy.linux:
|
||||
|
||||
if renpy.config.tts_voice is None:
|
||||
process = subprocess.Popen([ "espeak", fsencode(s) ])
|
||||
else:
|
||||
process = subprocess.Popen([ "espeak", "-v", fsencode(renpy.config.tts_voice), fsencode(s) ])
|
||||
cmd = [ "espeak", "-a", fsencode(str(amplitude_100)) ]
|
||||
|
||||
if renpy.config.tts_voice is not None:
|
||||
cmd.extend([ "-v", fsencode(renpy.config.tts_voice) ])
|
||||
|
||||
cmd.append(fsencode(s))
|
||||
|
||||
process = subprocess.Popen(cmd)
|
||||
|
||||
elif renpy.macintosh:
|
||||
|
||||
s = "[[volm {}]]".format(amplitude) + s
|
||||
|
||||
if renpy.config.tts_voice is None:
|
||||
process = subprocess.Popen([ "say", fsencode(s) ])
|
||||
else:
|
||||
@@ -137,13 +147,13 @@ def default_tts_function(s):
|
||||
|
||||
say_vbs = os.path.join(os.path.dirname(sys.executable), "say.vbs")
|
||||
s = s.replace('"', "")
|
||||
process = subprocess.Popen([ "wscript", fsencode(say_vbs), fsencode(s), fsencode(voice) ])
|
||||
process = subprocess.Popen([ "wscript", fsencode(say_vbs), fsencode(s), fsencode(voice), fsencode(str(amplitude_100)) ])
|
||||
|
||||
elif renpy.emscripten and renpy.config.webaudio:
|
||||
|
||||
try:
|
||||
from renpy.audio.webaudio import call
|
||||
call("tts", s)
|
||||
call("tts", s, amplitude)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -254,6 +264,8 @@ def displayable(d):
|
||||
else:
|
||||
prefix = renpy.translation.translate_string("Self-voicing enabled. ")
|
||||
|
||||
last_raw = None
|
||||
|
||||
for i in renpy.config.tts_voice_channels:
|
||||
if not prefix and renpy.audio.music.get_playing(i):
|
||||
return
|
||||
|
||||
+52
-9
@@ -100,6 +100,9 @@ fullscreen = False
|
||||
# place.
|
||||
reset_channels = set()
|
||||
|
||||
# These store the textures for movies in the same group.
|
||||
group_texture = { }
|
||||
|
||||
|
||||
def early_interact():
|
||||
"""
|
||||
@@ -246,9 +249,15 @@ def get_movie_texture_web(channel, mask_channel, side_mask, mipmap):
|
||||
return tex, new
|
||||
|
||||
|
||||
def render_movie(channel, width, height):
|
||||
def render_movie(channel, width, height, group=None):
|
||||
tex, _new = get_movie_texture(channel)
|
||||
|
||||
if group is not None:
|
||||
if tex is None:
|
||||
tex = group_texture.get(group, None)
|
||||
else:
|
||||
group_texture[group] = tex
|
||||
|
||||
if tex is None:
|
||||
return None
|
||||
|
||||
@@ -370,6 +379,13 @@ class Movie(renpy.display.core.Displayable):
|
||||
If False, the movie will not loop. If `image` is defined, the image
|
||||
will be displayed when the movie ends. Otherwise, the displayable will
|
||||
become transparent.
|
||||
|
||||
`group`
|
||||
If not None, this should be a string. If given, and if the movie has not
|
||||
yet started playing, and another movie in the same group has played in
|
||||
the previous frame, the last frame from that movie will be used for
|
||||
this movie. This can prevent flashes of transparency when switching
|
||||
between two movies.
|
||||
"""
|
||||
|
||||
fullscreen = False
|
||||
@@ -387,6 +403,7 @@ class Movie(renpy.display.core.Displayable):
|
||||
play_callback = None
|
||||
|
||||
loop = True
|
||||
group = None
|
||||
|
||||
|
||||
def any_loadable(self, name):
|
||||
@@ -417,7 +434,6 @@ class Movie(renpy.display.core.Displayable):
|
||||
if self.mask_channel is not None:
|
||||
self.mask_channel = self.channel + "_mask"
|
||||
|
||||
|
||||
def ensure_channel(self, name):
|
||||
|
||||
if name is None:
|
||||
@@ -433,7 +449,7 @@ class Movie(renpy.display.core.Displayable):
|
||||
|
||||
renpy.audio.music.register_channel(name, renpy.config.movie_mixer, loop=True, stop_on_mute=False, movie=True, framedrop=framedrop, force=True)
|
||||
|
||||
def __init__(self, fps=24, size=None, channel="movie", play=None, mask=None, mask_channel=None, image=None, play_callback=None, side_mask=False, loop=True, start_image=None, **properties):
|
||||
def __init__(self, fps=24, size=None, channel="movie", play=None, mask=None, mask_channel=None, image=None, play_callback=None, side_mask=False, loop=True, start_image=None, group=None, **properties):
|
||||
|
||||
global movie_channel_serial
|
||||
|
||||
@@ -475,16 +491,23 @@ class Movie(renpy.display.core.Displayable):
|
||||
|
||||
self.play_callback = play_callback
|
||||
|
||||
self.group = group
|
||||
|
||||
if (self.channel == "movie") and (renpy.config.hw_video) and renpy.mobile:
|
||||
raise Exception("Movie(channel='movie') doesn't work on mobile when config.hw_video is true. (Use a different channel argument.)")
|
||||
|
||||
def _handles_event(self, event):
|
||||
return event == "show"
|
||||
|
||||
def set_transform_event(self, event):
|
||||
if event == "show":
|
||||
reset_channels.add(self.channel)
|
||||
|
||||
def render(self, width, height, st, at):
|
||||
|
||||
if self._play and not (renpy.game.preferences.video_image_fallback is True):
|
||||
channel_movie[self.channel] = self
|
||||
|
||||
if st == 0:
|
||||
reset_channels.add(self.channel)
|
||||
if channel_movie.get(self.channel, None) is not self:
|
||||
channel_movie[self.channel] = self
|
||||
|
||||
playing = renpy.audio.music.get_playing(self.channel)
|
||||
|
||||
@@ -493,6 +516,9 @@ class Movie(renpy.display.core.Displayable):
|
||||
if self.channel in reset_channels:
|
||||
not_playing = False
|
||||
|
||||
if self.group is not None and self.group in group_texture:
|
||||
not_playing = False
|
||||
|
||||
if (self.image is not None) and not_playing:
|
||||
surf = renpy.display.render.render(self.image, width, height, st, at)
|
||||
w, h = surf.get_size()
|
||||
@@ -505,6 +531,12 @@ class Movie(renpy.display.core.Displayable):
|
||||
|
||||
tex, _ = get_movie_texture(self.channel, self.mask_channel, self.side_mask, self.style.mipmap)
|
||||
|
||||
if self.group is not None:
|
||||
if tex is None:
|
||||
tex = group_texture.get(self.group, None)
|
||||
else:
|
||||
group_texture[self.group] = tex
|
||||
|
||||
if (not not_playing) and (tex is not None):
|
||||
width, height = tex.get_size()
|
||||
|
||||
@@ -527,7 +559,7 @@ class Movie(renpy.display.core.Displayable):
|
||||
if not playing:
|
||||
rv = None
|
||||
else:
|
||||
rv = render_movie(self.channel, w, h)
|
||||
rv = render_movie(self.channel, w, h, group=self.group)
|
||||
|
||||
if rv is None:
|
||||
rv = renpy.display.render.Render(w, h)
|
||||
@@ -613,7 +645,6 @@ def update_playing():
|
||||
renpy.game.context().movie = dict(channel_movie)
|
||||
reset_channels.clear()
|
||||
|
||||
|
||||
def frequent():
|
||||
"""
|
||||
Called to update the video playback. Returns true if a video refresh is
|
||||
@@ -624,6 +655,18 @@ def frequent():
|
||||
|
||||
renpy.audio.audio.advance_time()
|
||||
|
||||
# Cycle the group textures.
|
||||
global group_texture
|
||||
|
||||
old_group_texture = group_texture
|
||||
group_texture = { }
|
||||
|
||||
for movies in displayable_channels.values():
|
||||
for m in movies:
|
||||
if m.group is not None:
|
||||
group_texture[m.group] = old_group_texture.get(m.group, None)
|
||||
|
||||
# Determine if we need to redraw.
|
||||
if displayable_channels:
|
||||
|
||||
update = True
|
||||
|
||||
+10
-4
@@ -75,7 +75,7 @@ from renpy.display.image import image_exists, image_exists as has_image, list_im
|
||||
from renpy.display.image import get_available_image_tags, get_available_image_attributes, check_image_attributes, get_ordered_image_attributes
|
||||
from renpy.display.image import get_registered_image
|
||||
|
||||
from renpy.display.im import load_surface, load_image
|
||||
from renpy.display.im import load_surface, load_image, load_rgba
|
||||
|
||||
from renpy.curry import curry, partial
|
||||
from renpy.display.video import movie_start_fullscreen, movie_start_displayable, movie_stop
|
||||
@@ -835,6 +835,9 @@ def web_input(prompt, default='', allow=None, exclude='{}', length=None, mask=Fa
|
||||
|
||||
renpy.exports.mode('input')
|
||||
|
||||
# Take the user out of fullscreen during input.
|
||||
renpy.game.preferences.fullscreen = False
|
||||
|
||||
prompt = renpy.text.extras.filter_text_tags(prompt, allow=set())
|
||||
|
||||
roll_forward = renpy.exports.roll_forward_info()
|
||||
@@ -864,7 +867,7 @@ def web_input(prompt, default='', allow=None, exclude='{}', length=None, mask=Fa
|
||||
return rv
|
||||
|
||||
|
||||
def input(prompt, default='', allow=None, exclude='{}', length=None, with_none=None, pixel_width=None, screen="input", mask=None, copypaste=True, **kwargs): # @ReservedAssignment
|
||||
def input(prompt, default='', allow=None, exclude='{}', length=None, with_none=None, pixel_width=None, screen="input", mask=None, copypaste=True, multiline=False, **kwargs): # @ReservedAssignment
|
||||
"""
|
||||
:doc: input
|
||||
|
||||
@@ -904,6 +907,9 @@ def input(prompt, default='', allow=None, exclude='{}', length=None, with_none=N
|
||||
`copypaste`
|
||||
When true, copying from and pasting to this input is allowed.
|
||||
|
||||
`multiline`
|
||||
When true, move caret to next line is allowed.
|
||||
|
||||
If :var:`config.disable_input` is True, this function only returns
|
||||
`default`.
|
||||
|
||||
@@ -940,7 +946,7 @@ def input(prompt, default='', allow=None, exclude='{}', length=None, with_none=N
|
||||
|
||||
if has_screen(screen):
|
||||
widget_properties = { }
|
||||
widget_properties["input"] = dict(default=default, length=length, allow=allow, exclude=exclude, editable=not fixed, pixel_width=pixel_width, mask=mask, copypaste=copypaste)
|
||||
widget_properties["input"] = dict(default=default, length=length, allow=allow, exclude=exclude, editable=not fixed, pixel_width=pixel_width, mask=mask, copypaste=copypaste, multiline=multiline)
|
||||
|
||||
show_screen(screen, _transient=True, _widget_properties=widget_properties, prompt=prompt, **show_properties)
|
||||
|
||||
@@ -1447,7 +1453,7 @@ def say(who, what, *args, **kwargs):
|
||||
|
||||
e "Hello, world."
|
||||
$ renpy.say(e, "Hello, world.")
|
||||
$ e("Hello, world.")
|
||||
$ e("Hello, world.") # when e is not a string
|
||||
"""
|
||||
|
||||
if renpy.config.old_substitutions:
|
||||
|
||||
@@ -47,6 +47,7 @@ cdef class GL2Draw:
|
||||
cdef tuple default_clip
|
||||
cdef float dpi_scale
|
||||
cdef object shader_cache
|
||||
cdef bint ever_set_position
|
||||
|
||||
cdef public tuple clip_rtt_box
|
||||
|
||||
@@ -73,6 +74,15 @@ cdef class GL2Draw:
|
||||
# The framebuffer object used for offscreen rendering.
|
||||
cdef GLuint fbo
|
||||
|
||||
# The color texture object used for pixel tests (1x1).
|
||||
cdef GLuint color_renderbuffer_1px
|
||||
|
||||
# The depth renderbuffer object used for pixel tests. (1x1)
|
||||
cdef GLuint depth_renderbuffer_1px
|
||||
|
||||
# The framebuffer object used for pixel tests (1x1)
|
||||
cdef GLuint fbo_1px
|
||||
|
||||
# The texture_loader singleton.
|
||||
cdef public TextureLoader texture_loader
|
||||
|
||||
|
||||
+97
-15
@@ -76,6 +76,9 @@ vsync = True
|
||||
# A list of frame end times, used for the same purpose.
|
||||
frame_times = [ ]
|
||||
|
||||
# The default position of the window.
|
||||
default_position = (pygame.WINDOWPOS_CENTERED, pygame.WINDOWPOS_CENTERED)
|
||||
|
||||
cdef class GL2Draw:
|
||||
|
||||
def __init__(self, name):
|
||||
@@ -121,6 +124,9 @@ cdef class GL2Draw:
|
||||
# The shader cache,
|
||||
self.shader_cache = None
|
||||
|
||||
# Has the position of this window ever been set?
|
||||
self.ever_set_position = False
|
||||
|
||||
def get_texture_size(self):
|
||||
"""
|
||||
Returns the amount of memory locked up in textures.
|
||||
@@ -142,7 +148,7 @@ cdef class GL2Draw:
|
||||
if old_surface is not None:
|
||||
maximized = old_surface.get_flags() & pygame.WINDOW_MAXIMIZED
|
||||
else:
|
||||
maximized = False
|
||||
maximized = renpy.game.preferences.maximized
|
||||
|
||||
# Information about the virtual size.
|
||||
vwidth, vheight = self.virtual_size
|
||||
@@ -370,6 +376,10 @@ cdef class GL2Draw:
|
||||
self.window = None
|
||||
|
||||
if self.window is None:
|
||||
|
||||
if renpy.game.preferences.maximized:
|
||||
window_flags |= pygame.WINDOW_MAXIMIZED
|
||||
|
||||
try:
|
||||
renpy.display.log.write("Windowed mode.")
|
||||
self.window = pygame.display.set_mode((pwidth, pheight), window_flags)
|
||||
@@ -440,22 +450,35 @@ cdef class GL2Draw:
|
||||
# Are we in fullscreen mode?
|
||||
fullscreen = bool(pygame.display.get_window().get_window_flags() & (pygame.WINDOW_FULLSCREEN_DESKTOP | pygame.WINDOW_FULLSCREEN))
|
||||
|
||||
# Are we maximized?
|
||||
maximized = bool(pygame.display.get_window().get_window_flags() & pygame.WINDOW_MAXIMIZED)
|
||||
|
||||
# See if we've ever set the screen position, and if not, center the window.
|
||||
if not fullscreen and not maximized:
|
||||
if not self.ever_set_position:
|
||||
self.ever_set_position = True
|
||||
pygame.display.get_window().set_position(default_position)
|
||||
|
||||
# Get the size of the created screen.
|
||||
pwidth, pheight = renpy.display.core.get_size()
|
||||
|
||||
renpy.game.preferences.fullscreen = fullscreen
|
||||
renpy.game.interface.fullscreen = fullscreen
|
||||
|
||||
vwidth, vheight = self.virtual_size
|
||||
|
||||
self.physical_size = (pwidth, pheight)
|
||||
self.drawable_size = pygame.display.get_drawable_size()
|
||||
|
||||
if not fullscreen:
|
||||
renpy.game.preferences.physical_size = self.get_physical_size()
|
||||
|
||||
renpy.display.log.write("Screen sizes: virtual=%r physical=%r drawable=%r" % (self.virtual_size, self.physical_size, self.drawable_size))
|
||||
|
||||
# Update the preferences.
|
||||
renpy.game.preferences.fullscreen = fullscreen
|
||||
renpy.game.interface.fullscreen = fullscreen
|
||||
|
||||
if not fullscreen:
|
||||
renpy.game.preferences.maximized = maximized
|
||||
|
||||
if not fullscreen and not maximized:
|
||||
renpy.game.preferences.physical_size = self.get_physical_size()
|
||||
|
||||
if renpy.config.adjust_view_size is not None:
|
||||
view_width, view_height = renpy.config.adjust_view_size(pwidth, pheight)
|
||||
else:
|
||||
@@ -542,8 +565,12 @@ cdef class GL2Draw:
|
||||
width = max(width, 256)
|
||||
height = max(height, 256)
|
||||
|
||||
pygame.display.get_window().restore()
|
||||
pygame.display.get_window().resize((width, height), opengl=True, fullscreen=fullscreen)
|
||||
if pygame.display.get_window().get_window_flags() & pygame.WINDOW_FULLSCREEN:
|
||||
maximized = renpy.game.preferences.maximized
|
||||
else:
|
||||
maximized = False
|
||||
|
||||
pygame.display.get_window().resize((width, height), opengl=True, fullscreen=fullscreen, maximized=maximized)
|
||||
|
||||
def update(self, force=False):
|
||||
"""
|
||||
@@ -568,6 +595,18 @@ cdef class GL2Draw:
|
||||
Called when terminating the use of the OpenGL context.
|
||||
"""
|
||||
|
||||
global default_position
|
||||
|
||||
# Are we in fullscreen mode?
|
||||
fullscreen = bool(pygame.display.get_window().get_window_flags() & (pygame.WINDOW_FULLSCREEN_DESKTOP | pygame.WINDOW_FULLSCREEN))
|
||||
|
||||
# Are we maximized?
|
||||
maximized = bool(pygame.display.get_window().get_window_flags() & pygame.WINDOW_MAXIMIZED)
|
||||
|
||||
# See if we've ever set the screen position, and if not, center the window.
|
||||
if not fullscreen and not maximized:
|
||||
default_position = pygame.display.get_position()
|
||||
|
||||
self.kill_textures()
|
||||
|
||||
if self.texture_loader is not None:
|
||||
@@ -611,11 +650,14 @@ cdef class GL2Draw:
|
||||
|
||||
# Generate the framebuffer.
|
||||
glGenFramebuffers(1, &self.fbo)
|
||||
glGenFramebuffers(1, &self.fbo_1px)
|
||||
|
||||
glGenRenderbuffers(1, &self.color_renderbuffer)
|
||||
glGenRenderbuffers(1, &self.color_renderbuffer_1px)
|
||||
|
||||
if renpy.config.depth_size:
|
||||
glGenRenderbuffers(1, &self.depth_renderbuffer)
|
||||
glGenRenderbuffers(1, &self.depth_renderbuffer_1px)
|
||||
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size)
|
||||
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &max_renderbuffer_size)
|
||||
@@ -642,6 +684,7 @@ cdef class GL2Draw:
|
||||
self.texture_loader.max_texture_width = width
|
||||
self.texture_loader.max_texture_height = height
|
||||
|
||||
# Full-size fbo.
|
||||
self.change_fbo(self.fbo)
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, self.color_renderbuffer)
|
||||
@@ -672,6 +715,37 @@ cdef class GL2Draw:
|
||||
GL_RENDERBUFFER,
|
||||
self.depth_renderbuffer)
|
||||
|
||||
# 1px fbo.
|
||||
self.change_fbo(self.fbo_1px)
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, self.color_renderbuffer_1px)
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 1, 1)
|
||||
|
||||
glFramebufferRenderbuffer(
|
||||
GL_FRAMEBUFFER,
|
||||
GL_COLOR_ATTACHMENT0,
|
||||
GL_RENDERBUFFER,
|
||||
self.color_renderbuffer_1px)
|
||||
|
||||
if renpy.config.depth_size:
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, self.depth_renderbuffer_1px)
|
||||
|
||||
if self.gles:
|
||||
if renpy.config.depth_size >= 24:
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 1, 1)
|
||||
else:
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 1, 1)
|
||||
|
||||
else:
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, 1, 1)
|
||||
|
||||
glFramebufferRenderbuffer(
|
||||
GL_FRAMEBUFFER,
|
||||
GL_DEPTH_ATTACHMENT,
|
||||
GL_RENDERBUFFER,
|
||||
self.depth_renderbuffer_1px)
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, default_renderbuffer)
|
||||
self.change_fbo(self.default_fbo)
|
||||
|
||||
@@ -680,12 +754,20 @@ cdef class GL2Draw:
|
||||
|
||||
self.change_fbo(self.default_fbo)
|
||||
|
||||
# Full-size fbo.
|
||||
glDeleteFramebuffers(1, &self.fbo)
|
||||
glDeleteRenderbuffers(1, &self.color_renderbuffer)
|
||||
|
||||
if renpy.config.depth_size:
|
||||
glDeleteRenderbuffers(1, &self.depth_renderbuffer)
|
||||
|
||||
# 1px fbo.
|
||||
glDeleteFramebuffers(1, &self.fbo_1px)
|
||||
glDeleteRenderbuffers(1, &self.color_renderbuffer_1px)
|
||||
|
||||
if renpy.config.depth_size:
|
||||
glDeleteRenderbuffers(1, &self.depth_renderbuffer_1px)
|
||||
|
||||
|
||||
def can_block(self):
|
||||
"""
|
||||
@@ -940,7 +1022,7 @@ cdef class GL2Draw:
|
||||
self.load_all_textures(what)
|
||||
|
||||
# Switch to the right FBO, and the right viewport.
|
||||
self.change_fbo(self.fbo)
|
||||
self.change_fbo(self.fbo_1px)
|
||||
|
||||
# Set up the viewport.
|
||||
glViewport(0, 0, 1, 1)
|
||||
@@ -1149,7 +1231,7 @@ cdef class GL2DrawingContext:
|
||||
"""
|
||||
|
||||
cdef float halfwidth
|
||||
cdef float halfheigh
|
||||
cdef float halfheight
|
||||
|
||||
# This is the equivalent of projecting (0, 0, 0, 1), and getting x and y.
|
||||
cdef float sx = transform.xdw
|
||||
@@ -1175,9 +1257,6 @@ cdef class GL2DrawingContext:
|
||||
if model.reverse is not IDENTITY:
|
||||
transform = transform * model.reverse
|
||||
|
||||
if properties["pixel_perfect"]:
|
||||
transform = self.correct_pixel_perfect(transform)
|
||||
|
||||
# If a clip polygon is in place, clip the mesh with it.
|
||||
if clip_polygon is not None:
|
||||
|
||||
@@ -1271,10 +1350,13 @@ cdef class GL2DrawingContext:
|
||||
|
||||
has_reverse = (r.reverse is not None) and (r.reverse is not IDENTITY)
|
||||
|
||||
if r.properties and r.properties.get("pixel_perfect", False) and properties["pixel_perfect"] is None:
|
||||
transform = self.correct_pixel_perfect(transform)
|
||||
|
||||
if has_reverse or r.properties:
|
||||
properties = self.merge_properties(properties, r.properties)
|
||||
|
||||
if has_reverse and (properties["pixel_perfect"] is None):
|
||||
if has_reverse:
|
||||
properties["pixel_perfect"] = False
|
||||
|
||||
if r.shaders is not None:
|
||||
|
||||
@@ -8,11 +8,11 @@ cdef class AttributeLayout:
|
||||
|
||||
# A map from a string giving the name of the attribute to the
|
||||
# offset of the attribute.
|
||||
cdef dict offset
|
||||
cdef public dict offset
|
||||
|
||||
# The number of floats that make up the attributes for a single
|
||||
# point.
|
||||
cdef int stride
|
||||
cdef public int stride
|
||||
|
||||
cdef class Mesh:
|
||||
"""
|
||||
@@ -21,28 +21,28 @@ cdef class Mesh:
|
||||
"""
|
||||
|
||||
# The number of points that space has been allocated for.
|
||||
cdef int allocated_points
|
||||
cdef public int allocated_points
|
||||
|
||||
# The number of points that are in use.
|
||||
cdef int points
|
||||
cdef public int points
|
||||
|
||||
# The data corresponding to each point.
|
||||
cdef float *point_data
|
||||
|
||||
# The number of floats corresponding to each point.
|
||||
cdef int point_size
|
||||
cdef public int point_size
|
||||
|
||||
# An AttributeLayout object controlling how attributes are stored.
|
||||
cdef AttributeLayout layout
|
||||
cdef public AttributeLayout layout
|
||||
|
||||
# The non-geometry attribute data. This is allocated_points * attribute_per_point in size.
|
||||
cdef float *attribute
|
||||
|
||||
# The number of triangles that spaces has been allocated for.,
|
||||
cdef int allocated_triangles
|
||||
cdef public int allocated_triangles
|
||||
|
||||
# The number of triangles that are in use.
|
||||
cdef int triangles
|
||||
cdef public int triangles
|
||||
|
||||
# The triangle data, where each triangle consists of the index of three
|
||||
# points. This is 3 * allocated_triangles in size.
|
||||
|
||||
@@ -32,6 +32,80 @@ TEXTURE_LAYOUT.add_attribute("a_tex_coord", 2)
|
||||
|
||||
cdef class Mesh:
|
||||
|
||||
def set_geometry_data(self, geometry):
|
||||
"""
|
||||
Sets the geometry data corresponding to this mesh.
|
||||
|
||||
`geometry`
|
||||
This should be a sequence of floats, which are interpreted
|
||||
as x, y for a Mesh2 or x, y, z for a Mesh3. The length of
|
||||
the sequence must be a multiple of the point size of the
|
||||
mesh, and must be less than or equal to the number of
|
||||
allocated points.
|
||||
|
||||
This sets the `points` attribute of the mesh to the number of
|
||||
points in the geometry.
|
||||
"""
|
||||
|
||||
points = len(geometry) // self.point_size
|
||||
|
||||
if points > self.allocated_points:
|
||||
raise Exception("Geometry contains too much data.")
|
||||
|
||||
self.points = points
|
||||
cdef int i
|
||||
cdef int len_geometry = len(geometry)
|
||||
|
||||
for 0 <= i < len_geometry:
|
||||
self.point_data[i] = geometry[i]
|
||||
|
||||
def set_attribute_data(self, attributes):
|
||||
"""
|
||||
Sets the attribute data corresponding to this mesh.
|
||||
|
||||
`attributes`
|
||||
This should be a list of floats, with the first
|
||||
layout.stride floats corresponding to the first point, the
|
||||
next layout.stride floats corresponding to the second point,
|
||||
and so on. The length of the sequence must be a multiple of
|
||||
the stride of the layout, and must be less than or equal to
|
||||
the number of allocated points.
|
||||
"""
|
||||
|
||||
cdef int i
|
||||
cdef int len_attributes = len(attributes)
|
||||
|
||||
if len_attributes > self.allocated_points * self.layout.stride:
|
||||
raise Exception("Attributes contains too much data.")
|
||||
|
||||
for 0 <= i < len_attributes:
|
||||
self.attribute[i] = attributes[i]
|
||||
|
||||
def set_triangle_data(self, triangles):
|
||||
"""
|
||||
Sets the triangle data corresponding to this mesh.
|
||||
|
||||
`triangles`
|
||||
This should be a list of integers, with each triple
|
||||
corresponding to a triangle. The length of the sequence
|
||||
must be a multiple of 3, and must be less than or equal to
|
||||
the number of allocated triangles.
|
||||
|
||||
This sets the `triangles` attribute of the mesh to the number
|
||||
of triangles given here.
|
||||
"""
|
||||
|
||||
cdef int i
|
||||
cdef int len_triangles = len(triangles)
|
||||
|
||||
if len_triangles > self.allocated_triangles * 3:
|
||||
raise Exception("Triangles contains too much data.")
|
||||
|
||||
self.triangles = len_triangles // 3
|
||||
|
||||
for 0 <= i < len_triangles:
|
||||
self.triangle[i] = triangles[i]
|
||||
|
||||
def get_triangles(self):
|
||||
"""
|
||||
Returns the triangles that make up this mesh as triples.
|
||||
|
||||
@@ -105,7 +105,7 @@ class ShaderPart(object):
|
||||
used.add(m.group(0))
|
||||
|
||||
for l in variables.split("\n"):
|
||||
l = l.strip(' ;')
|
||||
l = l.partition("//")[0].strip(' ;')
|
||||
|
||||
a = l.split()
|
||||
if not a:
|
||||
|
||||
@@ -37,7 +37,7 @@ cdef class TextureLoader:
|
||||
|
||||
# The total size (in bytes) of all the textures that have been allocated
|
||||
# but not deallocated.
|
||||
cdef int total_texture_size
|
||||
cdef object total_texture_size
|
||||
|
||||
# The program used for fast texture loading
|
||||
cdef Program ftl_program
|
||||
|
||||
@@ -275,7 +275,6 @@ cdef class GLTexture(GL2Model):
|
||||
|
||||
# Update the loader.
|
||||
self.loader = loader
|
||||
self.loader.total_texture_size += self.width * self.height * 4
|
||||
|
||||
if renpy.emscripten and generate:
|
||||
# Generate a texture name to access video frames for web
|
||||
@@ -287,6 +286,15 @@ cdef class GLTexture(GL2Model):
|
||||
0.0, 0.0, width, height,
|
||||
0.0, 0.0, 1.0, 1.0,
|
||||
)
|
||||
self.properties = { }
|
||||
|
||||
def has_mipmaps(GLTexture self):
|
||||
"""
|
||||
Returns true if this texture has mipmaps (or will have mipmaps
|
||||
when it's loaded).
|
||||
"""
|
||||
|
||||
return self.properties.get("mipmap", True)
|
||||
|
||||
def get_number(GLTexture self):
|
||||
return self.number if renpy.emscripten else None
|
||||
@@ -311,6 +319,11 @@ cdef class GLTexture(GL2Model):
|
||||
This renders `what` to this texture.
|
||||
"""
|
||||
|
||||
self.properties = {
|
||||
"mipmap" : properties.get("mipmap", True),
|
||||
"pixel_perfect" : properties.get("pixel_perfect", False),
|
||||
}
|
||||
|
||||
cw, ch = size = what.get_size()
|
||||
|
||||
loader = self.loader
|
||||
@@ -381,9 +394,6 @@ cdef class GLTexture(GL2Model):
|
||||
self.number = premultiplied
|
||||
self.loader.allocated.add(self.number)
|
||||
|
||||
if "pixel_perfect" in properties:
|
||||
self.properties = { "pixel_perfect" : properties["pixel_perfect"] }
|
||||
|
||||
self.loaded = True
|
||||
|
||||
|
||||
@@ -494,8 +504,12 @@ cdef class GLTexture(GL2Model):
|
||||
# Going from a single to multiple mipmap levels takes ~9ms when loading
|
||||
# each mipmap, while allocating the space first reduces that to ~1ms.
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, tex)
|
||||
if self.has_mipmaps():
|
||||
self.loader.total_texture_size += int(self.width * self.height * 4 * 1.34)
|
||||
else:
|
||||
self.loader.total_texture_size += int(self.width * self.height * 4)
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, tex)
|
||||
|
||||
max_level = renpy.config.max_mipmap_level
|
||||
|
||||
@@ -525,7 +539,6 @@ cdef class GLTexture(GL2Model):
|
||||
|
||||
cdef GLuint level = 0
|
||||
|
||||
|
||||
while True:
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, tw, th, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
@@ -567,7 +580,10 @@ cdef class GLTexture(GL2Model):
|
||||
if self.loaded:
|
||||
self.loader.free_list.append(self.number)
|
||||
|
||||
self.loader.total_texture_size -= self.width * self.height * 4
|
||||
if self.has_mipmaps():
|
||||
self.loader.total_texture_size -= int(self.width * self.height * 4 * 1.34)
|
||||
else:
|
||||
self.loader.total_texture_size -= int(self.width * self.height * 4)
|
||||
except TypeError:
|
||||
pass # Let's not error on shutdown.
|
||||
|
||||
|
||||
+1
-1
@@ -671,7 +671,7 @@ class Live2D(renpy.display.core.Displayable):
|
||||
break
|
||||
|
||||
# Choose all possible nonexclusive attributes.
|
||||
for i in list(attributes) + list(optional):
|
||||
for i in sorted(list(attributes) + list(optional)):
|
||||
if i in common.nonexclusive:
|
||||
rv.append(i)
|
||||
|
||||
|
||||
+14
-21
@@ -178,6 +178,10 @@ lllword = re.compile(r'__(\w+)|\w+| +|.', re.S)
|
||||
def munge_filename(fn):
|
||||
# The prefix that's used when __ is found in the file.
|
||||
rv = os.path.basename(fn)
|
||||
|
||||
if rv.endswith("_ren.py"):
|
||||
rv = rv[:-7]
|
||||
|
||||
rv = os.path.splitext(rv)[0]
|
||||
rv = rv.replace(" ", "_")
|
||||
|
||||
@@ -1141,42 +1145,31 @@ class Lexer(object):
|
||||
return False
|
||||
|
||||
old_pos = self.pos
|
||||
c = self.text[self.pos]
|
||||
|
||||
# Allow unicode, raw, and formatted strings.
|
||||
for mod in ('u', 'r', 'f'):
|
||||
if c != mod:
|
||||
continue
|
||||
|
||||
self.pos += 1
|
||||
# Delimiter.
|
||||
start = self.match(r'[urfURF]*("""|\'\'\'|"|\')')
|
||||
|
||||
if self.pos == len(self.text):
|
||||
self.pos = old_pos
|
||||
return False
|
||||
|
||||
c = self.text[self.pos]
|
||||
|
||||
if c not in ('"', "'"):
|
||||
if not start:
|
||||
self.pos = old_pos
|
||||
return False
|
||||
|
||||
delim = c
|
||||
delim = start.lstrip('urfURF')
|
||||
|
||||
# String contents.
|
||||
while True:
|
||||
self.pos += 1
|
||||
|
||||
if self.eol():
|
||||
self.error("end of line reached while parsing string.")
|
||||
|
||||
c = self.text[self.pos]
|
||||
|
||||
if c == delim:
|
||||
if self.match(delim):
|
||||
break
|
||||
|
||||
if c == '\\':
|
||||
if self.match(r'\\'):
|
||||
self.pos += 1
|
||||
continue
|
||||
|
||||
self.match(r'.[^\'"\\]*')
|
||||
|
||||
self.pos += 1
|
||||
return True
|
||||
|
||||
def dotted_name(self):
|
||||
|
||||
+60
-34
@@ -40,7 +40,7 @@ import sys
|
||||
import renpy
|
||||
from json import dumps as json_dumps
|
||||
|
||||
from renpy.compat.pickle import dump, loads
|
||||
from renpy.compat.pickle import PROTOCOL, dump, loads
|
||||
|
||||
|
||||
# This is used as a quick and dirty way of versioning savegame
|
||||
@@ -86,6 +86,14 @@ def save_dump(roots, log):
|
||||
else:
|
||||
o_repr = "<method {0}.{1}>".format(o.__self__.__class__.__name__, o.__name__)
|
||||
|
||||
elif isinstance(o, types.FunctionType):
|
||||
if PY2:
|
||||
name = o.__name__
|
||||
else:
|
||||
name = o.__qualname__ or o.__name__
|
||||
|
||||
o_repr = o.__module__ + '.' + name
|
||||
|
||||
elif isinstance(o, object):
|
||||
o_repr = "<{0}>".format(type(o).__name__)
|
||||
|
||||
@@ -115,47 +123,55 @@ def save_dump(roots, log):
|
||||
elif isinstance(o, types.MethodType):
|
||||
size = 1 + visit(o.__self__, path + ".im_self")
|
||||
|
||||
elif isinstance(o, types.FunctionType):
|
||||
size = 1
|
||||
|
||||
else:
|
||||
|
||||
try:
|
||||
reduction = o.__reduce_ex__(2)
|
||||
reduction = o.__reduce_ex__(PROTOCOL)
|
||||
except Exception:
|
||||
reduction = [ ]
|
||||
o_repr = "BAD REDUCTION " + o_repr
|
||||
o_repr_cache[ido] = "BAD REDUCTION " + o_repr
|
||||
|
||||
# Gets an element from the reduction, or o if we don't have
|
||||
# such an element.
|
||||
def get(idx, default):
|
||||
if idx < len(reduction) and reduction[idx] is not None:
|
||||
return reduction[idx]
|
||||
else:
|
||||
return default
|
||||
if isinstance(reduction, basestring):
|
||||
o_repr_cache[ido] = o.__module__ + '.' + reduction
|
||||
size = 1
|
||||
|
||||
# An estimate of the size of the object, in arbitrary units. (These units are about 20-25 bytes on
|
||||
# my computer.)
|
||||
size = 1
|
||||
|
||||
state = get(2, { })
|
||||
if isinstance(state, dict):
|
||||
for k, v in state.items():
|
||||
size += 2
|
||||
size += visit(v, path + "." + k)
|
||||
else:
|
||||
size += visit(state, path + ".__getstate__()")
|
||||
# Gets an element from the reduction, or o if we don't have
|
||||
# such an element.
|
||||
def get(idx, default):
|
||||
if idx < len(reduction) and reduction[idx] is not None:
|
||||
return reduction[idx]
|
||||
else:
|
||||
return default
|
||||
|
||||
for i, oo in enumerate(get(3, [])): # type: ignore
|
||||
size += 1
|
||||
size += visit(oo, "{0}[{1}]".format(path, i))
|
||||
# An estimate of the size of the object, in arbitrary units.
|
||||
# (These units are about 20-25 bytes on my computer.)
|
||||
size = 1
|
||||
|
||||
for i in get(4, []): # type: ignore
|
||||
state = get(2, { })
|
||||
if isinstance(state, dict):
|
||||
for k, v in state.items():
|
||||
size += 2
|
||||
size += visit(v, path + "." + k)
|
||||
else:
|
||||
size += visit(state, path + ".__getstate__()")
|
||||
|
||||
if len(i) != 2:
|
||||
continue
|
||||
for i, oo in enumerate(get(3, [])): # type: ignore
|
||||
size += 1
|
||||
size += visit(oo, "{0}[{1}]".format(path, i))
|
||||
|
||||
k, v = i
|
||||
for i in get(4, []): # type: ignore
|
||||
|
||||
size += 2
|
||||
size += visit(v, "{0}[{1!r}]".format(path, k))
|
||||
if len(i) != 2:
|
||||
continue
|
||||
|
||||
k, v = i
|
||||
|
||||
size += 2
|
||||
size += visit(v, "{0}[{1!r}]".format(path, k))
|
||||
|
||||
f.write("{0: 7d} {1} = {2}\n".format(size, path, o_repr_cache[ido]))
|
||||
|
||||
@@ -452,11 +468,16 @@ def autosave_thread_function(take_screenshot):
|
||||
global autosave_counter
|
||||
global did_autosave
|
||||
|
||||
if renpy.config.autosave_prefix_callback:
|
||||
prefix = renpy.config.autosave_prefix_callback()
|
||||
else:
|
||||
prefix = "auto-"
|
||||
|
||||
try:
|
||||
|
||||
try:
|
||||
|
||||
cycle_saves("auto-", renpy.config.autosave_slots)
|
||||
cycle_saves(prefix, renpy.config.autosave_slots)
|
||||
|
||||
if renpy.config.auto_save_extra_info:
|
||||
extra_info = renpy.config.auto_save_extra_info()
|
||||
@@ -466,7 +487,7 @@ def autosave_thread_function(take_screenshot):
|
||||
if take_screenshot:
|
||||
renpy.exports.take_screenshot(background=True)
|
||||
|
||||
save("auto-1", mutate_flag=True, extra_info=extra_info)
|
||||
save(prefix + "1", mutate_flag=True, extra_info=extra_info)
|
||||
autosave_counter = 0
|
||||
|
||||
did_autosave = True
|
||||
@@ -561,12 +582,17 @@ def force_autosave(take_screenshot=False, block=False):
|
||||
else:
|
||||
extra_info = ""
|
||||
|
||||
cycle_saves("auto-", renpy.config.autosave_slots)
|
||||
if renpy.config.autosave_prefix_callback:
|
||||
prefix = renpy.config.autosave_prefix_callback()
|
||||
else:
|
||||
prefix = "auto-"
|
||||
|
||||
cycle_saves(prefix, renpy.config.autosave_slots)
|
||||
|
||||
if take_screenshot:
|
||||
renpy.exports.take_screenshot()
|
||||
|
||||
save("auto-1", extra_info=extra_info)
|
||||
save(prefix + "1", extra_info=extra_info)
|
||||
|
||||
return
|
||||
|
||||
@@ -736,7 +762,7 @@ def slot_json(slotname):
|
||||
Returns the json information for `slotname`, or None if the slot is
|
||||
empty.
|
||||
|
||||
Much like the ``d`` argument to the :var:`config.save_json_callback`
|
||||
Much like the ``d`` argument to the :var:`config.save_json_callbacks`
|
||||
function, it will be returned as a dictionary. More precisely, the
|
||||
dictionary will contain the same data as it did when the game was saved.
|
||||
"""
|
||||
|
||||
@@ -366,6 +366,9 @@ def main():
|
||||
choose_variants()
|
||||
renpy.display.touch = "touch" in renpy.config.variants
|
||||
|
||||
if (renpy.android or renpy.ios) and not renpy.config.log_to_stdout:
|
||||
print("Version:", renpy.version)
|
||||
|
||||
log_clock("Early init.")
|
||||
|
||||
# Note the game directory.
|
||||
|
||||
+7
-2
@@ -963,6 +963,9 @@ def define_statement(l, loc):
|
||||
elif l.match(r'\|='):
|
||||
operator = "|="
|
||||
else:
|
||||
if l.match(":"):
|
||||
l.match(r"[^=\n]*")
|
||||
|
||||
l.require('=')
|
||||
operator = "="
|
||||
|
||||
@@ -999,6 +1002,9 @@ def default_statement(l, loc):
|
||||
store = store + "." + name
|
||||
name = l.require(l.word)
|
||||
|
||||
if l.match(":"):
|
||||
l.match(r"[^=\n]*")
|
||||
|
||||
l.require('=')
|
||||
expr = l.rest()
|
||||
|
||||
@@ -1357,7 +1363,6 @@ def style_statement(l, loc):
|
||||
|
||||
# Parse priority and name.
|
||||
name = l.require(l.word)
|
||||
parent = None
|
||||
|
||||
rv = ast.Style(loc, name)
|
||||
|
||||
@@ -1366,7 +1371,7 @@ def style_statement(l, loc):
|
||||
def parse_clause(l):
|
||||
|
||||
if l.keyword("is"):
|
||||
if parent is not None:
|
||||
if rv.parent is not None:
|
||||
l.error("parent clause appears twice.")
|
||||
|
||||
rv.parent = l.require(l.word) # type: ignore
|
||||
|
||||
@@ -182,6 +182,9 @@ Preference("web_cache_preload", False)
|
||||
# Should the voice continue to play after the user enters the game menu.
|
||||
Preference("voice_after_game_menu", False)
|
||||
|
||||
# Should the game be maximized?
|
||||
Preference("maximized", False)
|
||||
|
||||
class Preferences(renpy.object.Object):
|
||||
"""
|
||||
Stores preferences that will one day be persisted.
|
||||
@@ -232,6 +235,7 @@ class Preferences(renpy.object.Object):
|
||||
audio_when_unfocused = True
|
||||
web_cache_preload = False
|
||||
voice_after_game_menu = False
|
||||
maximized = False
|
||||
|
||||
def init(self):
|
||||
"""
|
||||
@@ -319,7 +323,7 @@ class Preferences(renpy.object.Object):
|
||||
return self.mute[mixer]
|
||||
|
||||
def init_mixers(self):
|
||||
for i in renpy.audio.music.get_all_mixers() + ["main"]:
|
||||
for i in renpy.audio.music.get_all_mixers() + ["main", "voice"]:
|
||||
self.volumes.setdefault(i, 1.0)
|
||||
self.mute.setdefault(i, False)
|
||||
|
||||
|
||||
@@ -404,6 +404,33 @@ class StarredVariables(ast.NodeVisitor):
|
||||
# starred assignment.
|
||||
find_starred_variables = StarredVariables().find
|
||||
|
||||
class WrapFormattedValue(ast.NodeTransformer):
|
||||
"""
|
||||
This walks through the children of a FormattedValue, to look for
|
||||
nodes with the __name syntax, and format those nodes.
|
||||
"""
|
||||
|
||||
def visit_Name(self, node):
|
||||
|
||||
name = node.id
|
||||
|
||||
if not name.startswith("__"):
|
||||
return node
|
||||
|
||||
name = name[2:]
|
||||
|
||||
if (not name) or ("__" in name):
|
||||
return node
|
||||
|
||||
prefix = renpy.lexer.munge_filename(compile_filename)
|
||||
|
||||
name = prefix + name
|
||||
|
||||
return ast.Name(id=name, ctx=node.ctx, lineno=node.lineno, col_offset=node.col_offset, end_lineno=node.end_lineno, end_col_offset=node.end_col_offset)
|
||||
|
||||
wrap_formatted_value = WrapFormattedValue().visit
|
||||
|
||||
|
||||
class WrapNode(ast.NodeTransformer):
|
||||
|
||||
|
||||
@@ -696,6 +723,10 @@ class WrapNode(ast.NodeTransformer):
|
||||
kwargs=None)
|
||||
|
||||
|
||||
def visit_FormattedValue(self, n):
|
||||
n = wrap_formatted_value(n)
|
||||
return self.generic_visit(n)
|
||||
|
||||
wrap_node = WrapNode()
|
||||
|
||||
|
||||
@@ -899,6 +930,9 @@ def quote_eval(s):
|
||||
return "".join(rv[:-2])
|
||||
|
||||
|
||||
# The filename being compiled.
|
||||
compile_filename = ""
|
||||
|
||||
def py_compile(source, mode, filename='<none>', lineno=1, ast_node=False, cache=True, py=None):
|
||||
"""
|
||||
Compiles the given source code using the supplied codegenerator.
|
||||
@@ -925,6 +959,8 @@ def py_compile(source, mode, filename='<none>', lineno=1, ast_node=False, cache=
|
||||
that would be used.
|
||||
"""
|
||||
|
||||
global compile_filename
|
||||
|
||||
if ast_node:
|
||||
cache = False
|
||||
|
||||
@@ -974,6 +1010,7 @@ def py_compile(source, mode, filename='<none>', lineno=1, ast_node=False, cache=
|
||||
source = quote_eval(source)
|
||||
|
||||
line_offset = lineno - 1
|
||||
compile_filename = filename
|
||||
|
||||
try:
|
||||
|
||||
|
||||
+5
-1
@@ -751,6 +751,8 @@ class RollbackLog(renpy.object.Object):
|
||||
"""
|
||||
|
||||
self.checkpointing_suspended = flag
|
||||
self.current.not_greedy = True
|
||||
renpy.game.contexts[0].force_checkpoint = True
|
||||
|
||||
def block(self, purge=False):
|
||||
"""
|
||||
@@ -759,6 +761,8 @@ class RollbackLog(renpy.object.Object):
|
||||
"""
|
||||
|
||||
self.rollback_limit = 0
|
||||
if self.current is not None:
|
||||
self.current.not_greedy = True
|
||||
renpy.game.context().force_checkpoint = True
|
||||
|
||||
if purge:
|
||||
@@ -897,7 +901,7 @@ class RollbackLog(renpy.object.Object):
|
||||
force_checkpoint = False
|
||||
|
||||
# Try to rollback to just after the previous checkpoint.
|
||||
while greedy and self.log and (self.rollback_limit > 0):
|
||||
while greedy and self.log:
|
||||
|
||||
rb = self.log[-1]
|
||||
|
||||
|
||||
+35
-13
@@ -355,6 +355,9 @@ class SLBlock(SLNode):
|
||||
# RawBlock from parse or None if not present.
|
||||
atl_transform = None
|
||||
|
||||
# The actual transform created from the atl transform.
|
||||
transform = None
|
||||
|
||||
def __init__(self, loc):
|
||||
SLNode.__init__(self, loc)
|
||||
|
||||
@@ -439,6 +442,9 @@ class SLBlock(SLNode):
|
||||
const = self.atl_transform.constant
|
||||
self.constant = min(self.constant, const)
|
||||
|
||||
self.transform = renpy.display.transform.ATLTransform(self.atl_transform)
|
||||
renpy.atl.compile_queue.append(self.transform)
|
||||
|
||||
was_last_keyword = False
|
||||
for i in self.children:
|
||||
if i.has_keyword:
|
||||
@@ -485,7 +491,18 @@ class SLBlock(SLNode):
|
||||
|
||||
if self.atl_transform is not None:
|
||||
transform = ATLTransform(self.atl_transform, context=context.scope)
|
||||
context.keywords["at"] = transform
|
||||
transform.parent_transform = self.transform # type: ignore
|
||||
|
||||
if "at" in context.keywords:
|
||||
try:
|
||||
at_list = list(context.keywords["at"])
|
||||
except TypeError:
|
||||
at_list = [ context.keywords["at"] ]
|
||||
|
||||
at_list.append(transform)
|
||||
context.keywords["at"] = at_list
|
||||
else:
|
||||
context.keywords["at"] = transform
|
||||
|
||||
style_prefix = context.keywords.pop("style_prefix", NotGiven)
|
||||
|
||||
@@ -2048,15 +2065,13 @@ class SLUse(SLNode):
|
||||
|
||||
ctx.old_cache = context.old_use_cache.get(use_id, None) or context.old_cache.get(self.serial, None) or { }
|
||||
|
||||
if use_id in ctx.old_use_cache:
|
||||
ctx.updating = True
|
||||
|
||||
ctx.new_use_cache[use_id] = ctx.new_cache
|
||||
|
||||
else:
|
||||
|
||||
ctx.old_cache = context.old_cache.get(self.serial, None) or { }
|
||||
|
||||
|
||||
if not isinstance(ctx.old_cache, dict):
|
||||
ctx.old_cache = { }
|
||||
if not isinstance(ctx.miss_cache, dict):
|
||||
@@ -2076,11 +2091,15 @@ class SLUse(SLNode):
|
||||
args = [ ]
|
||||
kwargs = { }
|
||||
|
||||
if ctx.updating:
|
||||
scope = ctx.old_cache.get("scope", None) or ctx.miss_cache.get("scope", None) or { }
|
||||
else:
|
||||
scope = { }
|
||||
|
||||
# Apply the arguments to the parameters (if present) or to the scope of the used screen.
|
||||
if ast.parameters is not None:
|
||||
new_scope = ast.parameters.apply(args, kwargs, ignore_errors=context.predicting)
|
||||
|
||||
scope = ctx.old_cache.get("scope", None) or ctx.miss_cache.get("scope", None) or { }
|
||||
scope.update(new_scope)
|
||||
|
||||
else:
|
||||
@@ -2088,13 +2107,14 @@ class SLUse(SLNode):
|
||||
if args:
|
||||
raise Exception("Screen {} does not take positional arguments. ({} given)".format(self.target, len(args)))
|
||||
|
||||
scope = ctx.old_cache.get("scope", None) or ctx.miss_cache.get("scope", None) or { }
|
||||
scope.clear()
|
||||
scope.update(context.scope)
|
||||
scope.update(kwargs)
|
||||
|
||||
scope["_scope"] = scope
|
||||
ctx.new_cache["scope"] = scope
|
||||
|
||||
if not ctx.predicting:
|
||||
ctx.new_cache["scope"] = scope
|
||||
|
||||
# Run the child screen.
|
||||
ctx.scope = scope
|
||||
@@ -2332,9 +2352,6 @@ class SLCustomUse(SLNode):
|
||||
|
||||
ctx.old_cache = context.old_use_cache.get(use_id, None) or context.old_cache.get(self.serial, None) or { }
|
||||
|
||||
if use_id in ctx.old_use_cache:
|
||||
ctx.updating = True
|
||||
|
||||
ctx.new_use_cache[use_id] = ctx.new_cache
|
||||
|
||||
else:
|
||||
@@ -2348,11 +2365,15 @@ class SLCustomUse(SLNode):
|
||||
|
||||
ast = self.ast
|
||||
|
||||
if ctx.updating:
|
||||
scope = ctx.old_cache.get("scope", None) or ctx.miss_cache.get("scope", None) or { }
|
||||
else:
|
||||
scope = { }
|
||||
|
||||
# Apply the arguments to the parameters (if present) or to the scope of the used screen.
|
||||
if ast.parameters is not None:
|
||||
new_scope = ast.parameters.apply(args, kwargs, ignore_errors=context.predicting)
|
||||
|
||||
scope = ctx.old_cache.get("scope", None) or ctx.miss_cache.get("scope", None) or { }
|
||||
scope.update(new_scope)
|
||||
|
||||
else:
|
||||
@@ -2360,13 +2381,14 @@ class SLCustomUse(SLNode):
|
||||
if args:
|
||||
raise Exception("Screen {} does not take positional arguments. ({} given)".format(self.target, len(args)))
|
||||
|
||||
scope = ctx.old_cache.get("scope", None) or ctx.miss_cache.get("scope", None) or { }
|
||||
scope.clear()
|
||||
scope.update(context.scope)
|
||||
scope.update(kwargs)
|
||||
|
||||
scope["_scope"] = scope
|
||||
ctx.new_cache["scope"] = scope
|
||||
|
||||
if not ctx.predicting:
|
||||
ctx.new_cache["scope"] = scope
|
||||
|
||||
# Run the child screen.
|
||||
ctx.scope = scope
|
||||
|
||||
@@ -188,6 +188,7 @@ Keyword("pixel_width")
|
||||
Keyword("value")
|
||||
Keyword("mask")
|
||||
Keyword("caret_blink")
|
||||
Keyword("multiline")
|
||||
Style("caret")
|
||||
add(text_properties)
|
||||
|
||||
|
||||
+16
-8
@@ -282,15 +282,11 @@ class Parser(object):
|
||||
else:
|
||||
l.error('%r is not a keyword argument or valid child of the %s statement.' % (name, self.name))
|
||||
|
||||
if name in seen_keywords:
|
||||
l.error('keyword argument %r appears more than once in a %s statement.' % (name, self.name))
|
||||
incomprop = check_incompatible_props(name, seen_keywords)
|
||||
if incomprop:
|
||||
l.deferred_error("check_conflicting_properties", 'keyword argument {!r} is incompatible with {!r}.'.format(name, incomprop))
|
||||
if name == "at" and l.keyword("transform"):
|
||||
|
||||
seen_keywords.add(name)
|
||||
if target.atl_transform is not None:
|
||||
l.error("More than one 'at transform' block is given.")
|
||||
|
||||
if name == "at" and block and l.keyword("transform"):
|
||||
l.require(":")
|
||||
l.expect_eol()
|
||||
l.expect_block("ATL block")
|
||||
@@ -298,6 +294,17 @@ class Parser(object):
|
||||
target.atl_transform = expr
|
||||
return
|
||||
|
||||
if name in seen_keywords:
|
||||
l.error('keyword argument %r appears more than once in a %s statement.' % (name, self.name))
|
||||
incomprop = check_incompatible_props(name, seen_keywords)
|
||||
if incomprop:
|
||||
l.deferred_error("check_conflicting_properties", 'keyword argument {!r} is incompatible with {!r}.'.format(name, incomprop))
|
||||
|
||||
if name == "at" and target.atl_transform:
|
||||
l.error("The 'at' property must occur before the 'at transform' block.")
|
||||
|
||||
seen_keywords.add(name)
|
||||
|
||||
expr = l.comma_expression()
|
||||
if expr is None:
|
||||
l.error("the {} keyword argument was not given a value.".format(name))
|
||||
@@ -330,7 +337,8 @@ class Parser(object):
|
||||
break
|
||||
|
||||
if l.eol():
|
||||
l.expect_noblock(self.name)
|
||||
if not target.atl_transform:
|
||||
l.expect_noblock(self.name)
|
||||
block = False
|
||||
break
|
||||
|
||||
|
||||
@@ -646,6 +646,15 @@ def new_change_language(tl, language):
|
||||
|
||||
renpy.config.init_system_styles()
|
||||
|
||||
def clean_data():
|
||||
"""
|
||||
This cleans out data that's dependent on the language.
|
||||
"""
|
||||
|
||||
renpy.store._history_list = renpy.store.list() # type: ignore
|
||||
renpy.store.nvl_list = renpy.store.list() # type: ignore
|
||||
renpy.game.log.forward = [ ]
|
||||
|
||||
|
||||
def change_language(language, force=False):
|
||||
"""
|
||||
@@ -658,8 +667,7 @@ def change_language(language, force=False):
|
||||
global old_language
|
||||
|
||||
if old_language != language:
|
||||
renpy.store._history_list = renpy.store.list() # type: ignore
|
||||
renpy.store.nvl_list = renpy.store.list() # type: ignore
|
||||
clean_data()
|
||||
|
||||
renpy.game.preferences.language = language
|
||||
if old_language == language and not force:
|
||||
@@ -728,7 +736,7 @@ def check_language():
|
||||
# has already set translate_identifier to None.
|
||||
ctx.translate_identifier = tid
|
||||
|
||||
renpy.game.log.forward = [ ]
|
||||
clean_data()
|
||||
|
||||
raise renpy.game.JumpException(node.name)
|
||||
|
||||
|
||||
@@ -470,7 +470,7 @@ def translate_command():
|
||||
ap.add_argument("--empty", help="Produce empty strings while generating translations.", dest="empty", action="store_true")
|
||||
ap.add_argument("--count", help="Instead of generating files, print a count of missing translations.", dest="count", action="store_true")
|
||||
ap.add_argument("--min-priority", help="Translate strings with more than this priority.", dest="min_priority", default=0, type=int)
|
||||
ap.add_argument("--max-priority", help="Translate strings with more than this priority.", dest="max_priority", default=0, type=int)
|
||||
ap.add_argument("--max-priority", help="Translate strings with less than this priority.", dest="max_priority", default=0, type=int)
|
||||
ap.add_argument("--strings-only", help="Only translate strings (not dialogue).", dest="strings_only", default=False, action="store_true")
|
||||
ap.add_argument("--common-only", help="Only translate string from the common code.", dest="common_only", default=False, action="store_true")
|
||||
ap.add_argument("--no-todo", help="Do not include the TODO flag.", dest="todo", default=True, action="store_false")
|
||||
|
||||
+7
-3
@@ -177,6 +177,7 @@ def enqueue(relpath, rtype, data):
|
||||
global queue
|
||||
|
||||
with queue_lock:
|
||||
voice_count = 0
|
||||
for rr in queue:
|
||||
# de-dup same .data/image_filename
|
||||
# don't de-dup same .relpath (different .data == different cache entry)
|
||||
@@ -189,9 +190,12 @@ def enqueue(relpath, rtype, data):
|
||||
elif rr.rtype == rtype == 'voice':
|
||||
if rr.relpath == relpath:
|
||||
return
|
||||
if len([True for rr in queue if rr.type == 'voice']) > renpy.config.predict_statements:
|
||||
# don't stack skipped dialogs
|
||||
return
|
||||
voice_count += 1
|
||||
|
||||
if voice_count > renpy.config.predict_statements:
|
||||
# don't stack skipped dialogs
|
||||
return
|
||||
|
||||
queue.append(ReloadRequest(relpath, rtype, data))
|
||||
|
||||
|
||||
|
||||
@@ -311,20 +311,35 @@ The following transform properties are used by the 3D Stage.
|
||||
variables are floats, this is relative to the size of the child, otherwise
|
||||
it's absolute pixels.
|
||||
|
||||
This sets the location of the (0, 0, 0) point that poi, orientation,
|
||||
xrotate, yrotate, zrotate and matrixtransform applie its transform to.
|
||||
This sets the location of the (0, 0, 0) point that point_to, orientation,
|
||||
xrotate, yrotate, zrotate and matrixtransform apply their transforms to.
|
||||
|
||||
.. transform-property:: poi
|
||||
.. transform-property:: point_to
|
||||
|
||||
:type: (float, float, float) or None
|
||||
:type: (float, float, float), Camera, or None
|
||||
:default: None
|
||||
|
||||
This gives the position of the point of interest. The camera or the
|
||||
displayable being transformed are rotated to face the point of interest,
|
||||
This gives a position to which to point. The camera or the
|
||||
displayable being transformed are rotated to face that point,
|
||||
even if the position of the camera or the displayable is changed.
|
||||
|
||||
If this is None, no point-of-interest rotation is applied.
|
||||
|
||||
If this is not None, this is a 3-tuple or an instance of :func:`Camera`.
|
||||
An (x, y, z) format tuple represents the position of the point of interest.
|
||||
An instance of Camera means to point at the camera.
|
||||
|
||||
Note point_to isn't updated automatically. so, you should write like below if
|
||||
you want it is updated::
|
||||
|
||||
# eileen always faces to the camera.
|
||||
show eileen happy at center:
|
||||
point_to Camera()
|
||||
0
|
||||
repeat
|
||||
|
||||
.. include:: inc/point_to_camera
|
||||
|
||||
.. transform-property:: orientation
|
||||
|
||||
:type: (float, float, float) or None
|
||||
|
||||
@@ -1148,7 +1148,7 @@ both horizontal and vertical positions.
|
||||
There are also several sets of transform properties that are documented elsewhere:
|
||||
|
||||
3D Stage properties:
|
||||
:tpref:`perspective`, :tpref:`poi`, :tpref:`orientation`, :tpref:`xrotate`, :tpref:`yrotate`, :tpref:`zrotate`, :tpref:`matrixanchor`, :tpref:`matrixtransform`, :tpref:`zpos`, :tpref:`zzoom`
|
||||
:tpref:`perspective`, :tpref:`point_to`, :tpref:`orientation`, :tpref:`xrotate`, :tpref:`yrotate`, :tpref:`zrotate`, :tpref:`matrixanchor`, :tpref:`matrixtransform`, :tpref:`zpos`, :tpref:`zzoom`
|
||||
|
||||
Model-based rendering properties:
|
||||
:tpref:`blend`, :tpref:`mesh`, :tpref:`mesh_pad`, :tpref:`shader`
|
||||
@@ -1167,7 +1167,7 @@ These properties are applied in the following order:
|
||||
#. crop, corner1, corner2
|
||||
#. xysize, size, maxsize
|
||||
#. zoom, xzoom, yzoom
|
||||
#. poi
|
||||
#. point_to
|
||||
#. orientation
|
||||
#. xrotate, yrotate, zrotate
|
||||
#. rotate
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
Speech Bubbles
|
||||
==============
|
||||
|
||||
Ren'Py supports dialogue that's displayed in speech bubbles, which can be
|
||||
interactively positioned on the screen. This provides an alternative to
|
||||
the textboxes used by ADV-style games, and the full screen dialogue used
|
||||
by NVL-mode.
|
||||
|
||||
To use speech bubbles your game, you'll have to define Characters with
|
||||
an image tag, a kind of ``bubble``. For example, ::
|
||||
|
||||
define e = Character(None, image="eileen", kind=bubble) # Eileen
|
||||
define l = Character(None, image="lucy", kind=bubble) # Lucy
|
||||
|
||||
While a name is supported, in general the speaking character will be
|
||||
implied by the tails of the bubble, so the name can be omitted.
|
||||
|
||||
You may then use these characters to write dialogue normally.
|
||||
|
||||
To position the balloons, hit shift+B to display the speech bubble editor.
|
||||
For each character that has a speech balloon, this will have two buttons
|
||||
in it.
|
||||
|
||||
Pressing the area button will launch the speech bubble editor. This editor
|
||||
lets you drag to select the area where the speech bubble will be placed,
|
||||
on a grid. When you complete the drag, the speech bubble will will change
|
||||
locations.
|
||||
|
||||
Pressing the properties buttons will select between sets of properties
|
||||
associated with the speech bubble. For the default speech bubble, the
|
||||
different properties control the positionm of the speech bubble tail.
|
||||
|
||||
Once you've changed the area or properties for a character (or group of
|
||||
characters with the same image tage), those properties remain set until
|
||||
changed again, or until the next scene statement.
|
||||
|
||||
When the area or properties are being set on the current line of dialogue,
|
||||
the corresponding line is brighter. If the values are being inherited from
|
||||
a prior line of dialogue or the default, the button is dimmed out. Right
|
||||
clicking on a button will prevent the current line from setting the value.
|
||||
|
||||
Tips
|
||||
----
|
||||
|
||||
The speech bubbles use the same identifiers used by the translation system.
|
||||
These identifiers can change if:
|
||||
|
||||
* The text of a line changes.
|
||||
* A second line with the same text inside the same label is added or removed.
|
||||
* The label before the line is added or removes (however, adding or removing
|
||||
a label with the ``hide`` clause will not change the translation identifier).
|
||||
|
||||
If you edit a scene, it's suggested that you replay through it to make sure
|
||||
the changes did not affect speech bubble placement.
|
||||
|
||||
|
||||
Configuration Variables
|
||||
-----------------------
|
||||
|
||||
The speech bubble system is controlled by variables in th ``bubble`` namespace,
|
||||
and by the ``bubble`` screen and its associated styles.
|
||||
|
||||
The ``bubble`` namespace contains the following variables:
|
||||
|
||||
.. var:: bubble.db_filename = "bubble.json"
|
||||
|
||||
The database file, stored in the game directory, that contains the
|
||||
speech bubble information.
|
||||
|
||||
|
||||
.. var:: bubble.cols = 24
|
||||
|
||||
The granularity of the grid that's used to position and size speech bubbles,
|
||||
in the horizontal direction.
|
||||
|
||||
.. var:: bubble.rows = 24
|
||||
|
||||
The granularity of the grid that's used to position and size speech bubbles,
|
||||
in the vertical direction.
|
||||
|
||||
.. var:: bubble.default_area = (15, 1, 8, 5)
|
||||
|
||||
This is the default area that speech bubbles are placed in, if no other
|
||||
area is specified. This is a tuple of the form (x, y, w, h),
|
||||
where each value is a number of grid cells.
|
||||
|
||||
|
||||
.. var:: bubble.properties = { ... }
|
||||
|
||||
These are properties, apart from the area, that can be used to customize
|
||||
the speech bubble. This is a map from the name of a set of proprerties
|
||||
to a dictionary of properties and values. These properties supersede the properties given
|
||||
the character, and are then supplied to the ``bubble`` screen.
|
||||
|
||||
This uses the same prefixing system as :func:`Character` does. Properties
|
||||
beginning with ``window_`` have the prefix removed, and are passed to the
|
||||
displayable with id "window" in the bubble screen, which is the bubble
|
||||
itself. Properties with ``what_`` have the prefix removed, and are passed
|
||||
to the displayable with id "what" in the bubble screen, which is the text
|
||||
of the bubble. Properties with ``who_`` are handled similarly, and given
|
||||
to the characters name. Properties with ``show_`` are given as arguments
|
||||
to the bubble screen itself.
|
||||
|
||||
In a new game, screens.rpy includes::
|
||||
|
||||
define bubble.frame = Frame("gui/bubble.png", 55, 55, 55, 95)
|
||||
|
||||
define bubble.properties = {
|
||||
"bottom_left" : {
|
||||
"window_background" : Transform(bubble.frame, xzoom=1, yzoom=1),
|
||||
"window_bottom_padding" : 27,
|
||||
},
|
||||
|
||||
"bottom_right" : {
|
||||
"window_background" : Transform(bubble.frame, xzoom=-1, yzoom=1),
|
||||
"window_bottom_padding" : 27,
|
||||
},
|
||||
|
||||
"top_left" : {
|
||||
"window_background" : Transform(bubble.frame, xzoom=1, yzoom=-1),
|
||||
"window_top_padding" : 27,
|
||||
},
|
||||
|
||||
"top_right" : {
|
||||
"window_background" : Transform(bubble.frame, xzoom=-1, yzoom=-1),
|
||||
"window_top_padding" : 27,
|
||||
},
|
||||
}
|
||||
|
||||
The bubble.frame variable is just used to make defining bubble.properties
|
||||
easier. Then for each of the four styles of bubble, the bubble is flipped
|
||||
so the tail is in the right place, and the padding is adjusted to leave
|
||||
room for the tail.
|
||||
|
||||
.. var:: bubble.properties_order = [ ]
|
||||
|
||||
This is a list of the names of the sets of properties, in the order they should
|
||||
be cycled through in the speech bubble editor. If the names of the sets of properties
|
||||
are not given, the properties are cycled through in alphabetical order.
|
||||
|
||||
.. var:: bubble.expand_area = { ... }
|
||||
|
||||
This is a map from the name of a set of properties to a (left, top, right, bottom)
|
||||
tuple. If found in this set, the area of the speech bubble is expanded by the
|
||||
given number of pixels.
|
||||
|
||||
This makes the speech bubble bigger than the area the creator dragged out.
|
||||
The intent is that this can be used to drag out the body of the speech
|
||||
bubble without concern for the tail, and also for the text itself to stay
|
||||
put when the set of properties is changed and the tail moves.
|
||||
|
||||
By default, this is::
|
||||
|
||||
define bubble.expand_area = {
|
||||
"bottom_left" : (0, 0, 0, 22),
|
||||
"bottom_right" : (0, 0, 0, 22),
|
||||
"top_left" : (0, 22, 0, 0),
|
||||
"top_right" : (0, 22, 0, 0),
|
||||
}
|
||||
|
||||
Bubble Screen
|
||||
-------------
|
||||
|
||||
The default ``bubble`` screen can be found in ``screens.rpy``, and is similar
|
||||
to the default ``say`` screen::
|
||||
|
||||
screen bubble(who, what):
|
||||
style_prefix "bubble"
|
||||
|
||||
window:
|
||||
id "window"
|
||||
|
||||
if who is not None:
|
||||
|
||||
window:
|
||||
id "namebox"
|
||||
style "bubble_namebox"
|
||||
|
||||
text who:
|
||||
id "who"
|
||||
|
||||
text what:
|
||||
id "what"
|
||||
|
||||
It's separate from the say screen as it uses its own set of styles, including
|
||||
``bubble_window``, ``bubble_what``, ``bubble_namebox``, and ``bubble_who``.
|
||||
These styles can be customized directly to avoid having to set a property
|
||||
in all of the sets of properties in :var:`bubble.properties`.
|
||||
@@ -400,6 +400,21 @@ The following variables provide further control of the build process:
|
||||
click "Build distributions", "Upload to itch.io" to cause an upload
|
||||
to occur.
|
||||
|
||||
.. var:: build.itch_channels = { ... }
|
||||
|
||||
This maps a filename pattern (such as "\*-win.zip") to a string giving
|
||||
the itch channel the file should be uploaded to. This defaults to::
|
||||
|
||||
{
|
||||
"*-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",
|
||||
}
|
||||
|
||||
.. var:: build.mac_info_plist = { }
|
||||
|
||||
This is a dictionary mapping strings to strings, that can be used to
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user