Compare commits

..

233 Commits

Author SHA1 Message Date
Tom Rothamel 5534516253 Take background screenshots when polling events.
This prevents constant animation from preventing autosaves from
happening.
2019-05-02 22:50:06 -04:00
Tom Rothamel a445a7b000 Screens take a sensitive property.
If False, than the screen does not respond to input.

Fixes #1755.
2019-05-01 22:16:20 -04:00
Tom Rothamel 601594e047 Text interpolations support !c, !u, and !l.
For capitalization, upper case, and lower case.
2019-04-30 23:31:45 -04:00
Tom Rothamel cedff0527f Merge pull request #1808 from Andykl/fix-console
Do short repr for defaultdict and OrderedDict
2019-04-30 16:59:14 -04:00
Andy_kl 2b1f812051 Do short repr for defaultdict and OrderedDict 2019-04-30 23:57:41 +03:00
Tom Rothamel c85c917d12 Merge pull request #1803 from Moshibit/master
Corrections in the Spanish translation of the tutorial
2019-04-30 00:16:18 -04:00
Tom Rothamel f2040ea6d5 On emscripten, force a redraw after is_pixel_opaque.
This fixes an issue with emscripten where it flips GL buffers
without Ren'Py explicitly asking for it.
2019-04-28 18:03:38 -04:00
Sylvain Beucler 7602e726f2 web: self.redraw_period=1.0 re-introduced audio glitch 2019-04-28 19:53:24 +02:00
Tom Rothamel dce59141a7 web: Avoid the use of float arguments.
This is because of a bug in emscripten, see
https://github.com/emscripten-core/emscripten/issues/6759 for
the details.
2019-04-28 13:09:11 -04:00
Sylvain Beucler 8dfafd617b web: use FBO and GLES 2019-04-28 15:01:44 +02:00
Sylvain Beucler a28829662c web: Fix 'TypeError: coercing to Unicode: need string or buffer, NoneType found' 2019-04-28 14:22:33 +02:00
Tom Rothamel 9665ee9226 Add config.equal_mono.
This changes mono support so there's two channels at 100%, rather
than each channel getting 50% of the energy.
2019-04-27 23:21:47 -04:00
Tom Rothamel b79419ea2f Support for equal_mono. 2019-04-27 22:28:22 -04:00
Tom Rothamel b2787cc61d Clear the history list on language change.
Fixes #1797.
2019-04-27 02:57:06 -04:00
Moshibit 8c45a9a1ec Add files via upload 2019-04-27 00:08:59 -05:00
Tom Rothamel fc45dca4c8 Merge pull request #1798 from Moshibit/master
Add Spanish translation for Ren'Py Tutorial
2019-04-26 17:12:14 -04:00
Tom Rothamel be4d4290a8 Rename the internal copy of OpenDyslexic.
This is so it doesn't interfere with a game that already includes
a copy.
2019-04-25 18:58:52 -04:00
Tom Rothamel 59c49a6512 Documentation tweak to config.reload_modules. 2019-04-24 17:56:51 -04:00
Tom Rothamel 6fedb10d30 Merge pull request #1801 from mal/reload-modules
Python module reloading support
2019-04-24 17:55:15 -04:00
Tom Rothamel 5eb650d3fb Merge pull request #1802 from mal/master
Keep vanilla python's late import binding
2019-04-24 17:53:57 -04:00
Mal Graty 8c2d509b9c Keep vanilla python's late import binding 2019-04-24 19:41:25 +01:00
Mal Graty 1eee09bb9e Document config.reload_modules 2019-04-24 19:13:06 +01:00
Sylvain Beucler bacc645355 renpyweb: link to a controlled donation URL 2019-04-24 18:45:39 +02:00
Sylvain Beucler db83f8462d renpyweb: more sound tuning
No real user feedback despite dedicated test URLs, so using my system as reference.
2019-04-24 18:40:51 +02:00
Sylvain Beucler 4ee3026cad renpyweb: consistent capitalization 2019-04-24 18:38:08 +02:00
Tom Rothamel cc6594d068 web: Fix tyop. 2019-04-23 22:34:21 -04:00
Tom Rothamel 3469504ffd web: Since Popen doesn't exist, do not modify the class. 2019-04-23 22:33:38 -04:00
Tom Rothamel 7954d1d9cd web: Serve built games through a local webserver. 2019-04-23 20:22:03 -04:00
Sylvain Beucler 8706d19545 Typo in French translation 2019-04-23 21:50:38 +02:00
Mal Graty 666370ac7f Python module reloading support 2019-04-23 19:03:08 +01:00
Tom Rothamel 88722cc03b web: Set the title of the generated page based on display_name. 2019-04-22 21:20:00 -04:00
Tom Rothamel 64171052ac Default display_name to config.name.
It seems like a closer match to what display_name is used for.
2019-04-22 21:17:15 -04:00
Tom Rothamel 793a2b2250 web: One click build of a web app. 2019-04-22 02:08:45 -04:00
Moshibit d16e1e71b8 Add files via upload 2019-04-21 16:48:25 -05:00
Moshibit cf56ebc008 Delete common.rpyc 2019-04-21 14:50:30 -05:00
Moshibit 26166cfdd8 Delete 01example.rpyc 2019-04-21 14:50:01 -05:00
Moshibit c5f60655d3 Add files via upload
Update translations.
2019-04-21 14:49:14 -05:00
Tom Rothamel 0ef5e6650e web: Tweak wording of the web screen, add Beuc's info. 2019-04-21 11:43:13 -04:00
Moshibit 036bdc4afe Add files via upload 2019-04-21 01:46:10 -05:00
Moshibit 33d8c014ec Add files via upload 2019-04-21 00:51:20 -05:00
Moshibit 1b193e6884 Add files via upload 2019-04-20 23:50:22 -05:00
Moshibit d6b7786f60 Add files via upload 2019-04-20 18:04:49 -05:00
Tom Rothamel a1bcb09260 web: Interface for creating a web project. 2019-04-20 16:10:50 -04:00
Moshibit d05704c7c7 Add files via upload 2019-04-20 13:40:11 -05:00
Tom Rothamel 68af2f3db2 Shrink the spacer sizes.
This provides a little extra room as we add the Web button to
the launcher.
2019-04-20 12:23:36 -04:00
Tom Rothamel 9b6ab94970 Add SimpleHTTPServer to the list of modules Ren'Py imports. 2019-04-20 10:37:00 -04:00
Tom Rothamel 51abcae5d4 Revert "Log startup timings to the Javascript console."
This reverts commit 045b6cc227.
2019-04-20 01:35:44 -04:00
Tom Rothamel 79e9d81173 web: Distribute compiled Ren'Py files as part of the web distribution.
This includes the .pyo files, and also creates a main.py to start
the game up.
2019-04-20 01:03:22 -04:00
Tom Rothamel 4e60a996dd Merge pull request #1796 from jbeich/bsdarm
Unbreak ARM build on BSDs
2019-04-19 17:01:11 -04:00
Jan Beich 15738d1dfb Unbreak ARM build on BSDs
In file included from ffmedia.c:13:
/usr/include/malloc.h:3:2: error: "<malloc.h> has been replaced by <stdlib.h>"
 #error "<malloc.h> has been replaced by <stdlib.h>"
  ^
2019-04-19 20:01:23 +00:00
Moshibit c37c44d227 Add files via upload 2019-04-19 08:39:03 -05:00
Tom Rothamel 045b6cc227 Log startup timings to the Javascript console. 2019-04-18 22:53:00 -04:00
Moshibit 56f86495fb Add files via upload 2019-04-18 20:56:40 -05:00
Moshibit 576b02d124 Add files via upload 2019-04-17 23:18:42 -05:00
Moshibit b5b7673e8b Add files via upload 2019-04-17 23:05:41 -05:00
Moshibit 455274bbaa Add files via upload 2019-04-17 21:40:04 -05:00
Tom Rothamel 0b492357d9 Fixes to get compiling on PC. 2019-04-17 22:38:59 -04:00
Tom Rothamel fb0149d0dc Merge branch 'web'
Based on @beuc's patches.
2019-04-17 21:58:45 -04:00
Tom Rothamel e7184c3a11 Minor cleanups. 2019-04-17 21:40:08 -04:00
Tom Rothamel 963dba7cc6 Minor cleanups. 2019-04-17 21:36:18 -04:00
Tom Rothamel 087f618326 Revert "web: Remove threading from pgrender.py"
This reverts commit 8dc57697bf.
2019-04-17 21:28:27 -04:00
Tom Rothamel 4c2be41c63 Revert "web: Replace uses of float with double in the gl renderer."
This reverts commit 86bab3977e.
2019-04-17 20:27:36 -04:00
Moshibit d23d5d91e3 Add files via upload 2019-04-17 19:09:34 -05:00
Tom Rothamel 7f8a07a8a5 web: Ifdef changes to renpysound_core.c 2019-04-17 02:38:53 -04:00
Tom Rothamel 173eaf6029 web: Use ifdefs to separate emscripten code in ffmpeg. 2019-04-17 01:07:17 -04:00
Tom Rothamel 8d0830f66f web: Remove threading in ffmedia.c
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-17 00:07:52 -04:00
Moshibit 584f7a0be7 Add files via upload 2019-04-16 21:50:40 -05:00
Tom Rothamel e210147343 Only force a checkpoint if one hasn't been forced already.
This improves things because forced checkpoints would prevent
rollforward data from being saved.
2019-04-16 01:35:59 -04:00
Tom Rothamel e4b33e595a Force a checkpoint after a print statement occurs.
This prevents the console from rolling back past the python block
it's in.
2019-04-15 23:05:56 -04:00
Tom Rothamel 53d08e0f2d Threadless image prediciton. 2019-04-15 01:50:16 -04:00
Tom Rothamel 84c53d3d9c Revert "web: Remove threading in savelocation.py (and a call to renpy.sync?)"
This reverts commit fe2ccc43e2.
2019-04-15 01:50:16 -04:00
Tom Rothamel 2962b5c398 Revert "web: Remove threading in main.py."
This reverts commit 030d9d2dbf.
2019-04-15 01:50:16 -04:00
Tom Rothamel 0ebfae81c9 Revert some changes from loadsave.py. 2019-04-15 01:50:16 -04:00
Tom Rothamel 5dc0372228 Revert "web: Remove threading from render.pyx"
This reverts commit 02ded280af.
2019-04-15 01:50:16 -04:00
Tom Rothamel 82780c57ee Revert "web: Remove threading from presplash."
This reverts commit d6ba807878.
2019-04-15 01:50:16 -04:00
Tom Rothamel 305215823d Revert "web: Remove threading from im.py"
This reverts commit 3c12d7ddb5.
2019-04-15 01:50:16 -04:00
Tom Rothamel 38440bf843 Do not take background screenshots when in emscripten. 2019-04-15 01:50:16 -04:00
Tom Rothamel 3596a59a54 Revert "web: Remove threading in core.py"
This reverts commit 626a5b03a1.
2019-04-15 01:50:16 -04:00
Tom Rothamel 5aaf1f8ef4 Revert "web: Remove threading in debug.py"
This reverts commit 7da19e215f.
2019-04-15 01:50:16 -04:00
Tom Rothamel 398c8a3053 Revert "web: Remove threading in audio.py"
This reverts commit 7a11cb35eb.
2019-04-15 01:50:16 -04:00
Tom Rothamel cfa560d8fa Revert "web: Patch out use of the subprocess module."
This reverts commit e4aa46e7fe.
2019-04-15 01:50:16 -04:00
Tom Rothamel 46fe772cc5 Revert "web: Remove threading from loader.py,"
This reverts commit c0281a41de.
2019-04-15 01:50:16 -04:00
Moshibit 9149a5c941 Add files via upload 2019-04-15 00:45:16 -05:00
Moshibit ce50d6e3f1 Add files via upload 2019-04-15 00:09:51 -05:00
Moshibit f015cda018 Add files via upload 2019-04-15 00:02:19 -05:00
Sylvain Beucler 069ac8e6c3 web: audio: use larger buffer/latency to work-around sound jitter 2019-04-14 23:19:27 +02:00
Moshibit e2ad4d5a3c Add files via upload 2019-04-14 00:08:22 -05:00
Moshibit 3239f0d5bb Add files via upload 2019-04-14 00:04:54 -05:00
Moshibit 7ac80c1a4f Add files via upload 2019-04-14 00:04:17 -05:00
Tom Rothamel eda749e44a Avoid running expressions with side effects when predicting.
See https://twitter.com/SakuraRiver/status/1117201035217358848 .
2019-04-13 21:34:37 -04:00
Moshibit 5da79e86ae Add files via upload 2019-04-13 20:12:14 -05:00
Tom Rothamel dc73065241 Web packaging changes. 2019-04-12 23:59:26 -04:00
Tom Rothamel 55992074c1 Allow building when the .exe files are missing. 2019-04-12 23:57:22 -04:00
Moshibit ba79f7986c Add files via upload 2019-04-12 14:17:21 -05:00
Tom Rothamel 5a0e3da293 sl2: Fix regression in 49c92600bf.
Layouts weren't being parsed in layout mode, which lead to all
sorts of problems.
2019-04-12 09:57:03 -04:00
Tom Rothamel d7a2bd7bdf Add config.load_failed_label.
This is a label that is jumped if the load fails, to give the game
a chance at recovering.

Fixes #1792.
2019-04-11 20:15:57 -04:00
Moshibit aa43d1828a Add files via upload 2019-04-11 17:31:14 -05:00
Moshibit 21d90dbd55 Add files via upload 2019-04-10 23:18:22 -05:00
Tom Rothamel 4b8026fdd7 Use RENPY_STATIC to control the static build.
This lets us enable and disable static building on a per-platform
basis.
2019-04-10 22:54:27 -04:00
Tom Rothamel 73b8629724 Compat last. 2019-04-10 10:17:04 -04:00
Tom Rothamel 79f0037afc sl2: Report when a non-constant keyword argument follows a python statement.
Fixes #1791.
2019-04-10 09:39:37 -04:00
Tom Rothamel 49c92600bf sl2: Parsing can reject keyword arguments. 2019-04-10 02:59:24 -04:00
Moshibit f594f51ada Add files via upload 2019-04-10 01:22:58 -05:00
Tom Rothamel 254c5b6a2a Make it possible to determine if an SL2 statement supports python. 2019-04-09 20:40:51 -04:00
Tom Rothamel 7568ae3146 Document the supported Python version.
Fixes #1789.
2019-04-08 21:35:41 -04:00
Moshibit 1d7f7353c8 Add files via upload 2019-04-08 15:42:18 -05:00
Moshibit ad4756039d Add files via upload 2019-04-07 16:00:10 -05:00
Tom Rothamel dc30cc39aa Add config.notify. 2019-04-07 10:05:04 -04:00
Tom Rothamel 2720ab3615 Add compatibility for the change in #1774. 2019-04-06 23:08:16 -04:00
Tom Rothamel d7a8c5fb96 Merge pull request #1774 from mal/extend-say-attribute-trans-callback
Extend say_attribute_transition_callback with canonical tag list
2019-04-06 22:45:35 -04:00
Tom Rothamel 49baa6e6cb Script to upload to itch.io. 2019-04-06 04:27:01 -04:00
Tom Rothamel b5abd9f4c4 Merge pull request #1785 from kobaltcore/bmfont-kerning-support
Add ability to parse kerning messages of BMFont fonts
2019-04-04 17:55:44 -04:00
Tom Rothamel 98d104b9ec Shortcut creating vc_version.py for build processes. 2019-04-03 01:32:49 -04:00
Tom Rothamel f5e001ac99 web: Mitigate busy script warnings from the browser.
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-02 02:03:39 -04:00
Tom Rothamel fe2ccc43e2 web: Remove threading in savelocation.py (and a call to renpy.sync?)
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-02 02:03:39 -04:00
Tom Rothamel 030d9d2dbf web: Remove threading in main.py.
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-02 02:03:39 -04:00
Tom Rothamel 011c3147dc web: Remove threading from loadsave.py,
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-02 02:03:39 -04:00
Tom Rothamel c0281a41de web: Remove threading from loader.py,
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-02 02:03:39 -04:00
Tom Rothamel 02ded280af web: Remove threading from render.pyx
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-02 02:03:39 -04:00
Tom Rothamel d6ba807878 web: Remove threading from presplash.
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-02 02:03:39 -04:00
Tom Rothamel 8dc57697bf web: Remove threading from pgrender.py
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-02 02:03:39 -04:00
Tom Rothamel 3c12d7ddb5 web: Remove threading from im.py
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-02 02:03:39 -04:00
Tom Rothamel c851968def web: Download a screenshot once it's been taken.
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-02 02:03:39 -04:00
Tom Rothamel 626a5b03a1 web: Remove threading in core.py
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-02 02:03:39 -04:00
Tom Rothamel 7da19e215f web: Remove threading in debug.py
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-02 02:03:39 -04:00
Tom Rothamel 7a11cb35eb web: Remove threading in audio.py
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-02 02:03:39 -04:00
Tom Rothamel d2981c59cd web: Remove threading in renpysound_core.c
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-02 02:03:39 -04:00
Tom Rothamel e4aa46e7fe web: Patch out use of the subprocess module.
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-02 00:07:48 -04:00
Tom Rothamel c56aea9dc8 web: Fix up source for static loading.
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-02 00:06:40 -04:00
Tom Rothamel 86bab3977e web: Replace uses of float with double in the gl renderer.
Originally-by: Sylvain Beucler <beuc@beuc.net>
2019-04-02 00:03:14 -04:00
Tom Rothamel 2b03eb6c39 Add the ability to build a web package. 2019-04-01 23:04:11 -04:00
Tom Rothamel 7f33118758 Bump version. 2019-04-01 19:25:56 -04:00
Tom Rothamel 1bea3fc006 Update changelog and credits, fix docs. 2019-03-31 10:37:27 -04:00
Tom Rothamel 9608615542 android: Set the searchpath _after_ clearing it. 2019-03-30 19:44:09 -04:00
Tom Rothamel 9ddff5d7a5 android: Prioritize files on disk over files in the APK. 2019-03-30 19:41:41 -04:00
Tom Rothamel e623134867 Predict the proper image in resolve_say_attributes.
Fixes #1175.
2019-03-30 14:30:57 -04:00
Tom Rothamel 77e428c9a1 Include temporary attributes when generating translations.
Fixes #1784.
2019-03-30 14:22:38 -04:00
Tom Rothamel 03c951c4a4 itch: Download butler and use the downloaded copy.
Fixes #1782.
2019-03-30 13:26:46 -04:00
Tom Rothamel d9fb4a4234 Update sponsors doc. 2019-03-29 23:29:43 -04:00
Tom Rothamel 571aff3eb1 Update changelog. 2019-03-29 22:20:37 -04:00
Moshibit 7b1e96300f Add files via upload 2019-03-29 15:16:03 -06:00
kobaltcore 0f00705da7 Add ability to parse kerning messages of BMFont fonts
This allows the BMFont object to understand "kerning" lines embedded within BMFont definitions.
The kerning information is added to the existing "self.kerns" property.
2019-03-29 18:14:40 +01:00
Tom Rothamel 9d9f2c2a75 Correct the priority order of crop vs (corner1, corner2). 2019-03-28 23:23:37 -04:00
Tom Rothamel ce2fa71356 Adjust the accessibility message. 2019-03-28 18:55:09 -04:00
Tom Rothamel e315006136 Add the accessibility menu. 2019-03-27 21:15:41 -04:00
Tom Rothamel a2bf4f3452 Resize a side if the displayabls that comprise it change size. 2019-03-27 21:13:57 -04:00
Tom Rothamel f9a2d1153a Add accessability preferences. 2019-03-27 00:44:41 -04:00
Tom Rothamel 33f9cef572 renpy.free_memory frees fonts.
As intended.
2019-03-26 22:36:47 -04:00
Tom Rothamel c3a1c8d7ff Add bars to error handling. 2019-03-25 22:29:05 -04:00
Tom Rothamel 565956e966 Allow bar values to call an action when they change something. 2019-03-25 22:12:44 -04:00
Tom Rothamel 4eb84611fe More work on the accessibility menu. 2019-03-24 13:16:29 -04:00
Moshibit 69f3c01442 Add files via upload 2019-03-24 11:12:07 -06:00
Moshibit f205b611b5 Add files via upload 2019-03-24 10:53:24 -06:00
Tom Rothamel fad22b3631 Start work on the accessibility screen. 2019-03-24 11:36:02 -04:00
Tom Rothamel 176309152f Shrink _label_text in most places. 2019-03-24 11:32:42 -04:00
Tom Rothamel 1bf0e75bc1 Add preferences to adjust ftfont size and spacing. 2019-03-23 22:53:39 -04:00
Moshibit 8266095feb Add files via upload 2019-03-23 15:14:20 -06:00
Moshibit e2843c06ba Add files via upload 2019-03-23 12:26:37 -06:00
Tom Rothamel 28d5f5fc39 Fix issue with #1780. 2019-03-22 18:24:07 -04:00
Tom Rothamel a6b21ec78f Merge pull request #1781 from Andykl/fix-lexer-eob
Perform re-create Lexer.eob on revert
2019-03-21 22:25:35 -04:00
Andy_kl 62bac5aa0e Performed re-create Lexer.eob on revert 2019-03-21 19:03:41 +03:00
Tom Rothamel 843b10851f Fix bad .pop() in #1780. 2019-03-20 23:21:48 -04:00
Tom Rothamel c5f15b5c6a Merge pull request #1780 from Meithal/clean
GC optimizations
2019-03-20 23:20:31 -04:00
Meithal ebd8baba00 GC optimizations 2019-03-21 03:58:30 +01:00
Tom Rothamel 464ae528b1 Dejavusans font transform. 2019-03-20 02:04:08 -04:00
Tom Rothamel 884de6f4c3 Add additonal font scaling options.
There is a way to scale the size per font file, and a way to scale the
vertical extent by font file. The main reason for these is to provide
a way to rescale opendyslexic to fit in the same space as DejaVuSans.
2019-03-19 00:50:19 -04:00
Tom Rothamel 414b1a36c7 Add OpenDyslexic files. 2019-03-18 23:58:18 -04:00
Tom Rothamel 8b1168b621 Add font transform support.
This is an accesibility function that can turn one font into
another font, or a font group. It's currently being used to
enable OpenDyslexic when it's needed.
2019-03-18 23:51:33 -04:00
Tom Rothamel 8526cc1ba3 Rewrite FontGroup.
It's now O(1) in operation, at the cost of increasing the size of the
FontGroup. There's also the option to specify a default font, and
it's possible to add a FontGroup to a FontGroup.
2019-03-18 22:43:19 -04:00
Moshibit 5870b6cd1f Add files via upload 2019-03-17 21:21:18 -06:00
Tom Rothamel 7b2f7d7e85 Set steam=None if steam fails to initialize.
This fixes #1772.
2019-03-17 18:31:32 -04:00
Tom Rothamel cbff654273 Bump version. 2019-03-17 17:36:02 -04:00
Tom Rothamel 7cad0e9e06 Fix typo in changelog. 2019-03-17 13:44:10 -04:00
Mal Graty a7471bba03 Extend say_attribute_transition_callback with tags
This provides context to the callback about the image it's affecting
with the transition. This can be used in isolation, or with the current
state to intelligently utilise a transition based upon the
tags/attributes of the image being displayed.
2019-03-17 15:40:33 +00:00
Moshibit 44c11a7803 Add files via upload 2019-03-16 20:31:21 -06:00
Tom Rothamel 210935c4c6 Fix docs. 2019-03-16 21:32:05 -04:00
Tom Rothamel 412cbda546 Update credits and sponsors. 2019-03-16 21:04:39 -04:00
Tom Rothamel 553e80d12e Update changelog. 2019-03-16 20:49:58 -04:00
Tom Rothamel 5dd3af6a0a console: Properly parse jump command.
Fixes #1767.
2019-03-16 20:23:36 -04:00
Tom Rothamel 879d97a93b sl2: Propagate transform state to the right place.
Fixes #1769.
2019-03-16 11:10:55 -04:00
Tom Rothamel c5325bb61e Arguments to extend override older arguments.
Fixed #1768.
2019-03-15 22:32:08 -04:00
Tom Rothamel 5a708e86d0 Ensure a __ne__ method exists for each __eq__ method. 2019-03-14 20:54:29 -04:00
Tom Rothamel 3a52fd76e5 Merge pull request #1766 from Enerccio/rpe-enviroment-loading
added RENPY_SEARCHPATH environment variable
2019-03-14 00:03:28 -04:00
Tom Rothamel 4e24239322 Document iOS image files. 2019-03-14 00:02:18 -04:00
Tom Rothamel d2bdc159bd Ignore iOS files. 2019-03-13 22:49:03 -04:00
Tom Rothamel 49e804eb50 Update the changelog. 2019-03-13 22:43:06 -04:00
Tom Rothamel 209564d2d2 Set _voice.auto_filename.
Fixes #1765.
2019-03-13 21:09:10 -04:00
Tom Rothamel 56b42f7f8a Remain in rollback until an interaction occurs.
This makes it more reliable to detect rollbacks and loads, and avoid
triggering code when they happen.
2019-03-13 20:27:25 -04:00
Enerccio fb1a01c37f added RENPY_SEARCHPATH environment variable 2019-03-13 18:24:46 +01:00
Tom Rothamel 1d1c53e2d5 Generate iOS images when needed. 2019-03-12 21:23:37 -04:00
Tom Rothamel 0604e61151 Add iOS icons. 2019-03-12 21:16:43 -04:00
Moshibit 19f69717de Add files via upload 2019-03-12 11:50:18 -06:00
Tom Rothamel f8232ed24a Doc config.say_attribute_transition_callback. 2019-03-11 02:02:18 -04:00
Tom Rothamel efafa4e9e6 Add config.say_attribute_transition_callback.
This is a callback that returns the say attribute transition to use,
allowing it to be changed in more complex ways.
2019-03-10 22:46:59 -04:00
Moshibit 5a7856cb26 Add files via upload 2019-03-10 10:48:41 -06:00
Tom Rothamel 48130761d7 Open save_dump.txt in the same location as other error files.
This prevents a crash on mac when it's opened in /.
2019-03-10 03:02:01 -04:00
Tom Rothamel 95daa0251f Properly quote % in profiling data. 2019-03-09 22:42:05 -05:00
Tom Rothamel 31c3efa9bc Prevent logging from crashing Ren'Py if a spurious % appears. 2019-03-09 21:21:16 -05:00
Tom Rothamel 34875f0011 Pass names and versions when creating iOS project. 2019-03-09 15:12:59 -05:00
Moshibit 78c61e45c5 Add files via upload 2019-03-09 09:06:25 -06:00
Tom Rothamel 4f78eeed28 DynamicImages that take a scope should consider that in equality.
That's because when a scope is involved, it's possible for two
DynamicImages with the same name to be different, even if the
name is the same.

This can cause problems with displayable reuse in screens.

Fixes #1759.
2019-03-08 20:19:20 -05:00
Tom Rothamel 8b80e916a7 Changelog. 2019-03-07 09:21:46 -05:00
Tom Rothamel b810238038 Merge pull request #1758 from Moshibit/patch-4
correct button
2019-03-07 08:45:43 -05:00
Tom Rothamel 7808826773 Fix roll-forward not working through menus.
Fixes #1754.
2019-03-06 23:31:46 -05:00
Moshibit 782f89510b Add files via upload 2019-03-06 10:35:27 -06:00
Moshibit ec8147408b correct button 2019-03-06 09:22:44 -06:00
Tom Rothamel 92d89ea508 Merge pull request #1756 from fchapoton/patch-1
fix an illegal raise False
2019-03-05 18:08:16 -05:00
Tom Rothamel 1ebae688db Merge pull request #1757 from Moshibit/patch-3
correct buttons
2019-03-05 18:07:50 -05:00
Moshibit 927370ebfc Add files via upload 2019-03-05 14:14:27 -06:00
Moshibit 7afe53fb81 correct buttons 2019-03-05 13:40:31 -06:00
Moshibit 9eb1c06659 Add files via upload 2019-03-05 13:19:32 -06:00
Frédéric Chapoton f6e7f67faa fix an illegal raise False
changed to return False

found by https://lgtm.com/projects/g/renpy/renpy/
2019-03-05 19:48:10 +01:00
Tom Rothamel f96d677d4a Bump version to 7.2.1. 2019-03-04 19:15:11 -05:00
Tom Rothamel dd115f186f Remove spurious character. 2019-03-04 19:14:26 -05:00
Tom Rothamel 344079de86 Fix changelog. 2019-03-03 21:16:41 -05:00
Tom Rothamel 47c53e90b4 Force begin rollback at menu and call screen.
This ensures that saves will load at those statements, even in
the presence of fast skipping.

Fixes #1751.
2019-03-03 12:19:45 -05:00
Tom Rothamel bd07fa6abb Defer SHOW->UPDATE until the screen has been drawn.
Calls to renpy.restart_interaction were causing the screen to
restart early, which means that the show events never were
dispatched, and the update event would overwrite it.

Fixes #1753.
2019-03-03 11:15:22 -05:00
Moshibit 8f1d3636d5 Merge pull request #4 from Moshibit/Moshibit-patch-3
Add files via upload
2019-03-03 09:30:21 -06:00
Moshibit 07886362c5 Add files via upload 2019-03-03 09:29:31 -06:00
Moshibit 85c58d5220 Add files via upload 2019-03-03 09:20:46 -06:00
Tom Rothamel 203fdbcc84 Fix User Interface Statement documentation.
Fixes #1752.
2019-03-03 00:33:33 -05:00
Tom Rothamel a47e9ae043 Round .subsurface arguments to integers.
This is more correct, and fixes a bug where inaccuracy could cause
is_pixel_opaque to fail because the 1x1 it wants was rounded down
to 0x1.
2019-03-02 23:21:22 -05:00
Tom Rothamel 6c09b387a3 Document lint functions as functions.
Rather than in the .rst file.
2019-03-01 21:53:28 -05:00
Tom Rothamel 8969d18c91 Merge pull request #1747 from Andykl/CDSfixs
Documentation corrections
2019-03-01 20:13:33 -05:00
Tom Rothamel 828bc67064 Merge pull request #1750 from kevinturner/patch-2
Remove MPEG-1 from renpy.movie_cutscene()
2019-03-01 19:47:16 -05:00
Kevin Turner de01c76882 Remove MPEG-1 from renpy.movie_cutscene()
renpy.movie_cutscene() can play any Ren'Py compatible movie file, but the documentation had not been updated to reflect this.
2019-02-28 01:47:20 -05:00
Andy_kl 7be9a851c3 Added docs for lint functions 2019-02-27 22:52:03 +03:00
Andy_kl 38e83e3401 Improved CDS description 2019-02-27 19:14:28 +03:00
Andy_kl bcda13f128 Added documentation for simple_expression and delimited_python 2019-02-27 17:12:30 +03:00
Andy_kl 121ba27204 Documentation for some of the Lexer methods. 2019-02-27 02:05:41 +03:00
Andy_kl 572c915b06 Added missing "expression" 2019-02-26 16:48:16 +03:00
Andy_kl 520a4016db Small fix 2019-02-26 16:25:58 +03:00
Andy_kl 16b85e42ac Typos fixed 2019-02-26 16:12:20 +03:00
118 changed files with 10980 additions and 465 deletions
+4
View File
@@ -54,6 +54,7 @@ def main():
ap.add_argument("--variant", action="store")
ap.add_argument("--sign", action="store_true", default=True)
ap.add_argument("--nosign", action="store_false", dest="sign")
ap.add_argument("--vc-version-only", action="store_true")
args = ap.parse_args()
@@ -86,6 +87,9 @@ def main():
with open("renpy/vc_version.py", "w") as f:
f.write("vc_version = {}".format(vc_version))
if args.vc_version_only:
return
try:
reload(sys.modules['renpy.vc_version']) # @UndefinedVariable
except:
+2 -2
View File
@@ -843,7 +843,7 @@ style radio_vbox:
style radio_button:
properties gui.button_properties("radio_button")
foreground "gui/button/check_[prefix_]foreground.png"
foreground "gui/button/radio_[prefix_]foreground.png"
style radio_button_text:
properties gui.button_text_properties("radio_button")
@@ -1441,7 +1441,7 @@ style window:
style radio_button:
variant "small"
foreground "gui/phone/button/check_[prefix_]foreground.png"
foreground "gui/phone/button/radio_[prefix_]foreground.png"
style check_button:
variant "small"
+20 -6
View File
@@ -373,7 +373,7 @@ init python in distribute:
This manages the process of building distributions.
"""
def __init__(self, project, destination=None, reporter=None, packages=None, build_update=True, open_directory=False, noarchive=False, packagedest=None, report_success=True):
def __init__(self, project, destination=None, reporter=None, packages=None, build_update=True, open_directory=False, noarchive=False, packagedest=None, report_success=True, scan=True):
"""
Distributes `project`.
@@ -441,8 +441,9 @@ init python in distribute:
# dictionaries.
data = project.data
self.reporter.info(_("Scanning project files..."))
project.update_dump(force=True, gui=False, compile=project.data['force_recompile'])
if scan:
self.reporter.info(_("Scanning project files..."))
project.update_dump(force=True, gui=False, compile=project.data['force_recompile'])
if project.dump.get("error", False):
raise Exception("Could not get build data from the project. Please ensure the project runs.")
@@ -523,6 +524,10 @@ init python in distribute:
# Build the mac app and windows exes.
self.add_mac_files()
self.add_windows_files()
self.add_main_py()
# Add the main.py.
self.add_main_py()
# Add generated/special files.
if build['renpy']:
@@ -805,6 +810,7 @@ init python in distribute:
self.add_file_list_hash("rapt")
self.add_file_list_hash("renios")
self.add_file_list_hash("web")
tmp_fn = self.temp_filename("renpy.py")
@@ -965,7 +971,7 @@ init python in distribute:
old_exe_fn = os.path.join(config.renpy_base, "renpy.exe")
old_main_fn = os.path.join(config.renpy_base, "lib/windows-i686/renpy.exe")
if os.path.exists(icon_fn):
if os.path.exists(icon_fn) and os.path.exists(old_exe_fn):
exe_fn = self.temp_filename("renpy.exe")
main_fn = self.temp_filename("main.exe")
@@ -979,8 +985,16 @@ init python in distribute:
exe_fn = old_exe_fn
main_fn = old_main_fn
self.add_file(windows, self.exe, exe_fn)
self.add_file(windows, "lib/windows-i686/" + self.exe, main_fn)
if os.path.exists(exe_fn):
self.add_file(windows, self.exe, exe_fn)
self.add_file(windows, "lib/windows-i686/" + self.exe, main_fn)
def add_main_py(self):
if self.build['renpy']:
return
self.add_file("web", "main.py", os.path.join(config.renpy_base, "renpy.py"))
def mark_executable(self):
"""
+1
View File
@@ -249,6 +249,7 @@ screen front_page_project:
textbutton _("Android") action Jump("android")
textbutton _("iOS") action Jump("ios")
textbutton _("Web") + " " + _("(Beta)") action Jump("web")
textbutton _("Generate Translations") action Jump("translate")
textbutton _("Extract Dialogue") action Jump("extract_dialogue")
+15 -1
View File
@@ -58,6 +58,7 @@ init python:
if RENIOS_PATH:
import renios.create
import renios.image
def IOSState():
if not RENIOS_PATH:
@@ -119,6 +120,10 @@ init python:
return os.path.join(persistent.xcode_projects_directory, xcode_name(p.name))
def ios_create(p=None, gui=True):
project.current.update_dump(force=True)
name = project.current.dump.get("name", None)
version = project.current.dump.get("version", None)
dest = xcode_project(p)
@@ -135,7 +140,7 @@ init python:
os.rename(dest, backup)
iface = MobileInterface("ios")
renios.create.create_project(iface, dest)
renios.create.create_project(iface, dest, name, version)
ios_populate(p, gui=gui)
@@ -187,6 +192,15 @@ init python:
os.unlink(py_fn)
ios_image(p, "ios-icon.png", "Media.xcassets/AppIcon.appiconset", True)
ios_image(p, "ios-launchimage.png", "Media.xcassets/LaunchImage.launchimage", False)
def ios_image(p, source, destination, scale):
source = os.path.join(p.path, source)
destination = os.path.join(xcode_project(p), destination)
renios.image.generate(source, destination, scale)
def launch_xcode():
dist = xcode_project(None)
+48 -14
View File
@@ -23,23 +23,57 @@ init python:
def find_itch_butler():
import os
if renpy.windows:
rv = os.path.join(os.environ.get("APPDATA", ""), "Roaming", "itch", "bin", "butler.exe")
if not os.path.exists(rv):
rv = os.path.join(os.environ.get("APPDATA", ""), "itch", "bin", "butler.exe")
platform = "windows-amd64"
exe = "butler.exe"
elif renpy.macintosh:
rv = os.path.join(os.environ.get("HOME", ""), "Library", "Application Support", "itch", "bin", "butler")
plaform = "darwin-amd64"
exe = "butler"
else:
rv = os.path.join(os.environ.get("HOME", ""), ".config", "itch", "bin", "butler")
platform = "linux-amd64"
exe = "butler"
if not os.path.exists(rv):
return None
dn = os.path.join(config.renpy_base, "tmp", "butler-" + platform)
zip = os.path.join(dn, "butler.zip")
exe = os.path.join(dn, exe)
if os.path.exists(exe):
return exe
interface.processing(_("Downloading the itch.io butler."))
try:
os.makedirs(dn)
except:
pass
import urllib2
import ssl
with interface.error_handling("Downloading the itch.io butler."):
context = ssl._create_unverified_context()
response = urllib2.urlopen("https://broth.itch.ovh/butler/{}/LATEST/archive/default".format(platform), context=context)
with open(zip, "wb") as f:
while True:
data = response.read(1024 * 1024)
if not data:
break
f.write(data)
import zipfile
with zipfile.ZipFile(zip) as zf:
zf.extractall(dn)
try:
os.chmod(exe, 0o755)
except:
pass
return exe
return rv
label itch:
@@ -48,6 +82,8 @@ label itch:
python hide:
butler = find_itch_butler()
build = project.current.dump["build"]
destination = build["destination"]
@@ -100,8 +136,6 @@ label itch:
label="build_distributions"
)
butler = find_itch_butler()
if not butler:
interface.error(
_("The butler program was not found."),
+1 -1
View File
@@ -3744,7 +3744,7 @@ class PE:
offset = self.get_physical_by_rva(rva)
if not offset:
raise False
return False
return self.set_bytes_at_offset(offset, data)
+2 -2
View File
@@ -109,10 +109,10 @@ init 1 python:
SEPARATOR = Frame(PATTERN, 0, 0, tile=True, ymaximum=5, yalign=1.0)
SEPARATOR2 = Frame(PATTERN, 0, 0, tile=True, ymaximum=10, yalign=1.0)
SPACER_HEIGHT = 12
SPACER_HEIGHT = 8
SPACER = Null(height=SPACER_HEIGHT)
HALF_SPACER_HEIGHT = 6
HALF_SPACER_HEIGHT = 4
HALF_SPACER = Null(height=HALF_SPACER_HEIGHT)
# DIVIDING THE SCREEN
+88 -1
View File
@@ -647,4 +647,91 @@ translate spanish strings:
# 00gallery.rpy:596
old "return"
new "volver"
new "volver"
translate spanish strings:
# 00accessibility.rpy:76
old "Font Override"
new "Invalidar Fuente"
# 00accessibility.rpy:80
old "Default"
new "por Defecto"
# 00accessibility.rpy:84
old "DejaVu Sans"
new "DejaVu Sans"
# 00accessibility.rpy:88
old "Opendyslexic"
new "Opendyslexic"
# 00accessibility.rpy:94
old "Text Size Scaling"
new "Tamaño de la Escala del Texto"
# 00accessibility.rpy:100
old "Reset"
new "Reiniciar"
# 00accessibility.rpy:105
old "Line Spacing Scaling"
new "Escalado del espaciado de línea"
# 00accessibility.rpy:117
old "Self-Voicing"
new "Voz-automática"
# 00accessibility.rpy:121
old "Off"
new "Apagado"
# 00accessibility.rpy:125
old "Text-to-speech"
new "Texto a voz"
# 00accessibility.rpy:129
old "Clipboard"
new "Potapapeles"
# 00accessibility.rpy:133
old "Debug"
new "Depurar"
# 00action_file.rpy:434
old "Load slot %s: [text]"
new "Cargar ranura %s: [text]"
# 00preferences.rpy:430
old "font transform"
new "transformación de fuente"
# 00preferences.rpy:433
old "font size"
new "tamaño de fuente"
# 00preferences.rpy:441
old "font line spacing"
new "fuente de espacio de líneas"
# _developer\developer.rpym:63
old "Image Attributes"
new "Atrubutos de Imagen"
# _developer\developer.rpym:90
old "[name] [attributes] (hidden)"
new "[name] [attributes] (hidden)"
# _developer\developer.rpym:94
old "[name] [attributes]"
new "[name] [attributes]"
# _developer\developer.rpym:154
old "Hide deleted"
new "Hide deleted"
# _developer\developer.rpym:154
old "Show deleted"
new "Show deleted"
+12 -1
View File
@@ -187,4 +187,15 @@ translate spanish strings:
# 00console.rpy:639
old "jump <label>: jumps to label"
new "jump <label>: salta a la etiqueta"
new "jump <label>: salta a la etiqueta"
translate spanish strings:
# 00console.rpy:680
old "short: Shorten the representation of objects on the console (default)."
new "corto: Acorta la representación de los objetos en la consola (predeterminado)."
# 00console.rpy:685
old "long: Print the full representation of objects on the console."
new "largo: imprime la representación completa de los objetos en la consola."
+28 -1
View File
@@ -211,4 +211,31 @@ translate spanish strings:
# _errorhandling.rpym:667
old "Copies the errors.txt file to the clipboard."
new "Copia el archivo errors.txt al portapapeles."
new "Copia el archivo errors.txt al portapapeles."
translate spanish strings:
# _errorhandling.rpym:542
old "Copy BBCode"
new "Copiar BBCode"
# _errorhandling.rpym:544
old "Copies the traceback.txt file to the clipboard as BBcode for forums like https://lemmasoft.renai.us/."
new "Copia el archivo traceback.txt en el portapapeles como BBcode para foros como https://lemmasoft.renai.us/."
# _errorhandling.rpym:546
old "Copy Markdown"
new "Copiar Markdown"
# _errorhandling.rpym:548
old "Copies the traceback.txt file to the clipboard as Markdown for Discord."
new "Copia el archivo traceback.txt al portapapeles como Markdown paara Discord."
# _errorhandling.rpym:683
old "Copies the errors.txt file to the clipboard as BBcode for forums like https://lemmasoft.renai.us/."
new "Copia el archivo errors.txt en el portapapeles como BBcode para foros como https://lemmasoft.renai.us/."
# _errorhandling.rpym:687
old "Copies the errors.txt file to the clipboard as Markdown for Discord."
new "Copia el archivo errors.txt al portapapeles como Markdown para Discord."
+217
View File
@@ -0,0 +1,217 @@
# Copyright 2004-2019 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.
# This file contains the launcher support for creating RenPyWeb projects,
# other than the logic in the standard distribute code.
init python:
import shutil
import webserver
WEB_PATH = None
def find_web():
global WEB_PATH
candidates = [ ]
WEB_PATH = os.path.join(config.renpy_base, "web")
if os.path.isdir(WEB_PATH) and check_hash_txt("web"):
pass
else:
WEB_PATH = None
find_web()
def get_web_destination(p):
"""
Returns the path to the desint
"""
build = p.dump['build']
base_name = build['directory_name']
destination = build["destination"]
parent = os.path.dirname(p.path)
destination = os.path.join(parent, destination, base_name + "-web")
return destination
def build_web(p, gui=True):
# Figure out the reporter to use.
if gui:
reporter = distribute.GuiReporter()
else:
reporter = distribute.TextReporter()
# Determine details.
p.update_dump(True, gui=gui)
destination = get_web_destination(p)
display_name = p.dump['build']['display_name']
# Clean the folder, then remale it.
if os.path.exists(destination):
shutil.rmtree(destination)
os.makedirs(destination, 0o777)
# Use the distributor to make game.zip.
distribute.Distributor(p, packages=[ "web" ], packagedest=os.path.join(destination, "game"), reporter=reporter, noarchive=True, scan=False)
# Copy the files from WEB_PATH to destination.
for fn in os.listdir(WEB_PATH):
if fn in { "game.zip", "hash.txt", "index.html" }:
continue
shutil.copy(os.path.join(WEB_PATH, fn), os.path.join(destination, fn))
# Copy over index.html.
with open(os.path.join(WEB_PATH, "index.html")) as f:
html = f.read()
html = html.replace("%%TITLE%%", display_name.encode("utf-8"))
with open(os.path.join(destination, "index.html"), "w") as f:
f.write(html)
webserver.start(destination)
def launch_web():
renpy.run(OpenURL("http://127.0.0.1:8042/index.html"))
screen web():
frame:
style_group "l"
style "l_root"
window:
has vbox
label _("Web: [project.current.display_name!q]")
add HALF_SPACER
hbox:
# Left side.
frame:
style "l_indent"
xmaximum ONEHALF
xfill True
has vbox
add SEPARATOR2
add HALF_SPACER
frame:
style "l_indent"
has vbox
text _("Build:")
add HALF_SPACER
frame style "l_indent":
has vbox
textbutton _("Build Web Application") action Jump("web_build")
textbutton _("Build and Open in Browser") action Jump("web_launch")
textbutton _("Open without Build") action Jump("web_start")
add SPACER
text _("Support:")
add HALF_SPACER
frame style "l_indent":
has vbox
textbutton _("RenPyWeb Home") action OpenURL("https://renpy.beuc.net/")
textbutton _("Beuc's Patreon") action OpenURL("https://www.beuc.net/donate/")
# Right side.
frame:
style "l_indent"
xmaximum ONEHALF
xfill True
has vbox
add SEPARATOR2
add HALF_SPACER
frame:
style "l_indent"
has vbox
text _("Ren'Py web applications require the entire game to be downloaded to the player's computer before it can start.")
add SPACER
text _("Current limitations in the web platform mean that loading large images, audio files, or movies may cause audio or framerate glitches, and lower performance in general.")
textbutton _("Return") action Jump("front_page") style "l_left_button"
label web:
if WEB_PATH is None:
$ interface.yesno(_("Before packaging web apps, you'll need to download RenPyWeb, Ren'Py's web support. Would you like to download RenPyWeb now?"), no=Jump("front_page"))
$ add_dlc("web", restart=True)
call screen web
jump front_page
label web_build:
$ build_web(project.current, gui=True)
jump web
label web_launch:
$ build_web(project.current, gui=True)
$ launch_web()
jump web
label web_start:
$ project.current.update_dump(True, gui=True)
$ webserver.start(get_web_destination(project.current))
$ launch_web()
jump web
+253
View File
@@ -0,0 +1,253 @@
import BaseHTTPServer
import threading
import posixpath
import urllib
import os
import os
import posixpath
import BaseHTTPServer
import urllib
import urlparse
import cgi
import sys
import shutil
import mimetypes
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
# The path to the root of the web server.
root = ""
import renpy
class WebHandler(BaseHTTPServer.BaseHTTPRequestHandler):
"""Simple HTTP request handler with GET and HEAD commands.
This serves files from the current directory and any of its
subdirectories. The MIME type for files is determined by
calling the .guess_type() method.
The GET and HEAD requests are identical except that the HEAD
request omits the actual contents of the file.
"""
server_version = "Ren'Py/" + renpy.version_only # @UndefinedVariable
def do_GET(self):
"""Serve a GET request."""
f = self.send_head()
if f:
try:
self.copyfile(f, self.wfile)
finally:
f.close()
def do_HEAD(self):
"""Serve a HEAD request."""
f = self.send_head()
if f:
f.close()
def send_head(self):
"""Common code for GET and HEAD commands.
This sends the response code and MIME headers.
Return value is either a file object (which has to be copied
to the outputfile by the caller unless the command was HEAD,
and must be closed by the caller under all circumstances), or
None, in which case the caller has nothing further to do.
"""
path = self.translate_path(self.path)
f = None
if os.path.isdir(path):
parts = urlparse.urlsplit(self.path)
if not parts.path.endswith('/'):
# redirect browser - doing basically what apache does
self.send_response(301)
new_parts = (parts[0], parts[1], parts[2] + '/',
parts[3], parts[4])
new_url = urlparse.urlunsplit(new_parts)
self.send_header("Location", new_url)
self.end_headers()
return None
for index in "index.html", "index.htm":
index = os.path.join(path, index)
if os.path.exists(index):
path = index
break
else:
return self.list_directory(path)
ctype = self.guess_type(path)
try:
# Always read in binary mode. Opening files in text mode may cause
# newline translations, making the actual size of the content
# transmitted *less* than the content-length!
f = open(path, 'rb')
except IOError:
self.send_error(404, "File not found")
return None
try:
self.send_response(200)
self.send_header("Content-type", ctype)
fs = os.fstat(f.fileno())
# Ren'Py - bust cache.
self.send_header("Pragma", "no-cache")
self.send_header("Content-Length", str(fs[6]))
self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
self.end_headers()
return f
except:
f.close()
raise
def list_directory(self, path):
"""Helper to produce a directory listing (absent index.html).
Return value is either a file object, or None (indicating an
error). In either case, the headers are sent, making the
interface the same as for send_head().
"""
try:
list = os.listdir(path)
except os.error:
self.send_error(404, "No permission to list directory")
return None
list.sort(key=lambda a: a.lower())
f = StringIO()
displaypath = cgi.escape(urllib.unquote(self.path))
f.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
f.write("<html>\n<title>Directory listing for %s</title>\n" % displaypath)
f.write("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath)
f.write("<hr>\n<ul>\n")
for name in list:
fullname = os.path.join(path, name)
displayname = linkname = name
# Append / for directories or @ for symbolic links
if os.path.isdir(fullname):
displayname = name + "/"
linkname = name + "/"
if os.path.islink(fullname):
displayname = name + "@"
# Note: a link to a directory displays with @ and links with /
f.write('<li><a href="%s">%s</a>\n'
% (urllib.quote(linkname), cgi.escape(displayname)))
f.write("</ul>\n<hr>\n</body>\n</html>\n")
length = f.tell()
f.seek(0)
self.send_response(200)
encoding = sys.getfilesystemencoding()
self.send_header("Content-type", "text/html; charset=%s" % encoding)
self.send_header("Content-Length", str(length))
self.end_headers()
return f
def translate_path(self, path):
"""Translate a /-separated PATH to the local filename syntax.
Components that mean special things to the local file system
(e.g. drive or directory names) are ignored. (XXX They should
probably be diagnosed.)
"""
# abandon query parameters
path = path.split('?', 1)[0]
path = path.split('#', 1)[0]
# Don't forget explicit trailing slash when normalizing. Issue17324
trailing_slash = path.rstrip().endswith('/')
path = posixpath.normpath(urllib.unquote(path))
words = path.split('/')
words = filter(None, words)
# Ren'Py - use the root we were given.
path = root
for word in words:
if os.path.dirname(word) or word in (os.curdir, os.pardir):
# Ignore components that are not a simple file/directory name
continue
path = os.path.join(path, word)
if trailing_slash:
path += '/'
return path
def copyfile(self, source, outputfile):
"""Copy all data between two file objects.
The SOURCE argument is a file object open for reading
(or anything with a read() method) and the DESTINATION
argument is a file object open for writing (or
anything with a write() method).
The only reason for overriding this would be to change
the block size or perhaps to replace newlines by CRLF
-- note however that this the default server uses this
to copy binary data as well.
"""
shutil.copyfileobj(source, outputfile)
def guess_type(self, path):
"""Guess the type of a file.
Argument is a PATH (a filename).
Return value is a string of the form type/subtype,
usable for a MIME Content-type header.
The default implementation looks the file's extension
up in the table self.extensions_map, using application/octet-stream
as a default; however it would be permissible (if
slow) to look inside the data to make a better guess.
"""
base, ext = posixpath.splitext(path)
if ext in self.extensions_map:
return self.extensions_map[ext]
ext = ext.lower()
if ext in self.extensions_map:
return self.extensions_map[ext]
else:
return self.extensions_map['']
extensions_map = {
'': 'application/octet-stream', # Default
'.gz': 'application/gzip',
'.htm': 'text/html',
'.html': 'text/html',
'.js': 'application/javascript',
}
def run():
server = BaseHTTPServer.HTTPServer(("127.0.0.1", 8042), WebHandler)
server.serve_forever()
def start(root_):
print("----")
global root
started = root
root = root_
if started:
return
t = threading.Thread(target=run)
t.daemon = True
t.start()
+182 -5
View File
@@ -10,9 +10,18 @@
#if defined(__arm__) && !(__MACOS__ || __IPHONEOS__)
#define USE_MEMALIGN
#include <malloc.h>
#include <stdlib.h>
#endif
/* Should a mono channel be split into two equal stero channels (true) or
* should the energy be split onto two stereo channels with 1/2 the energy
* (false).
*/
static int audio_equal_mono = 1;
/* The weight of stereo channels when audio_equal_mono is true. */
static float stereo_matrix[] = { 1.0, 1.0 };
/* The output audio sample rate. */
static int audio_sample_rate = 44100;
@@ -564,6 +573,10 @@ static void decode_audio(MediaState *ms) {
if (!ms->audio_decode_frame->channel_layout) {
ms->audio_decode_frame->channel_layout = av_get_default_channel_layout(ms->audio_decode_frame->channels);
if (audio_equal_mono && (ms->audio_decode_frame->channels == 1)) {
swr_set_matrix(ms->swr, stereo_matrix, 1);
}
}
converted_frame = av_frame_alloc();
@@ -723,7 +736,7 @@ static SurfaceQueueEntry *decode_video_frame(MediaState *ms) {
#ifdef USE_MEMALIGN
rv->pixels = memalign(16, rv->pitch * rv->h);
posix_memalign(&rv->pixels, 16, rv->pitch * rv->h);
memset(rv->pixels, 0, rv->pitch * rv->h);
#else
rv->pixels = SDL_calloc(1, rv->pitch * rv->h);
@@ -788,10 +801,16 @@ static void decode_video(MediaState *ms) {
}
static int decode_sync_start(void *arg);
void media_read_sync(struct MediaState *ms);
void media_read_sync_finish(struct MediaState *ms);
/**
* Returns 1 if there is a video frame ready on this channel, or 0 otherwise.
*/
int media_video_ready(struct MediaState *ms) {
int consumed = 0;
int rv = 0;
@@ -857,6 +876,7 @@ done:
SDL_Surface *media_read_video(MediaState *ms) {
SDL_Surface *rv = NULL;
SurfaceQueueEntry *sqe = NULL;
@@ -866,9 +886,11 @@ SDL_Surface *media_read_video(MediaState *ms) {
SDL_LockMutex(ms->lock);
#ifndef __EMSCRIPTEN__
while (!ms->ready) {
SDL_CondWait(ms->cond, ms->lock);
SDL_CondWait(ms->cond, ms->lock);
}
#endif
if (!ms->surface_queue_size) {
goto done;
@@ -1042,7 +1064,148 @@ finish:
}
void media_read_sync_finish(struct MediaState *ms) {
// copy/paste from end of decode_thread
/* Data used by the decoder should be freed here, while data shared with
* the readers should be freed in media_close.
*/
SDL_LockMutex(ms->lock);
/* Ensures that every stream becomes ready. */
if (!ms->ready) {
ms->ready = 1;
SDL_CondBroadcast(ms->cond);
}
while (!ms->quit) {
/* SDL_CondWait(ms->cond, ms->lock); */
}
SDL_UnlockMutex(ms->lock);
deallocate(ms);
}
static int decode_sync_start(void *arg) {
// copy/paste from start of decode_thread
MediaState *ms = (MediaState *) arg;
int err;
AVFormatContext *ctx = avformat_alloc_context();
ms->ctx = ctx;
AVIOContext *io_context = rwops_open(ms->rwops);
ctx->pb = io_context;
err = avformat_open_input(&ctx, ms->filename, NULL, NULL);
if (err) {
media_read_sync_finish(ms);
}
err = avformat_find_stream_info(ctx, NULL);
if (err) {
media_read_sync_finish(ms);
}
ms->video_stream = -1;
ms->audio_stream = -1;
for (int i = 0; i < ctx->nb_streams; i++) {
if (ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
if (ms->want_video && ms->video_stream == -1) {
ms->video_stream = i;
}
}
if (ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
if (ms->audio_stream == -1) {
ms->audio_stream = i;
}
}
}
ms->video_context = find_context(ctx, ms->video_stream);
ms->audio_context = find_context(ctx, ms->audio_stream);
ms->swr = swr_alloc();
av_init_packet(&ms->video_pkt);
// Compute the number of samples we need to play back.
if (ms->audio_duration < 0) {
if (av_fmt_ctx_get_duration_estimation_method(ctx) != AVFMT_DURATION_FROM_BITRATE) {
long long duration = ((long long) ctx->duration) * audio_sample_rate;
ms->audio_duration = (unsigned int) (duration / AV_TIME_BASE);
ms->total_duration = 1.0 * ctx->duration / AV_TIME_BASE;
// Check that the duration is reasonable (between 0s and 3600s). If not,
// reject it.
if (ms->audio_duration < 0 || ms->audio_duration > 3600 * audio_sample_rate) {
ms->audio_duration = -1;
}
ms->audio_duration -= (unsigned int) (ms->skip * audio_sample_rate);
} else {
ms->audio_duration = -1;
}
}
if (ms->skip != 0.0) {
av_seek_frame(ctx, -1, (int64_t) (ms->skip * AV_TIME_BASE), AVSEEK_FLAG_BACKWARD);
}
// [snip!]
return 0;
}
void media_read_sync(struct MediaState *ms) {
// copy/paste from middle of decode_thread
// printf("---* media_read_sync %p\n", ms);
//while (!ms->quit) {
if (!ms->quit) {
// printf(" audio_finished: %d, video_finished: %d\n", ms->audio_finished, ms->video_finished);
if (! ms->audio_finished) {
decode_audio(ms);
}
if (! ms->video_finished) {
decode_video(ms);
}
SDL_LockMutex(ms->lock);
if (!ms->ready) {
ms->ready = 1;
SDL_CondBroadcast(ms->cond);
}
if (!(ms->needs_decode || ms->quit)) {
/* SDL_CondWait(ms->cond, ms->lock); */
}
ms->needs_decode = 0;
SDL_UnlockMutex(ms->lock);
}
}
int media_read_audio(struct MediaState *ms, Uint8 *stream, int len) {
#ifdef __EMSCRIPTEN__
media_read_sync(ms);
#endif
SDL_LockMutex(ms->lock);
@@ -1127,6 +1290,7 @@ int media_read_audio(struct MediaState *ms, Uint8 *stream, int len) {
}
void media_wait_ready(struct MediaState *ms) {
#ifndef __EMSCRIPTEN__
SDL_LockMutex(ms->lock);
while (!ms->ready) {
@@ -1134,6 +1298,7 @@ void media_wait_ready(struct MediaState *ms) {
}
SDL_UnlockMutex(ms->lock);
#endif
}
@@ -1142,7 +1307,12 @@ double media_duration(MediaState *ms) {
}
void media_start(MediaState *ms) {
char buf[1024];
#ifdef __EMSCRIPTEN__
decode_sync_start(ms);
#else
char buf[1024];
snprintf(buf, 1024, "decode: %s", ms->filename);
SDL_Thread *t = SDL_CreateThread(decode_thread, buf, (void *) ms);
@@ -1151,6 +1321,7 @@ void media_start(MediaState *ms) {
ms->started = 1;
SDL_DetachThread(t);
}
#endif
}
@@ -1210,6 +1381,11 @@ void media_close(MediaState *ms) {
/* Tell the decoder to terminate. It will deallocate everything for us. */
SDL_LockMutex(ms->lock);
ms->quit = 1;
#ifdef __EMSCRIPTEN__
media_read_sync_finish(ms);
#endif
SDL_CondBroadcast(ms->cond);
SDL_UnlockMutex(ms->lock);
@@ -1224,9 +1400,10 @@ void media_sample_surfaces(SDL_Surface *rgb, SDL_Surface *rgba) {
rgba_surface = rgba;
}
void media_init(int rate, int status) {
void media_init(int rate, int status, int equal_mono) {
audio_sample_rate = rate / SPEED;
audio_equal_mono = equal_mono;
av_register_all();
+33 -18
View File
@@ -31,11 +31,33 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define MAXVOLUME 16384
#ifdef __EMSCRIPTEN__
#define EVAL_LOCK() { }
#define EVAL_UNLOCK() { }
#define BEGIN() { }
#define ENTER() { }
#define EXIT() { }
#define ALTENTER() { }
#define ALTEXIT() { }
#else
#define EVAL_LOCK() { PyEval_AcquireLock(); }
#define EVAL_UNLOCK() { PyEval_ReleaseLock(); }
#define BEGIN() PyThreadState *_save;
#define ENTER() { _save = PyEval_SaveThread(); SDL_LockAudio(); }
#define EXIT() { SDL_UnlockAudio(); PyEval_RestoreThread(_save); }
#define ALTENTER() { _save = PyEval_SaveThread(); }
#define ALTEXIT() { PyEval_RestoreThread(_save); }
#endif
/* Declarations of ffdecode functions. */
struct MediaState;
typedef struct MediaState MediaState;
void media_init(int rate, int status);
void media_init(int rate, int status, int equal_mono);
void media_advance_time(void);
void media_sample_surfaces(SDL_Surface *rgb, SDL_Surface *rgba);
@@ -61,21 +83,21 @@ PyThreadState* thread = NULL;
static void incref(PyObject *ref) {
PyThreadState *oldstate;
PyEval_AcquireLock();
EVAL_LOCK();
oldstate = PyThreadState_Swap(thread);
Py_INCREF(ref);
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
EVAL_UNLOCK();
}
static void decref(PyObject *ref) {
PyThreadState *oldstate;
PyEval_AcquireLock();
EVAL_LOCK();
oldstate = PyThreadState_Swap(thread);
Py_DECREF(ref);
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
EVAL_UNLOCK();
}
/* A mutex that protects the shared data structures. */
@@ -84,17 +106,6 @@ SDL_mutex *name_mutex;
#define LOCK_NAME() { SDL_LockMutex(name_mutex); }
#define UNLOCK_NAME() { SDL_UnlockMutex(name_mutex); }
/* Locking on entry from python... */
// #define BEGIN() PyThreadState *_save;
// #define ENTER() { printf("Locking by %s.\n", __FUNCTION__); _save = PyEval_SaveThread(); SDL_LockAudio(); printf("Lock by %s\n", __FUNCTION__); }
// #define EXIT() { SDL_UnlockAudio(); PyEval_RestoreThread(_save); printf("Release by %s\n", __FUNCTION__); }
#define BEGIN() PyThreadState *_save;
#define ENTER() { _save = PyEval_SaveThread(); SDL_LockAudio(); }
#define EXIT() { SDL_UnlockAudio(); PyEval_RestoreThread(_save); }
#define ALTENTER() { _save = PyEval_SaveThread(); }
#define ALTEXIT() { PyEval_RestoreThread(_save); }
/* Min and Max */
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) > (b)) ? (a) : (b))
@@ -433,6 +444,7 @@ static void pan_audio(struct Channel *c, Uint8 *stream, int length) {
}
static void callback(void *userdata, Uint8 *stream, int length) {
int channel = 0;
memset(stream, 0, length);
@@ -1199,7 +1211,7 @@ void RPS_set_video(int channel, int video) {
* Initializes the sound to the given frequencies, channels, and
* sample buffer size.
*/
void RPS_init(int freq, int stereo, int samples, int status) {
void RPS_init(int freq, int stereo, int samples, int status, int equal_mono) {
if (initialized) {
return;
@@ -1207,7 +1219,10 @@ void RPS_init(int freq, int stereo, int samples, int status) {
name_mutex = SDL_CreateMutex();
#ifndef __EMSCRIPTEN__
PyEval_InitThreads();
#endif
import_pygame_sdl2();
if (!thread) {
@@ -1238,7 +1253,7 @@ void RPS_init(int freq, int stereo, int samples, int status) {
return;
}
media_init(audio_spec.freq, status);
media_init(audio_spec.freq, status, equal_mono);
SDL_PauseAudio(0);
+1 -1
View File
@@ -50,7 +50,7 @@ PyObject *RPS_read_video(int channel);
void RPS_sample_surfaces(PyObject *rgb, PyObject *rgba);
void RPS_set_video(int channel, int video);
void RPS_init(int freq, int stereo, int samples, int status);
void RPS_init(int freq, int stereo, int samples, int status, int equal_mono);
void RPS_quit(void);
void RPS_advance_time(void);
+20
View File
@@ -45,11 +45,19 @@ emscripten = "RENPY_EMSCRIPTEN" in os.environ
# Is coverage enabled?
coverage = "RENPY_COVERAGE" in os.environ
# Are we doing a static build?
static = "RENPY_STATIC" in os.environ
if coverage:
gen = "gen.coverage"
else:
gen = "gen"
if static:
gen += "-static"
# The cython command.
cython_command = os.environ.get("RENPY_CYTHON", "cython")
@@ -317,6 +325,18 @@ def cython(name, source=[], libs=[], includes=[], compile_if=True, define_macros
"-o",
c_fn])
# Fix-up source for static loading
if static and (len(split_name) > 1):
parent_module = '.'.join(split_name[:-1])
parent_module_identifier = parent_module.replace('.', '_')
with open(c_fn, 'r') as f:
ccode = f.read()
ccode = re.sub('Py_InitModule4\("([^"]+)"', 'Py_InitModule4("'+parent_module+'.\\1"', ccode)
ccode = re.sub('^__Pyx_PyMODINIT_FUNC init', '__Pyx_PyMODINIT_FUNC init'+parent_module_identifier+'_', ccode, 0, re.MULTILINE) # Cython 0.28.2
ccode = re.sub('^PyMODINIT_FUNC init', 'PyMODINIT_FUNC init'+parent_module_identifier+'_', ccode, 0, re.MULTILINE) # Cython 0.25.2
with open(c_fn, 'w') as f:
f.write(ccode)
except subprocess.CalledProcessError as e:
print()
print(str(e))
+15 -2
View File
@@ -40,7 +40,7 @@ except ImportError:
vc_version = 0
# The tuple giving the version number.
version_tuple = (7, 2, 0, vc_version)
version_tuple = (7, 2, 3, vc_version)
# The name of this version.
version_name = "What's on the menu."
@@ -551,6 +551,10 @@ def post_import():
vars(renpy.exports).setdefault(k, v)
def issubmodule(sub, module):
return sub == module or sub.startswith(module + ".")
def reload_all():
"""
Resets all modules to the state they were in right after import_all
@@ -588,10 +592,11 @@ def reload_all():
renpy.display.interface = None
py_compile_cache = renpy.python.py_compile_cache
reload_modules = renpy.config.reload_modules
# Delete the store modules.
for i in sys.modules.keys():
if i.startswith("store") or i == "renpy.store":
if issubmodule(i, "store") or i == "renpy.store":
m = sys.modules[i]
if m is not None:
@@ -599,6 +604,14 @@ def reload_all():
del sys.modules[i]
elif any(issubmodule(i, m) for m in reload_modules):
m = sys.modules[i]
if m is not None:
m.__dict__.clear()
del sys.modules[i]
# Restore the state of all modules from backup.
backup.restore()
+32 -4
View File
@@ -249,6 +249,16 @@ class PyExpr(unicode):
return (unicode(self), self.filename, self.linenumber) # E1101
def probably_side_effect_free(expr):
"""
Returns true if an expr probably does not have side effects, and should
be predicted. Basically, this just whitelists a set of characters that
doesn't allow for a function call.
"""
return not ("(" in expr)
class PyCode(object):
__slots__ = [
@@ -629,6 +639,10 @@ class Say(Node):
if self.attributes is not None:
rv.extend(self.attributes)
if self.temporary_attributes:
rv.append("@")
rv.extend(self.temporary_attributes)
what = self.what
if dialogue_filter is not None:
what = dialogue_filter(what)
@@ -1432,6 +1446,10 @@ class Call(Node):
label = self.label
if self.expression:
if not probably_side_effect_free(label):
return [ ]
label = renpy.python.py_eval(label)
return [ renpy.game.context().predict_call(label, self.next.name) ]
@@ -1498,6 +1516,7 @@ class Return(Node):
class Menu(Node):
translation_relevant = True
rollback = "force"
__slots__ = [
'items',
@@ -1677,10 +1696,16 @@ class Jump(Node):
def predict(self):
label = self.target
if self.expression:
return [ ]
else:
return [ renpy.game.script.lookup(self.target) ]
if not probably_side_effect_free(label):
return [ ]
label = renpy.python.py_eval(label)
return [ renpy.game.script.lookup(label) ]
def scry(self):
rv = Node.scry(self)
@@ -1827,6 +1852,7 @@ class UserStatement(Node):
'translatable',
'code_block',
'translation_relevant',
'rollback',
]
def __new__(cls, *args, **kwargs):
@@ -1835,6 +1861,7 @@ class UserStatement(Node):
self.code_block = None
self.translatable = False
self.translation_relevant = False
self.rollback = "normal"
return self
def __init__(self, loc, line, block):
@@ -1846,6 +1873,7 @@ class UserStatement(Node):
self.parsed = None
self.name = self.call("label")
self.rollback = renpy.statements.get("rollback", self.parsed) or "normal"
# Do not store the parse.
self.parsed = None
@@ -1976,7 +2004,7 @@ def get_namespace(store):
# Config variables that are set twice - once when the rpy is first loaded,
# and then again at init time.
EARLY_CONFIG = { "save_directory", "allow_duplicate_labels" }
EARLY_CONFIG = { "save_directory", "allow_duplicate_labels", "keyword_after_python" }
define_statements = [ ]
+3
View File
@@ -251,6 +251,9 @@ class Context(object):
return self.context == other.context
def __ne__(self, other):
return not (self == other)
# 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.
+5 -1
View File
@@ -811,12 +811,16 @@ def init():
if pcm_ok is None and renpysound:
bufsize = 2048
if renpy.emscripten:
# Large buffer (and latency) as compromise to avoid sound jittering
bufsize = 8192 # works for me
#bufsize = 16384 # jitter/silence right after starting a sound
if 'RENPY_SOUND_BUFSIZE' in os.environ:
bufsize = int(os.environ['RENPY_SOUND_BUFSIZE'])
try:
renpysound.init(renpy.config.sound_sample_rate, 2, bufsize, False)
renpysound.init(renpy.config.sound_sample_rate, 2, bufsize, False, renpy.config.equal_mono)
pcm_ok = True
except:
if renpy.config.debug_sound:
+3 -3
View File
@@ -77,7 +77,7 @@ cdef extern from "renpysound_core.h":
void RPS_set_video(int channel, int video)
void RPS_sample_surfaces(object, object)
void RPS_init(int freq, int stereo, int samples, int status)
void RPS_init(int freq, int stereo, int samples, int status, int equal_mono)
void RPS_quit()
void RPS_periodic()
@@ -354,7 +354,7 @@ def set_video(channel, video):
else:
RPS_set_video(channel, NO_VIDEO)
def init(freq, stereo, samples, status=False):
def init(freq, stereo, samples, status=False, equal_mono=False):
"""
Initializes the audio system with the given parameters. The parameter are
just informational - the audio system should be able to play all supported
@@ -379,7 +379,7 @@ def init(freq, stereo, samples, status=False):
else:
status = 0
RPS_init(freq, stereo, samples, status)
RPS_init(freq, stereo, samples, status, equal_mono)
check_error()
def quit(): # @ReservedAssignment
+3 -2
View File
@@ -86,6 +86,7 @@ def extra_imports():
import cProfile; cProfile
import pstats; pstats
import _ssl; _ssl
import SimpleHTTPServer; SimpleHTTPServer
# Used by requests.
import cgi; cgi
@@ -354,7 +355,7 @@ You may be using a system install of python. Please run {0}.sh,
renpy.audio.audio.quit()
# Prevent subprocess from throwing errors while trying to run it's
# __del__ method during shutdown.
subprocess.Popen.__del__ = popen_del
if not renpy.emscripten:
subprocess.Popen.__del__ = popen_del
+13 -8
View File
@@ -854,7 +854,7 @@ class ADVCharacter(object):
properties=self.properties,
**self.show_args)
def resolve_say_attributes(self, predict, attrs, wanted=[], remove=[], skip_trans=False):
def resolve_say_attributes(self, mode, predict, attrs, wanted=[], remove=[], skip_trans=False):
"""
Deals with image attributes associated with the current say
statement.
@@ -893,12 +893,14 @@ class ADVCharacter(object):
show_image = (self.image_tag,) + attrs + tuple(wanted) + tuple( "-" + i for i in remove)
if predict:
images.predict_show(new_image)
images.predict_show(show_image)
else:
trans = renpy.config.say_attribute_transition
layer = renpy.config.say_attribute_transition_layer
if renpy.config.say_attribute_transition_callback_attrs:
trans, layer = renpy.config.say_attribute_transition_callback(self.image_tag, new_image, mode)
else:
trans, layer = renpy.config.say_attribute_transition_callback(self.image_tag, mode)
if not skip_trans:
if (trans is not None) and (layer is not None):
@@ -950,7 +952,7 @@ class ADVCharacter(object):
if renpy.config.speaking_attribute is not None:
temporary_attrs.insert(0, renpy.config.speaking_attribute)
self.resolve_say_attributes(predicting, attrs, skip_trans=temporary_attrs)
self.resolve_say_attributes("permanent", predicting, attrs, skip_trans=temporary_attrs)
# This is so late to give resolve_say_attributes time to do some
# error handling.
@@ -963,7 +965,7 @@ class ADVCharacter(object):
images = renpy.game.context().images
attrs = images.get_attributes(None, self.image_tag)
self.resolve_say_attributes(predicting, temporary_attrs)
self.resolve_say_attributes("temporary", predicting, temporary_attrs)
return (attrs, images)
@@ -991,9 +993,12 @@ class ADVCharacter(object):
if images.showing(None, (self.image_tag,)):
if not predicting:
new_image = images.apply_attributes(None, self.image_tag, image_with_attrs)
trans = renpy.config.say_attribute_transition
layer = renpy.config.say_attribute_transition_layer
if renpy.config.say_attribute_transition_callback_attrs:
trans, layer = renpy.config.say_attribute_transition_callback(self.image_tag, new_image, "restore")
else:
trans, layer = renpy.config.say_attribute_transition_callback(self.image_tag, "restore")
if interact:
if (trans is not None) and (layer is not None):
+2 -1
View File
@@ -560,7 +560,8 @@ python early hide:
parse=parse_show_call_screen,
execute=execute_call_screen,
predict=predict_screen,
lint=lint_screen)
lint=lint_screen,
force_begin_rollback=True)
renpy.register_statement("hide screen",
parse=parse_hide_screen,
+147
View File
@@ -0,0 +1,147 @@
# Copyright 2004-2019 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.
init -1500 python hide:
def opendyslexic(f):
# Generated with fc-query --format=%{charset} OpenDyslexic3-Regular.ttf
charset = "20-7e a0-107 10a-113 116-11b 11e-123 126-12b 12e-133 136-137 139-148 14a-14d 150-15b 15e-16b 16e-17e 186 188-189 18e-192 194 197 19d 1a0-1a1 1a9 1ac 1af-1b4 1c0-1c3 1cd-1df 1e6-1e7 1fe-1ff 218-21b 228-22b 241-244 24b-24c 250-254 256-259 25b-25c 261-262 265 268 26a-26b 26f-270 272 274-276 279-27a 280-281 283 289-28a 28c-28f 294-295 298-299 29b-29c 29f 2a1-2a2 2c6-2c7 2d8-2dd 309 31b 323 326 32d 331 3b2 3b8 3c0 3c7 401 403 405-408 40c 40e 410 412 415 418 41c-41e 420-422 425 427 42f-430 432 435 437 439 43e 440-441 443 445 44f 451 453 455-458 45b-45c 45e 472 498-499 4ae 4b1 4c0 4d0 4d2-4df 4e2-4f5 4f8-4f9 1e04-1e05 1e0c-1e0f 1e12-1e13 1e24-1e25 1e2e-1e2f 1e36-1e37 1e3c-1e3f 1e44-1e47 1e4a-1e4d 1e50-1e53 1e62-1e63 1e6c-1e71 1e80-1e85 1e92-1e93 1ea0-1ef9 2013-2014 2018-201a 201c-201e 2020-2022 2026 2030 2039-203a 203d 2044 20ac 2122 2126 215b-215e 2202 2206 220f 2211-2212 2219-221a 221e 222b 2248 2260 2264-2265 25ca 2c64 2c6d 2c72-2c73 2e18 fb01-fb02"
rv = FontGroup()
for r in charset.split():
start, _, end = r.partition("-")
start = int(start, 16)
end = int(end, 16) if end else start
rv.add("_OpenDyslexic3-Regular.ttf", start, end)
rv.add(f, None, None)
return rv
config.font_transforms["opendyslexic"] = opendyslexic
config.ftfont_scale["_OpenDyslexic3-Regular.ttf"] = .87
config.ftfont_vertical_extent_scale["_OpenDyslexic3-Regular.ttf"] = .66
def dejavusans(f):
return "DejaVuSans.ttf"
config.font_transforms["dejavusans"] = dejavusans
screen _accessibility():
zorder 2000
modal True
frame:
style_group ""
has side "c b":
spacing gui._scale(10)
xfill True
yfill True
fixed:
viewport:
scrollbars "vertical"
mousewheel True
has grid 2 1:
xfill True
spacing 20
vbox:
label _("Font Override")
null height 10
textbutton _("Default"):
action Preference("font transform", None)
style_suffix "radio_button"
textbutton _("DejaVu Sans"):
action Preference("font transform", "dejavusans")
style_suffix "radio_button"
textbutton _("Opendyslexic"):
action Preference("font transform", "opendyslexic")
style_suffix "radio_button"
null height 10
label _("Text Size Scaling")
null height 10
bar value Preference("font size")
textbutton _("Reset"):
action Preference("font size", 1.0)
null height 10
label _("Line Spacing Scaling")
null height 10
bar value Preference("font line spacing")
textbutton _("Reset"):
action Preference("font line spacing", 1.0)
vbox:
label _("Self-Voicing")
null height 10
textbutton _("Off"):
action Preference("self voicing", "disable")
style_suffix "radio_button"
textbutton _("Text-to-speech"):
action Preference("self voicing", "enable")
style_suffix "radio_button"
textbutton _("Clipboard"):
action Preference("clipboard voicing", "enable")
style_suffix "radio_button"
textbutton _("Debug"):
action Preference("debug voicing", "enable")
style_suffix "radio_button"
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.")
hbox:
spacing gui._scale(25)
textbutton _("Return"):
action Hide("_accessibility")
yalign 1.0
+1
View File
@@ -226,6 +226,7 @@ init -1500 python in achievement:
backends.insert(0, SteamBackend())
else:
renpy.write_log("Failed to initialize steam.")
steam = None
def register(name, **kwargs):
+29 -9
View File
@@ -139,14 +139,17 @@ init -1500 python:
`step`
The amount to change the bar by. If None, defaults to 1/10th of
the bar.
`action`
If not None, an action to call when the field has changed.
"""
offset = 0
action = None
identity_fields = [ 'dict' ]
equality_fields = [ 'key', 'range', 'max_is_zero', 'style', 'offset', 'step']
equality_fields = [ 'key', 'range', 'max_is_zero', 'style', 'offset', 'step', 'action']
def __init__(self, dict, key, range, max_is_zero=False, style="bar", offset=0, step=None):
def __init__(self, dict, key, range, max_is_zero=False, style="bar", offset=0, step=None, action=None):
self.dict = dict
self.key = key
self.range = range
@@ -161,6 +164,7 @@ init -1500 python:
step = max(range / 10, 1)
self.step = step
self.action = action
def changed(self, value):
@@ -175,6 +179,8 @@ init -1500 python:
self.dict[self.key] = value
renpy.restart_interaction()
renpy.run(self.action)
def get_adjustment(self):
value = self.dict[self.key]
@@ -224,14 +230,17 @@ init -1500 python:
`step`
The amount to change the bar by. If None, defaults to 1/10th of
the bar.
`action`
If not None, an action to call when the field has changed.
"""
offset = 0
action = None
identity_fields = [ 'object' ]
equality_fields = [ 'range', 'max_is_zero', 'style', 'offset', 'step']
identity_fields = [ 'object', ]
equality_fields = [ 'range', 'max_is_zero', 'style', 'offset', 'step', 'action']
def __init__(self, object, field, range, max_is_zero=False, style="bar", offset=0, step=None):
def __init__(self, object, field, range, max_is_zero=False, style="bar", offset=0, step=None, action=None):
self.object = object
self.field = field
self.range = range
@@ -246,6 +255,7 @@ init -1500 python:
step = max(range / 10, 1)
self.step = step
self.action = action
def changed(self, value):
@@ -260,6 +270,8 @@ init -1500 python:
setattr(self.object, self.field, value)
renpy.restart_interaction()
renpy.run(self.action)
def get_adjustment(self):
value = getattr(self.object, self.field)
@@ -282,7 +294,7 @@ init -1500 python:
return self.style, "v" + self.style
@renpy.pure
def VariableValue(variable, range, max_is_zero=False, style="bar", offset=0, step=None):
def VariableValue(variable, range, max_is_zero=False, style="bar", offset=0, step=None, action=None):
"""
:doc: value
@@ -307,9 +319,11 @@ init -1500 python:
`step`
The amount to change the bar by. If None, defaults to 1/10th of
the bar.
`action`
If not None, an action to call when the field has changed.
"""
return FieldValue(store, variable, range, max_is_zero=max_is_zero, style=style, offset=offset, step=step)
return FieldValue(store, variable, range, max_is_zero=max_is_zero, style=style, offset=offset, step=step, action=action)
@renpy.pure
class ScreenVariableValue(BarValue, FieldEquality):
@@ -336,14 +350,17 @@ init -1500 python:
`step`
The amount to change the bar by. If None, defaults to 1/10th of
the bar.
`action`
If not None, an action to call when the field has changed.
"""
action = None
offset = 0
identity_fields = [ ]
equality_fields = [ 'variable', 'max_is_zero', 'style', 'offset', 'step']
equality_fields = [ 'variable', 'max_is_zero', 'style', 'offset', 'step', 'action']
def __init__(self, variable, range, max_is_zero=False, style="bar", offset=0, step=None):
def __init__(self, variable, range, max_is_zero=False, style="bar", offset=0, step=None, action=None):
self.variable = variable
self.range = range
self.max_is_zero = max_is_zero
@@ -357,6 +374,7 @@ init -1500 python:
step = max(range / 10, 1)
self.step = step
self.action = action
def changed(self, value):
@@ -373,6 +391,8 @@ init -1500 python:
cs.scope[self.variable] = value
renpy.restart_interaction()
renpy.run(self.action)
def get_adjustment(self):
cs = renpy.current_screen()
+12 -4
View File
@@ -72,13 +72,17 @@ init -1500 python in build:
( "renpy.py", "renpy"),
( "renpy/", "all"),
( "renpy/**.py", "renpy"),
( "renpy/**.pyx", "renpy"),
( "renpy/**.pyd", "renpy"),
( "renpy/**.pxi", "renpy"),
( "renpy/common/", "all"),
( "renpy/common/_compat/**", "renpy"),
( "renpy/common/**.rpy", "renpy"),
( "renpy/common/**.rpym", "renpy"),
( "renpy/common/_compat/**", "renpy"),
( "renpy/common/**", "all"),
( "renpy/**", "renpy"),
( "renpy/**", "all"),
# Ignore Ren'Py and renpy.exe.
( "lib/*/renpy", None),
@@ -148,6 +152,9 @@ init -1500 python in build:
("ouya_icon.png", "android"),
("ios-presplash.*", "ios"),
("ios-launchimage.png", None),
("ios-icon.png", None),
])
base_patterns = [ ]
@@ -326,8 +333,9 @@ init -1500 python in build:
package("win", "zip", "windows renpy all", "Windows x86")
package("market", "zip", "windows linux mac renpy all", "Windows, Mac, Linux for Markets")
package("steam", "zip", "windows linux mac renpy all", hidden=True)
package("android", "directory", "android renpy all", hidden=True, update=False, dlc=True)
package("ios", "directory", "ios renpy all", hidden=True, update=False, dlc=True)
package("android", "directory", "android all", hidden=True, update=False, dlc=True)
package("ios", "directory", "ios all", hidden=True, update=False, dlc=True)
package("web", "zip", "web all", update=False, dlc=True)
# Data that we expect the user to set.
@@ -424,7 +432,7 @@ init -1500 python in build:
rv["renpy_patterns"] = excludes + renpy_patterns
rv["xbit_patterns"] = xbit_patterns
rv["version"] = version or directory_name
rv["display_name"] = display_name or executable_name
rv["display_name"] = display_name or config.name or executable_name
rv["exclude_empty_directories"] = exclude_empty_directories
+16
View File
@@ -183,10 +183,26 @@ init -1900 python:
if version <= (7, 1, 1):
config.menu_actions = False
if version <= (7, 2, 2):
config.say_attribute_transition_callback_attrs = False
# The version of Ren'Py this script is intended for, or
# None if it's intended for the current version.
config.script_version = None
python early hide:
try:
import ast
script_version = renpy.file("script_version.txt").read()
script_version = ast.literal_eval(script_version)
if script_version <= (7, 2, 2):
config.keyword_after_python = True
except:
pass
init -1000 python hide:
try:
import ast
+3 -1
View File
@@ -140,6 +140,8 @@ init -1500 python in _console:
aRepr.repr_RevertableList = aRepr.repr_list
aRepr.repr_RevertableDict = aRepr.repr_dict
aRepr.repr_RevertableSet = aRepr.repr_set
aRepr.repr_defaultdict = aRepr.repr_dict
aRepr.repr_OrderedDict = aRepr.repr_dict
# The list of traced expressions.
class TracedExpressionsList(NoRollback, list):
@@ -418,7 +420,7 @@ init -1500 python in _console:
l.advance()
# Command can be None, but that's okay, since the lookup will fail.
command = l.name()
command = l.word()
command_fn = config.console_commands.get(command, None)
+1 -1
View File
@@ -373,7 +373,7 @@ init -1500 python:
if not _preferences.performance_test and not performance_test:
return
# Don't bother on android or ios - there's nothing the user can do.
# Don't bother on android or ios or emscripten - there's nothing the user can do.
if renpy.mobile:
return
+2
View File
@@ -42,6 +42,7 @@ init -1600 python:
help = [ 'K_F1', 'meta_shift_/' ],
choose_renderer = [ 'G', 'alt_shift_K_g' ],
progress_screen = [ 'alt_shift_K_p', 'meta_shift_K_p', 'K_F2' ],
accessibility = [ "K_a" ],
# Accessibility.
self_voicing = [ 'v', 'V', 'alt_K_v' ],
@@ -378,6 +379,7 @@ init -1100 python:
progress_screen = _progress_screen,
director = director.Start(),
performance = ToggleScreen("_performance"),
accessibility = ToggleScreen("_accessibility"),
)
config.underlay = [ _default_keymap ]
+9 -3
View File
@@ -55,6 +55,9 @@ init -1700 python:
return False
def __ne__(self, o):
return not (self == o)
class FieldEquality(object):
"""
Declares two objects equal if their types are the same, and
@@ -92,6 +95,9 @@ init -1700 python:
return False
def __ne__(self, o):
return not (self == o)
init -1700 python:
@@ -152,9 +158,9 @@ init -1700 python:
what = _last_say_what + config.extend_interjection + _last_raw_what
args = args + _last_say_args
kw = dict(kwargs)
kw.update(_last_say_kwargs)
kw.setdefault("interact", interact)
kw = dict(_last_say_kwargs)
kw.update(kwargs)
kw["interact"] = interact and kw.get("interact", True)
renpy.exports.say(who, what, *args, **kw)
store._last_say_what = what
+31
View File
@@ -71,6 +71,7 @@ init -1500 python:
"""
def __call__(self):
renpy.free_memory()
renpy.display.interface.display_reset = True
@@ -198,6 +199,13 @@ init -1500 python:
* Preference("gl tearing", True) - Tears rather than skipping frames.
* Preference("gl tearing", False) - Skips frames rather than tearing.
* Preference("font transform", "opendyslexic") - Sets the accessibility font transform to opendyslexic.
* Preference("font transform", "dejavusans") - Sets the accessibility font trandform to deja vu sans.
* Preference("font transform", None) - Disables the accessibility font transform.
* Preference("font size", 1.0) - Sets the accessibility font size scaling factor.
* Preference("font vertical spacing", 1.0) - Sets the accessibility font vertical spacing scaling factor.
Values that can be used with bars are:
* Preference("text speed")
@@ -206,6 +214,8 @@ init -1500 python:
* Preference("sound volume")
* Preference("voice volume")
* Preference("mixer <mixer> volume")
* Preference("font size")
* Preference("font vertical spacing")
The `range` parameter can be given to give the range of certain bars.
For "text speed", it defaults to 200 cps. For "auto-forward time", it
@@ -417,6 +427,27 @@ init -1500 python:
elif name == _("gl tearing"):
return [ SetField(_preferences, "gl_tearing", value), _DisplayReset() ]
elif name == _("font transform"):
return [ SetField(_preferences, "font_transform", value), _DisplayReset() ]
elif name == _("font size"):
if value is None:
bar_range = range or 1.0
return FieldValue(_preferences, "font_size", range=bar_range, style="slider", offset=.5, action=_DisplayReset())
return [ SetField(_preferences, "font_size", value), _DisplayReset() ]
elif name == _("font line spacing"):
if value is None:
bar_range = range or 1.0
return FieldValue(_preferences, "font_line_spacing", range=bar_range, style="slider", offset=.5, action=_DisplayReset())
return [ SetField(_preferences, "font_line_spacing", value), _DisplayReset() ]
mixer_names = {
"music" : "music",
"sound" : "sfx",
+1 -1
View File
@@ -319,7 +319,7 @@ init -1500 python:
else:
fn = config.auto_voice(tlid)
_voice.auto_filename = fn
self.auto_filename = fn
if fn and renpy.loadable(fn):
Binary file not shown.
+92
View File
@@ -0,0 +1,92 @@
Per https://www.opendyslexic.org/,
"OpenDyslexic 3 uses the SIL-OFL license, giving you freedom to use it for
personal use, business use, education, commercial, books, ebook readers,
applications, websites, and any other project or purpose you need."
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+10 -8
View File
@@ -203,28 +203,30 @@ init label _errorhandling:
left_padding gui._scale(15)
ypadding gui._scale(2)
style _radio_button_text is _button_text:
size gui._scale(26)
style _label is _default:
top_margin gui._scale(10)
bottom_margin gui._scale(15)
style _label_text is _default:
size gui._scale(40)
size gui._scale(30)
kerning -1
style _bar is _default:
left_bar "#24C"
left_bar "#468"
hover_left_bar "#24C"
right_bar "#b0b0b0"
ysize gui._scale(20)
style _vbar is _default:
bottom_bar "#24C"
bottom_bar "#468"
hover_bottom_bar "#24C"
top_bar "#b0b0b0"
xsize gui._scale(20)
bar_vertical True
style _slider is _bar
style _vslider is _vbar
style _scrollbar is _default:
thumb "#808080"
hover_thumb "#a0a0a0"
@@ -572,7 +574,7 @@ screen _exception:
side "c r":
xfill True
label _("An exception has occurred.")
label _("An exception has occurred.") text_size gui._scale(40)
text "{size=-3}[config.version!q]\n[renpy.version_only!q]\n[renpy.platform!q]{/size}" text_align 1.0 yalign 0.5
viewport:
@@ -648,7 +650,7 @@ screen _parse_errors:
has side "t c b":
spacing gui._scale(10)
label _("Parsing the script failed.")
label _("Parsing the script failed.") text_size gui._scale(40)
viewport:
id "viewport"
+47 -1
View File
@@ -514,6 +514,9 @@ statement_callbacks = [ ]
# A list of file extensions that are blacklisted by autoreload.
autoreload_blacklist = [ ".rpyc", ".rpymc", ".rpyb", ".pyc", ".pyo" ]
# A list of python modules that should be reloaded when appropriate.
reload_modules = [ ]
# The layer dialogue is shown on.
say_layer = "screens"
@@ -939,6 +942,48 @@ auto_clear_screenshot = True
# Should Ren'Py allow duplicate labels.
allow_duplicate_labels = False
# A map of font transform name to font transform function.
font_transforms = { }
# A scaling factor that is applied to a truetype font.
ftfont_scale = { }
# This is used to scale the ascent and descent of a font.
ftfont_vertical_extent_scale = { }
def say_attribute_transition_callback(*args):
"""
:args: (tag, attrs, mode)
Returns the say attribute transition to use, and the layer the transition
should be applied to (with None being a valid layer.
Attrs is the list of tags/attributes of the incoming image.
Mode is one of "permanent", "temporary", or "restore".
"""
return renpy.config.say_attribute_transition, renpy.config.say_attribute_transition_layer
# Should say_attribute_transition_callback take attrs?
say_attribute_transition_callback_attrs = True
# The function used by renpy.notify
notify = None
# Should Ren'Py support a SL2 keyword after a Python statement?
keyword_after_python = False
# A label Ren'Py should jump to if a load fails.
load_failed_label = None
# If true, Ren'Py distributes mono to both stereo channels. If false,
# it splits it 50/50.
equal_mono = True
del os
del collections
@@ -958,4 +1003,5 @@ def init():
global tts_function
tts_function = renpy.display.tts.default_tts_function
import os
global notify
notify = renpy.exports.display_notify
+4
View File
@@ -22,6 +22,7 @@
from __future__ import print_function
class Curry(object):
"""
Stores a callable and some arguments. When called, calls the
@@ -50,6 +51,9 @@ class Curry(object):
self.args == other.args and
self.kwargs == other.kwargs)
def __ne__(self, other):
return not (self == other)
def __hash__(self):
return hash(self.callable) ^ hash(self.args) ^ hash(self.kwargs)
+17 -2
View File
@@ -313,6 +313,9 @@ class Displayable(renpy.object.Object):
# Does this displayable have a tooltip?
_tooltip = None
def __ne__(self, o):
return not (self == o)
def __init__(self, focus=None, default=False, style='default', _args=None, tooltip=None, **properties):
global default_style
@@ -2115,7 +2118,7 @@ class Interface(object):
if not self.started:
return
if background:
if background and not renpy.emscripten:
self.bgscreenshot_event.clear()
self.bgscreenshot_needed = True
@@ -2191,6 +2194,9 @@ class Interface(object):
try:
renpy.display.scale.image_save_unscaled(window, filename)
if renpy.emscripten:
import emscripten
emscripten.run_script(r'''FSDownload('%s')''' % filename)
return True
except:
if renpy.config.debug:
@@ -2296,6 +2302,7 @@ class Interface(object):
ev = pygame.event.poll()
if ev.type == pygame.NOEVENT:
self.check_background_screenshot()
# Seems to prevent the CPU from speeding up.
renpy.display.draw.event_peek_sleep()
return None
@@ -2799,9 +2806,17 @@ class Interface(object):
if not expensive:
step += 1
# Step 4: Autosave.
# Step 4: Preload images (on emscripten)
elif step == 4:
if expensive and renpy.emscripten:
renpy.display.im.cache.preload_thread_pass()
step += 1
# Step 5: Autosave.
elif step == 5:
if not self.did_autosave:
renpy.loadsave.autosave()
renpy.persistent.check_update()
+53 -51
View File
@@ -442,69 +442,71 @@ class Cache(object):
self.preload_lock.wait()
self.preload_lock.release()
while self.preloads and self.keep_preloading:
def preload_thread_pass(self):
# If the size of the current generation is bigger than the
# total cache size, stop preloading.
with self.lock:
# If the cache is overfull, clean it out.
if not self.cleanout():
if renpy.config.debug_image_cache:
for i in self.preloads:
renpy.display.ic_log.write("Overfull %r", i)
self.preloads = [ ]
break
try:
image = self.preloads.pop(0)
if image not in self.preload_blacklist:
try:
self.preload_texture(image)
except:
self.preload_blacklist.add(image)
except:
pass
while self.preloads and self.keep_preloading:
# If the size of the current generation is bigger than the
# total cache size, stop preloading.
with self.lock:
self.cleanout()
# If we have time, preload pinned images.
if self.keep_preloading and not renpy.game.less_memory:
# If the cache is overfull, clean it out.
if not self.cleanout():
workset = set(renpy.store._cache_pin_set)
if renpy.config.debug_image_cache:
for i in self.preloads:
renpy.display.ic_log.write("Overfull %r", i)
# Remove things that are not in the workset from the pin cache,
# and remove things that are in the workset from pin cache.
for i in self.pin_cache.keys():
self.preloads = [ ]
if i in workset:
workset.remove(i)
else:
surf = self.pin_cache[i]
break
del self.pin_cache[i]
# For each image in the worklist...
for image in workset:
if image in self.preload_blacklist:
continue
# If we have normal preloads, break out.
if self.preloads:
break
try:
image = self.preloads.pop(0)
if image not in self.preload_blacklist:
try:
surf = image.load()
self.pin_cache[image] = surf
renpy.display.draw.load_texture(surf)
self.preload_texture(image)
except:
self.preload_blacklist.add(image)
except:
pass
with self.lock:
self.cleanout()
# If we have time, preload pinned images.
if self.keep_preloading and not renpy.game.less_memory:
workset = set(renpy.store._cache_pin_set)
# Remove things that are not in the workset from the pin cache,
# and remove things that are in the workset from pin cache.
for i in self.pin_cache.keys():
if i in workset:
workset.remove(i)
else:
surf = self.pin_cache[i]
del self.pin_cache[i]
# For each image in the worklist...
for image in workset:
if image in self.preload_blacklist:
continue
# If we have normal preloads, break out.
if self.preloads:
break
try:
surf = image.load()
self.pin_cache[image] = surf
renpy.display.draw.load_texture(surf)
except:
self.preload_blacklist.add(image)
def add_load_log(self, filename):
+3
View File
@@ -598,6 +598,9 @@ class DynamicImage(renpy.display.core.Displayable):
if self.name != o.name:
return False
if self._uses_scope and (self.target != o.target):
return False
return True
def _target(self):
+3
View File
@@ -1520,6 +1520,9 @@ class Side(Container):
super(Side, self)._clear()
self.sized = False
def per_interact(self):
self.sized = False
def render(self, width, height, st, at):
pos_d = { }
+6
View File
@@ -866,6 +866,12 @@ cdef class Render:
"""
(x, y, w, h) = rect
x = int(x)
y = int(y)
w = int(w)
h = int(h)
rv = Render(w, h)
reverse = self.reverse
+13 -4
View File
@@ -197,6 +197,8 @@ class Screen(renpy.object.Object):
This class stores information about the screen.
"""
sensitive = "True"
def __init__(self,
name,
function,
@@ -207,7 +209,8 @@ class Screen(renpy.object.Object):
variant=None,
parameters=False,
location=None,
layer="screens"):
layer="screens",
sensitive="True"):
# The name of this screen.
if isinstance(name, basestring):
@@ -256,6 +259,9 @@ class Screen(renpy.object.Object):
# The layer the screen will be shown on.
self.layer = layer
# Is this screen sensitive? An expression.
self.sensitive = sensitive
global prepared
global analyzed
@@ -654,9 +660,6 @@ class ScreenDisplayable(renpy.display.layout.Container):
if self.profile.debug:
profile_log.write("\n")
if self.phase == SHOW:
self.phase = UPDATE
return self.widgets
def render(self, w, h, st, at):
@@ -664,6 +667,9 @@ class ScreenDisplayable(renpy.display.layout.Container):
if not self.child:
self.update()
if self.phase == SHOW:
self.phase = UPDATE
try:
push_current_screen(self)
child = renpy.display.render.render(self.child, w, h, st, at)
@@ -691,6 +697,9 @@ class ScreenDisplayable(renpy.display.layout.Container):
if (self.phase == OLD) or (self.phase == HIDE):
return
if not renpy.python.py_eval(self.screen.sensitive):
return
try:
push_current_screen(self)
+4 -1
View File
@@ -107,6 +107,9 @@ class LineLogEntry(object):
return (self.filename == other.filename) and (self.line == other.line) and (self.node is other.node)
def __ne__(self, other):
return not (self == other)
class Context(renpy.object.Object):
"""
@@ -530,7 +533,7 @@ class Context(renpy.object.Object):
renpy.game.log.begin(force=force_rollback)
if self.rollback and self.force_checkpoint:
renpy.game.log.checkpoint(hard=False)
renpy.game.log.force_checkpoint = True
self.force_checkpoint = False
self.seen = False
+39 -7
View File
@@ -114,6 +114,8 @@ from renpy.ast import eval_who
from renpy.loader import add_python_directory
from renpy.lint import try_compile, try_eval
renpy_pure("ParameterizedText")
renpy_pure("Keymap")
renpy_pure("has_screen")
@@ -182,6 +184,7 @@ def public_api():
eval_who
is_selected, is_sensitive
add_python_directory
try_compile, try_eval
del public_api
@@ -228,7 +231,7 @@ def in_rollback():
Returns true if the game has been rolled back.
"""
return renpy.game.log.in_rollback()
return renpy.game.log.in_rollback() or renpy.game.after_rollback
def can_rollback():
@@ -1078,10 +1081,16 @@ def display_menu(items,
renpy.exports.mode(type)
choice_for_skipping()
# The possible choices in the menu.
choices = [ val for label, val in items ]
while None in choices:
choices.remove(None)
choices = [ ]
for _, val in items:
if isinstance(val, renpy.ui.ChoiceReturn):
val = val.value
if val is None:
continue
choices.append(val)
# Roll forward.
roll_forward = renpy.exports.roll_forward_info()
@@ -1463,12 +1472,12 @@ def movie_cutscene(filename, delay=None, loops=0, stop_music=True):
"""
:doc: movie_cutscene
This displays an MPEG-1 cutscene for the specified number of
This displays a movie cutscene for the specified number of
seconds. The user can click to interrupt the cutscene.
Overlays and Underlays are disabled for the duration of the cutscene.
`filename`
The name of a file containing an MPEG-1 movie.
The name of a file containing any movie playable by Ren'Py.
`delay`
The number of seconds to wait before ending the cutscene.
@@ -2305,8 +2314,10 @@ def free_memory():
Attempts to free some memory. Useful before running a renpygame-based
minigame.
"""
force_full_redraw()
renpy.display.interface.kill_textures_and_surfaces()
renpy.text.font.free_memory()
@renpy_pure
@@ -2370,6 +2381,14 @@ def pop_error_handler():
def error(msg):
"""
:doc: lint
Reports `msg`, a string, as as error for the user. This is logged as a
parse or lint error when approprate, and otherwise it is raised as an
exception.
"""
_error_handlers[-1](msg)
@@ -2990,6 +3009,19 @@ def notify(message):
Only one notification is displayed at a time. If a second notification
is displayed, the first notification is replaced.
This function just calls :var:`config.notify`, allowing its implementation
to be replaced by assigning a new function to that variable.
"""
renpy.config.notify(message)
def display_notify(message):
"""
:doc: other
The default implementation of :func:`renpy.notify`.
"""
hide_screen('notify')
+1 -1
View File
@@ -89,7 +89,7 @@ cdef class Environ:
cdef void imageblend(self, double fraction, int ramp)
cdef void set_vertex(self, float *vertices)
cdef void set_texture(self, int unit, float *coords)
cdef void set_color(self, float r, float g, float b, float a)
cdef void set_color(self, double r, double g, double b, double a)
cdef void set_clip(self, tuple clip_box, GLDraw draw)
cdef void unset_clip(self, GLDraw draw)
cdef void ortho(self, double left, double right, double bottom, double top, double near, double far)
+13 -2
View File
@@ -528,6 +528,14 @@ cdef class GLDraw:
self.always_opaque = True
gltexture.use_gles()
elif renpy.emscripten:
# give back control to browser regularly
self.redraw_period = 0.1
# True messes alpha imagemap/imagebutton mouseover
self.always_opaque = False
# WebGL is GLES
gltexture.use_gles()
else:
gltexture.use_gl()
@@ -637,7 +645,7 @@ cdef class GLDraw:
# ANGLE from working with fbo on Windows.
use_fbo = (
renpy.ios or renpy.android or (EGL and not ANGLE) or
renpy.ios or renpy.android or renpy.emscripten or (EGL and not ANGLE) or
use_subsystem(
glrtt_fbo,
"RENPY_GL_RTT",
@@ -1206,6 +1214,9 @@ cdef class GLDraw:
what.kill()
if renpy.emscripten:
renpy.display.interface.force_redraw = True
return a
@@ -1492,7 +1503,7 @@ cdef class Environ(object):
Sets the array of texture coordinates for unit `unit`.
"""
cdef void set_color(self, float r, float g, float b, float a):
cdef void set_color(self, double r, double g, double b, double a):
"""
Sets the color to be shown.
"""
+1 -1
View File
@@ -319,7 +319,7 @@ cdef class FixedFunctionEnviron(Environ):
else:
glDisableClientState(GL_TEXTURE_COORD_ARRAY)
cdef void set_color(self, float r, float g, float b, float a):
cdef void set_color(self, double r, double g, double b, double a):
glColor4f(r, g, b, a)
cdef void ortho(self, double left, double right, double bottom, double top, double near, double far):
+1 -1
View File
@@ -182,7 +182,7 @@ cdef class LimitedEnviron(Environ):
else:
glDisableClientState(GL_TEXTURE_COORD_ARRAY)
cdef void set_color(self, float r, float g, float b, float a):
cdef void set_color(self, double r, double g, double b, double a):
glColor4f(r, g, b, a)
+1 -1
View File
@@ -566,7 +566,7 @@ cdef class ShaderEnviron(Environ):
else:
glDisableVertexAttribArrayARB(tex)
cdef void set_color(self, float r, float g, float b, float a):
cdef void set_color(self, double r, double g, double b, double a):
glUniform4fARB(self.program.Color, r, g, b, a)
cdef void ortho(self, double left, double right, double bottom, double top, double near, double far):
+37 -1
View File
@@ -77,8 +77,26 @@ def add(msg):
print(unicode(msg).encode('utf-8'))
# Trys to evaluate an expression, announcing an error if it fails.
# Tries to evaluate an expression, announcing an error if it fails.
def try_eval(where, expr, additional=None):
"""
:doc: lint
Tries to evaluate an expression, and writes an error to lint.txt if
it fails.
`where`
A string giving the location the expression is found. Used to
generate an error message of the form "Could not evaluate `expr`
in `where`."
`expr`
The expression to try evaluating.
`additional`
If given, an additional line of information that is addded to the
error message.
"""
# Make sure the expression compiles.
try_compile(where, expr)
@@ -105,6 +123,24 @@ def try_eval(where, expr, additional=None):
def try_compile(where, expr, additional=None):
"""
:doc: lint
Tries to compile an expression, and writes an error to lint.txt if
it fails.
`where`
A string giving the location the expression is found. Used to
generate an error message of the form "Could not evaluate `expr`
in `where`."
`expr`
The expression to try compiling.
`additional`
If given, an additional line of information that is addded to the
error message.
"""
try:
renpy.python.py_compile_eval_bytecode(expr)
+15 -14
View File
@@ -465,6 +465,14 @@ def load_core(name):
if rv is not None:
return rv
# Look for the file directly.
if not renpy.config.force_archives:
try:
fn = transfn(name)
return open_file(fn, "rb")
except:
pass
# Look for the file in the apk.
for apk in apks:
prefixed_name = "/".join("x-" + i for i in name.split("/"))
@@ -474,14 +482,6 @@ def load_core(name):
except IOError:
pass
# Look for the file directly.
if not renpy.config.force_archives:
try:
fn = transfn(name)
return open_file(fn, "rb")
except:
pass
# Look for it in archive files.
for prefix, index in archives:
if not name in index:
@@ -586,11 +586,6 @@ def loadable_core(name):
if name in loadable_cache:
return loadable_cache[name]
for apk in apks:
prefixed_name = "/".join("x-" + i for i in name.split("/"))
if prefixed_name in apk.info:
return True
try:
transfn(name)
loadable_cache[name] = True
@@ -598,6 +593,12 @@ def loadable_core(name):
except:
pass
for apk in apks:
prefixed_name = "/".join("x-" + i for i in name.split("/"))
if prefixed_name in apk.info:
loadable_cache[name] = True
return True
for _prefix, index in archives:
if name in index:
loadable_cache[name] = True
@@ -743,7 +744,7 @@ class RenpyImporter(object):
code = compile(source, filename, 'exec', renpy.python.old_compile_flags, 1)
exec code in mod.__dict__
return mod
return sys.modules[fullname]
def get_data(self, filename):
return load(filename).read()
+12 -4
View File
@@ -179,7 +179,7 @@ def save_dump(roots, log):
return size
f = file("save_dump.txt", "w")
f, _ = renpy.error.open_error_file("save_dump.txt", "w")
visit(roots, "roots")
visit(log, "log")
@@ -474,6 +474,9 @@ def autosave_thread(take_screenshot):
finally:
autosave_not_running.set()
if renpy.emscripten:
import emscripten
emscripten.syncfs()
def autosave():
@@ -550,9 +553,14 @@ def force_autosave(take_screenshot=False, block=False):
return
autosave_not_running.clear()
t = threading.Thread(target=autosave_thread, args=(take_screenshot,))
t.daemon = True
t.start()
if not renpy.emscripten:
t = threading.Thread(target=autosave_thread, args=(take_screenshot,))
t.daemon = True
t.start()
else:
import emscripten
emscripten.async_call(autosave_thread, take_screenshot, -1)
################################################################################
+5 -1
View File
@@ -148,7 +148,11 @@ class LogFile(object):
if self.open():
if not self.raw_write:
s = s % args
try:
s = s % args
except:
s = repr((s,) + args)
s += "\n"
if not isinstance(s, unicode):
+12 -7
View File
@@ -258,13 +258,6 @@ def main():
game.basepath = renpy.config.gamedir
renpy.config.searchpath = [ renpy.config.gamedir ]
if renpy.android and ("ANDROID_PUBLIC" in os.environ):
android_game = os.path.join(os.environ["ANDROID_PUBLIC"], "game")
if os.path.exists(android_game):
renpy.config.searchpath.insert(0, android_game)
# Find the common directory.
commondir = __main__.path_to_common(renpy.config.renpy_base) # E1101 @UndefinedVariable
@@ -274,10 +267,22 @@ def main():
else:
renpy.config.commondir = None
# Add path from env variable, if any
if "RENPY_SEARCHPATH" in os.environ:
renpy.config.searchpath.extend(os.environ["RENPY_SEARCHPATH"].split("::"))
if renpy.android:
renpy.config.searchpath = [ ]
renpy.config.commondir = None
if "ANDROID_PUBLIC" in os.environ:
android_game = os.path.join(os.environ["ANDROID_PUBLIC"], "game")
print("Android searchpath: ", android_game)
if os.path.exists(android_game):
renpy.config.searchpath.insert(0, android_game)
# Load Ren'Py extensions.
for dir in renpy.config.searchpath: # @ReservedAssignment
for fn in os.listdir(dir):
+14
View File
@@ -51,6 +51,20 @@ from renpy.python import store_eval as eval
from renpy.display.core import absolute
_print = print
def print(*args, **kwargs):
"""
:undocumented:
This is a variant of the print function that forces a checkpoint
at the start of the next statement, so that it can't be rolled past.
"""
renpy.game.context().force_checkpoint = True
_print(*args, **kwargs)
def _(s):
"""
+6 -2
View File
@@ -722,7 +722,7 @@ class Lexer(object):
def string(self):
"""
Lexes a string, and returns the string to the user, or none if
Lexes a string, and returns the string to the user, or None if
no string could be found. This also takes care of expanding
escapes and collapsing whitespace.
@@ -1190,7 +1190,7 @@ class Lexer(object):
if not text:
return None
return renpy.ast.PyExpr(self.text[start:self.pos].strip(), self.filename, self.number)
return renpy.ast.PyExpr(text, self.filename, self.number)
def comma_expression(self):
"""
@@ -1222,6 +1222,10 @@ class Lexer(object):
self.line, self.filename, self.number, self.text, self.subblock, self.pos = state
self.word_cache_pos = -1
if self.line < len(self.block):
self.eob = False
else:
self.eob = True
def get_location(self):
"""
+1 -1
View File
@@ -108,7 +108,7 @@ def analyze():
dt[1],
dt[2],
dt[3],
event.format(*args),
event.format(*args).replace("%", "%%"),
)
s = s.encode("utf-8")
+12
View File
@@ -153,6 +153,15 @@ Preference("gl_framerate", None, (int, type(None)))
# Do we allow tearing?
Preference("gl_tearing", False)
# The font transformation used.
Preference("font_transform", None, (type(None), basestring))
# An adjustment applied to font size.
Preference("font_size", 1.0)
# An adjustment applied to font line spacing.
Preference("font_line_spacing", 1.0)
class Preferences(renpy.object.Object):
"""
@@ -233,6 +242,9 @@ class Preferences(renpy.object.Object):
def __eq__(self, other):
return vars(self) == vars(other)
def __ne__(self, other):
return not (self == other)
renpy.game.Preferences = Preferences
renpy.game.preferences = Preferences()
+50 -20
View File
@@ -1084,7 +1084,7 @@ class DetRandom(random.Random):
Resets the RNG, removing all of the pushbacked numbers.
"""
self.stack = [ ]
del self.stack[:]
def Random(self, seed=None):
"""
@@ -1237,7 +1237,8 @@ class Rollback(renpy.object.Object):
print("Removing unreachable:", o, file=renpy.log.real_stdout)
pass
self.objects = new_objects
del self.objects[:]
self.objects.extend(new_objects)
return True
@@ -1308,6 +1309,7 @@ class RollbackLog(renpy.object.Object):
nosave = [ 'old_store', 'mutated', 'identifier_cache' ]
identifier_cache = None
force_checkpoint = False
def __init__(self):
@@ -1337,6 +1339,10 @@ class RollbackLog(renpy.object.Object):
# reset?
self.did_interaction = True
# Should we force a checkpoint before completing the current
# statement.
self.force_checkpoint = False
def after_setstate(self):
self.mutated = { }
self.rolled_forward = False
@@ -1401,8 +1407,8 @@ class RollbackLog(renpy.object.Object):
begin_stores()
# If the log is too long, prune it.
if len(self.log) > renpy.config.rollback_length:
self.log = self.log[-renpy.config.rollback_length:]
while len(self.log) > renpy.config.rollback_length:
self.log.pop(0)
# check for the end of fixed rollback
if self.log and self.log[-1] == self.current:
@@ -1420,7 +1426,7 @@ class RollbackLog(renpy.object.Object):
self.log.append(self.current)
self.mutated = { }
self.mutated.clear()
# Flag a mutation as having happened. This is used by the
# save code.
@@ -1450,6 +1456,10 @@ class RollbackLog(renpy.object.Object):
Should be true if called from begin().
"""
if self.force_checkpoint:
self.checkpoint(hard=False)
self.force_checkpoint = False
# Update self.current.stores with the changes from each store.
# Also updates .ever_been_changed.
for name, sd in store_dicts.iteritems():
@@ -1460,7 +1470,7 @@ class RollbackLog(renpy.object.Object):
for _i in xrange(4):
self.current.objects = [ ]
del self.current.objects[:]
try:
for _k, v in self.mutated.iteritems():
@@ -1582,7 +1592,7 @@ class RollbackLog(renpy.object.Object):
self.forward.pop(0)
else:
self.current.forward = data
self.forward = [ ]
del self.forward[:]
elif data is not None:
if self.forward:
@@ -1591,12 +1601,12 @@ class RollbackLog(renpy.object.Object):
fwd_name, fwd_data = self.forward[0]
if (self.current.context.current == fwd_name
and data == fwd_data
and (keep_rollback or self.rolled_forward)
):
and data == fwd_data
and (keep_rollback or self.rolled_forward)
):
self.forward.pop(0)
else:
self.forward = [ ]
del self.forward[:]
# Log the data in case we roll back again.
self.current.forward = data
@@ -1619,7 +1629,7 @@ class RollbackLog(renpy.object.Object):
renpy.game.context().force_checkpoint = True
if purge:
self.log = [ ]
del self.log[:]
def retain_after_load(self):
"""
@@ -1645,6 +1655,25 @@ class RollbackLog(renpy.object.Object):
return self.rollback_limit > 0
def load_failed(self):
"""
This is called to try to recover when rollback fails.
"""
if not renpy.config.load_failed_label:
raise Exception("Couldn't find a place to stop rolling back. Perhaps the script changed in an incompatible way?")
rb = self.log.pop()
rb.rollback()
while renpy.exports.call_stack_depth():
renpy.exports.pop_call()
renpy.game.contexts[0].force_checkpoint = True
renpy.game.contexts[0].goto_label(renpy.config.load_failed_label)
raise renpy.game.RestartTopContext()
def rollback(self, checkpoints, force=False, label=None, greedy=True, on_load=False, abnormal=True, current_label=None):
"""
This rolls the system back to the first valid rollback point
@@ -1671,7 +1700,7 @@ class RollbackLog(renpy.object.Object):
load. Used to implement .retain_after_load()
`abnormal`
If true, treats this as an abnormal event, suppresisng rollback
If true, treats this as an abnormal event, suppressing transitions
and so on.
`current_label`
@@ -1708,15 +1737,16 @@ class RollbackLog(renpy.object.Object):
break
else:
if force:
raise Exception("Couldn't find a place to stop rolling back. Perhaps the script changed in an incompatible way?")
# Otherwise, just give up.
print("Can't find a place to rollback to. Not rolling back.")
revlog.reverse()
self.log = self.log + revlog
self.log.extend(revlog)
if force:
self.load_failed()
else:
print("Can't find a place to rollback to. Not rolling back.")
return
force_checkpoint = False
@@ -1783,7 +1813,7 @@ class RollbackLog(renpy.object.Object):
# If necessary, reset the RNG.
if force:
rng.reset()
self.forward = [ ]
del self.forward[:]
# Flag that we're in the transition immediately after a rollback.
renpy.game.after_rollback = abnormal
+7
View File
@@ -370,6 +370,9 @@ class FileLocation(object):
return self.directory == other.directory
def __ne__(self, other):
return not (self == other)
class MultiLocation(object):
"""
@@ -504,6 +507,10 @@ class MultiLocation(object):
return self.locations == other.locations
def __ne__(self, other):
return not (self == other)
# The thread that scans locations every few seconds.
scan_thread = None
+5
View File
@@ -263,6 +263,11 @@ class Script(object):
initcode = [ ]
for fn, dir in script_files: # @ReservedAssignment
# Mitigate "busy script" warning from the browser
if renpy.emscripten:
import emscripten
emscripten.sleep(0)
self.load_appropriate_file(".rpyc", ".rpy", dir, fn, initcode)
# Make the sort stable.
+28
View File
@@ -301,6 +301,13 @@ class SLNode(object):
return False
def has_python(self):
"""
Returns true if this node is Python or has a python node as a child.
"""
return False
# A sentinel used to indicate a keyword argument was not given.
NotGiven = renpy.object.Sentinel("NotGiven")
@@ -451,6 +458,9 @@ class SLBlock(SLNode):
return False
def has_python(self):
return any(i.has_python() for i in self.children)
list_or_tuple = (list, tuple)
@@ -1265,6 +1275,9 @@ class SLIf(SLNode):
return False
def has_python(self):
return any(i[1].has_python() for i in self.entries)
class SLShowIf(SLNode):
"""
@@ -1354,6 +1367,9 @@ class SLShowIf(SLNode):
return False
def has_python(self):
return any(i[1].has_python() for i in self.entries)
class SLFor(SLBlock):
"""
@@ -1514,6 +1530,9 @@ class SLPython(SLNode):
self.constant = NOT_CONST
self.last_keyword = True
def has_python(self):
return True
class SLPass(SLNode):
@@ -1559,6 +1578,9 @@ class SLDefault(SLNode):
scope[variable] = eval(self.expr, context.globals, scope)
def has_python(self):
return True
class SLUse(SLNode):
@@ -1862,6 +1884,7 @@ class SLScreen(SLBlock):
analysis = None
layer = "'screens'"
sensitive = "True"
def __init__(self, loc):
@@ -1885,6 +1908,9 @@ class SLScreen(SLBlock):
# Should we predict this screen?
self.predict = "None" # expr.
# Should this screen be sensitive.
self.sensitive = "True"
# The parameters this screen takes.
self.parameters = None
@@ -1905,6 +1931,7 @@ class SLScreen(SLBlock):
rv.variant = self.variant
rv.predict = self.predict
rv.parameters = self.parameters
rv.sensitive = self.sensitive
rv.prepared = False
rv.analysis = None
@@ -1928,6 +1955,7 @@ class SLScreen(SLBlock):
parameters=self.parameters,
location=self.location,
layer=renpy.python.py_eval(self.layer),
sensitive=self.sensitive,
)
def analyze(self, analysis):
+4 -4
View File
@@ -437,14 +437,14 @@ def sl2add(d, replaces=None, scope=None, **kwargs):
Transform = renpy.display.motion.Transform
if kwargs:
rv = Transform(child=d, **kwargs)
rv._main = d
if (replaces is not None) and isinstance(rv, Transform):
rv.take_state(replaces)
rv.take_execution_state(replaces)
if kwargs:
rv = Transform(child=d, **kwargs)
rv._main = d
return rv
+39 -22
View File
@@ -25,6 +25,8 @@ import collections
import renpy.sl2
import renpy.sl2.slast as slast
from ast import literal_eval
# A tuple of style prefixes that we know of.
STYLE_PREFIXES = [
'',
@@ -166,23 +168,23 @@ class Parser(object):
elif isinstance(i, Parser):
self.children[i.name] = i
def parse_statement(self, loc, l, layout_mode=False):
def parse_statement(self, loc, l, layout_mode=False, keyword=True):
word = l.word() or l.match(r'\$')
if word and word in self.children:
if layout_mode:
c = self.children[word].parse_layout(loc, l, self)
c = self.children[word].parse_layout(loc, l, self, keyword)
else:
c = self.children[word].parse(loc, l, self)
c = self.children[word].parse(loc, l, self, keyword)
return c
else:
return None
def parse_layout(self, loc, l, parent):
def parse_layout(self, loc, l, parent, keyword):
l.error("The %s statement cannot be used as a container for the has statement." % self.name)
def parse(self, loc, l, parent):
def parse(self, loc, l, parent, keyword):
"""
This is expected to parse a function statement, and to return
a list of python ast statements.
@@ -199,7 +201,7 @@ class Parser(object):
raise Exception("Not Implemented")
def parse_contents(self, l, target, layout_mode=False, can_has=False, can_tag=False, block_only=False):
def parse_contents(self, l, target, layout_mode=False, can_has=False, can_tag=False, block_only=False, keyword=True):
"""
Parses the remainder of the current line of `l`, and all of its subblock,
looking for keywords and children.
@@ -255,6 +257,12 @@ class Parser(object):
expr = l.comma_expression()
if (not keyword) and (not renpy.config.keyword_after_python):
try:
literal_eval(expr)
except:
l.error("a non-constant keyword argument like '%s %s' is not allowed after a python block." % (name, expr))
target.keyword.append((name, expr))
if block_only:
@@ -309,13 +317,16 @@ class Parser(object):
if child_index != 0:
l.error("The has statement may not be given after a child has been supplied.")
c = self.parse_statement(loc, l, layout_mode=True)
c = self.parse_statement(loc, l, layout_mode=True, keyword=keyword)
if c is None:
l.error('Has expects a child statement.')
target.children.append(c)
if c.has_python():
keyword = False
continue
c = self.parse_statement(loc, l)
@@ -328,6 +339,10 @@ class Parser(object):
if c is not None:
target.children.append(c)
child_index += 1
if c.has_python():
keyword = False
continue
l.revert(state)
@@ -553,10 +568,10 @@ class DisplayableParser(Parser):
add(renpy.sl2.slproperties.ui_properties)
add(renpy.sl2.slproperties.position_properties)
def parse_layout(self, loc, l, parent):
return self.parse(loc, l, parent, True)
def parse_layout(self, loc, l, parent, keyword):
return self.parse(loc, l, parent, keyword, layout_mode=True)
def parse(self, loc, l, parent, layout_mode=False):
def parse(self, loc, l, parent, keyword, layout_mode=False):
rv = slast.SLDisplayable(
loc,
@@ -612,7 +627,7 @@ class IfParser(Parser):
if not parent_contents:
childbearing_statements.add(self)
def parse(self, loc, l, parent):
def parse(self, loc, l, parent, keyword):
if self.parent_contents:
contents_from = parent
@@ -642,7 +657,7 @@ class IfParser(Parser):
l.require(':')
block = slast.SLBlock(loc)
contents_from.parse_contents(l, block, block_only=True)
contents_from.parse_contents(l, block, block_only=True, keyword=keyword)
rv.entries.append((condition, block))
@@ -654,7 +669,7 @@ class IfParser(Parser):
l.require(':')
block = slast.SLBlock(loc)
contents_from.parse_contents(l, block, block_only=True)
contents_from.parse_contents(l, block, block_only=True, keyword=keyword)
rv.entries.append((condition, block))
@@ -713,7 +728,7 @@ class ForParser(Parser):
l.error("expected variable or tuple pattern.")
def parse(self, loc, l, parent):
def parse(self, loc, l, parent, keyword):
l.skip_whitespace()
@@ -755,7 +770,7 @@ ForParser("for")
class OneLinePythonParser(Parser):
def parse(self, loc, l, parent):
def parse(self, loc, l, parent, keyword):
loc = l.get_location()
source = l.require(l.rest_statement)
@@ -772,7 +787,7 @@ OneLinePythonParser("$")
class MultiLinePythonParser(Parser):
def parse(self, loc, l, parent):
def parse(self, loc, l, parent, keyword):
loc = l.get_location()
@@ -792,7 +807,7 @@ MultiLinePythonParser("python")
class PassParser(Parser):
def parse(self, loc, l, parent):
def parse(self, loc, l, parent, keyword):
l.expect_eol()
@@ -804,7 +819,7 @@ PassParser("pass")
class DefaultParser(Parser):
def parse(self, loc, l, parent):
def parse(self, loc, l, parent, keyword):
name = l.require(l.word)
l.require(r'=')
@@ -825,7 +840,7 @@ class UseParser(Parser):
super(UseParser, self).__init__(name)
childbearing_statements.add(self)
def parse(self, loc, l, parent):
def parse(self, loc, l, parent, keyword):
if l.keyword('expression'):
target = l.require(l.simple_expression)
@@ -863,7 +878,7 @@ Keyword("style_group")
class TranscludeParser(Parser):
def parse(self, loc, l, parent):
def parse(self, loc, l, parent, keyword):
l.expect_eol()
return slast.SLTransclude(loc)
@@ -928,7 +943,7 @@ class CustomParser(Parser):
# The number of positional parameters required.
self.positional = positional
def parse(self, loc, l, parent):
def parse(self, loc, l, parent, keyword):
arguments = [ ]
@@ -961,7 +976,7 @@ class ScreenParser(Parser):
def __init__(self):
super(ScreenParser, self).__init__("screen", statement=False)
def parse(self, loc, l, parent, name="_name"):
def parse(self, loc, l, parent, name="_name", keyword=True):
screen = slast.SLScreen(loc)
@@ -977,6 +992,7 @@ class ScreenParser(Parser):
screen.variant = keyword.get("variant", "None")
screen.predict = keyword.get("predict", "None")
screen.layer = keyword.get("layer", "'screens'")
screen.sensitive = keyword.get("sensitive", "True")
return screen
@@ -989,6 +1005,7 @@ Keyword("predict")
Keyword("style_group")
Keyword("style_prefix")
Keyword("layer")
Keyword("sensitive")
parser = None
+13
View File
@@ -48,6 +48,7 @@ def register(
label=None,
warp=None,
translation_strings=None,
force_begin_rollback=False,
):
"""
:doc: statement_register
@@ -128,9 +129,15 @@ def register(
return a list of strings, which are then reported as being available
to be translated.
`force_begin_rollback`
This should be set to true on statements that are likely to cause the
end of a fast skip, similar to ``menu`` or ``call screen``.
"""
name = tuple(name.split())
if label:
force_begin_rollback = True
registry[name] = dict(
parse=parse,
lint=lint,
@@ -142,6 +149,7 @@ def register(
label=label,
warp=warp,
translation_strings=translation_strings,
rollback="force" if force_begin_rollback else "normal",
)
if block not in [True, False, "script", "possible"]:
@@ -212,6 +220,11 @@ def call(method, parsed, *args, **kwargs):
return method(parsed, *args, **kwargs)
def get(key, parsed):
name, parsed = parsed
return registry[name].get(key, None)
def get_name(parsed):
name, _parsed = parsed
return " ".join(name)
+9
View File
@@ -183,6 +183,15 @@ class Formatter(string.Formatter):
value = value.replace("{", "{{")
if "u" in conversion:
value = value.upper()
if "l" in conversion:
value = value.lower()
if "c" in conversion:
value = value.capitalize()
return value
+36 -32
View File
@@ -384,6 +384,10 @@ class BMFont(ImageFont):
self.width[c] = w + xo
self.advance[c] = xadvance
self.offsets[c] = (xo, yo)
elif kind == "kerning":
first = unichr(int(args["first"]))
second = unichr(int(args["second"]))
self.kerns[first + second] = int(args["amount"])
f.close()
@@ -635,7 +639,7 @@ def load_face(fn):
if font_file is None:
raise Exception("Could not find font {0!r}.".format(orig_fn))
rv = ftfont.FTFace(font_file, index) # @UndefinedVariable
rv = ftfont.FTFace(font_file, index, orig_fn) # @UndefinedVariable
face_cache[orig_fn] = rv
@@ -727,14 +731,9 @@ class FontGroup(object):
def __init__(self):
# A list of font names we know of.
self.fonts = [ ]
# A map from character to the index of the font it's part of.
self.cache = { }
# A list of (index, start, end) tuples.
self.patterns = [ ]
# A map from character index to font name. None is used for
# the default font.
self.map = { }
def add(self, font, start, end):
"""
@@ -744,7 +743,8 @@ class FontGroup(object):
`start`
The start of the range. This may be a single-character string, or
an integer giving a unicode code point.
an integer giving a unicode code point. If start is None, then the
font is used as the default.
`end`
The end of the range. This may be a single-character string, or an
@@ -757,6 +757,18 @@ class FontGroup(object):
chained together.
"""
if start is None:
if isinstance(font, FontGroup):
for k, v in font.map.items():
if k not in self.map:
self.map[k] = v
else:
if None not in self.map:
self.map[None] = font
return self
if not isinstance(start, int):
start = ord(start)
@@ -766,12 +778,9 @@ class FontGroup(object):
if end < start:
raise Exception("In FontGroup.add, the start of a character range must be before the end of the range.")
if font not in self.fonts:
self.fonts.append(font)
index = self.fonts.index(font)
self.patterns.append((index, start, end))
for i in range(start, end+1):
if i not in self.map:
self.map[i] = font
return self
@@ -783,32 +792,27 @@ class FontGroup(object):
mark = 0
pos = 0
old_index = 0
cache = self.cache
old_font = None
for c in s:
index = cache.get(c, None)
n = ord(c)
if index is None:
n = ord(c)
font = self.map.get(ord(c), None)
for index, start, end in self.patterns:
if start <= n <= end:
break
else:
if font is None:
font = self.map.get(None, None)
if font is None:
raise Exception("Character U+{0:04x} not found in FontGroup".format(n))
cache[c] = index
if index != old_index:
if font != old_font:
if pos:
yield self.fonts[old_index], s[mark:pos]
yield old_font, s[mark:pos]
old_index = index
old_font = font
mark = pos
pos += 1
yield self.fonts[old_index], s[mark:]
yield font, s[mark:]
+16 -4
View File
@@ -31,6 +31,8 @@ from ttgsubtable cimport *
from textsupport cimport Glyph, SPLIT_INSTEAD
import traceback
import renpy.config
cdef extern from "ftsupport.h":
char *freetype_error_to_string(int error)
@@ -147,11 +149,16 @@ cdef class FTFace:
# The offset in that file.
unsigned long offset
def __init__(self, f, index):
public object fn
def __init__(self, f, index, fn):
cdef int error
cdef unsigned long size
# The filename.
self.fn = fn
# The file that the font is opened from.
self.f = f
@@ -249,6 +256,8 @@ cdef class FTFont:
if bold:
antialias = True
size = size * renpy.config.ftfont_scale.get(face.fn, 1.0) * renpy.game.preferences.font_size
self.face_object = face
self.face = self.face_object.face
@@ -287,6 +296,7 @@ cdef class FTFont:
cdef int error
cdef FT_Face face
cdef FT_Fixed scale
cdef float ascent_scale
face = self.face
@@ -303,8 +313,10 @@ cdef class FTFont:
scale = face.size.metrics.y_scale
self.ascent = FT_CEIL(face.size.metrics.ascender)
self.descent = FT_FLOOR(face.size.metrics.descender)
vextent_scale = renpy.config.ftfont_vertical_extent_scale.get(self.face_object.fn, 1.0)
self.ascent = FT_CEIL(int(face.size.metrics.ascender * vextent_scale))
self.descent = FT_FLOOR(int(face.size.metrics.descender * vextent_scale))
if self.descent > 0:
self.descent = -self.descent
@@ -320,7 +332,7 @@ cdef class FTFont:
# if self.height > self.lineskip:
# self.lineskip = self.height
self.lineskip = self.height
self.lineskip = <int> self.height * renpy.game.preferences.font_line_spacing
if self.vertical:
self.underline_offset = FT_FLOOR(FT_MulFix(face.ascender + face.descender - face.underline_position, scale))
+10 -2
View File
@@ -293,13 +293,21 @@ class TextSegment(object):
on the font group.
"""
if not isinstance(self.font, font.FontGroup):
tf = self.font
font_transform = renpy.game.preferences.font_transform
if font_transform is not None:
font_func = renpy.config.font_transforms.get(font_transform, None)
if font_func is not None:
tf = font_func(tf)
if not isinstance(tf, font.FontGroup):
yield (self, s)
return
segs = { }
for f, ss in self.font.segment(s):
for f, ss in tf.segment(s):
seg = segs.get(f, None)
+2
View File
@@ -637,6 +637,8 @@ def change_language(language, force=False):
else:
old_change_language(tl, language)
renpy.store._history_list = [ ]
for i in renpy.config.change_language_callbacks:
i()
+1499
View File
File diff suppressed because it is too large Load Diff
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
set -e
if [ -z "$1" ]; then
echo "$0 requires a version."
exit 1
fi
RENPY="$(dirname $0)/.."
butler () {
"$RENPY/tmp/butler-linux-amd64/butler" "$@"
}
D="$RENPY/dl/$1"
P=renpytom/renpy-mirror
butler push "$D/renpy-$1-sdk.zip" $P:sdk-windows-mac-linux
butler push "$D/renpy-$1-rapt.zip" $P:rapt
butler push "$D/renpy-$1-raspi.tar.bz2" $P:raspi
butler push "$D/renpy-$1-renios.zip" $P:renios
butler push "$D/renpy-$1-atom-linux.tar.bz2" $P:atom-linux
butler push "$D/renpy-$1-atom-mac.zip" $P:atom-mac
butler push "$D/renpy-$1-atom-windows.zip" $P:atom-windows
butler push "$D/renpy-$1-editra-linux.tar.bz2" $P:editra-linux
butler push "$D/renpy-$1-editra-mac.zip" $P:editra-mac
butler push "$D/renpy-$1-editra-windows.zip" $P:editra-windows
+6 -4
View File
@@ -849,6 +849,8 @@ both horizontal and vertical positions.
Otherwise, the components are considered to be an absolute number
of pixels.
If corners and crop are given, crop takes priority over corners.
.. transform-property:: crop_relative
:type: boolean
@@ -862,16 +864,16 @@ both horizontal and vertical positions.
:type: None or (int, int)
:default: None
If not None, gives the upper-left corner of the crop box. This
takes priority over crop.
If not None, gives the upper-left corner of the crop box. Crop takes
priority over corners.
.. transform-property:: corner2
:type: None or (int, int)
:default: None
If not None, gives the lower right corner of the crop box. This
takes priority over crop.
If not None, gives the lower right corner of the crop box. Cropt takes
priority over corners.
.. transform-property:: size
+64 -10
View File
@@ -3,9 +3,20 @@
Creator-Defined Statements
==========================
Creator-defined statements allow you to add your own statements to Ren'Py. This
Creator-Defined Statements (CDS) allow you to add your own statements to Ren'Py. This
makes it possible to add things that are not supported by the current syntax of
Ren'Py.
Ren'Py. CDS are more flexible than the direct Python code. Most often, CDS are used
when you have a repeatable construction. For example, calling a function with one argument.
Ren'Py does not know what this function does and how it should be executed,
so Ren'Py does not do anything with it until execution and has an error if an exception occurs.
Using the CDS allows you to check the correctness of the syntax using parse
(for example, check that the argument is a valid string), to ignore incorrect data
at execution (for non-critical functions, it is better to skip the execute than
to throw an exception), predict displayables (if the function uses them),
and give you addition information during lint (if at runtime it was ignored you
can have a report here). The CDS does not guarantee that the execution will be successful,
but the better you code your statement, the better Ren'Py can "understand" what
you expect from it.
Creator-defined statements must be defined in a ``python early`` block. What's more,
the filename containing the user-defined statement must be be loaded earlier
@@ -13,7 +24,7 @@ than any file that uses it. Since Ren'Py loads files in Unicode sort order, it
generally makes sense to prefix the name of any file containing a user-defined
statement with 01, or some other small number.
A user-defined statement cannot be used in the file in which it is defined.
A creator-defined statement cannot be used in the file in which it is defined.
Creator-defined statement are registered using the :func:`renpy.register_statement`
function.
@@ -24,10 +35,42 @@ The parse method takes a Lexer object:
.. class:: Lexer
.. method:: error(msg)
Adds a `msg` (with the current position) in the list of detected
parsing errors. This interrupts the parsing of the current statement,
but does not prevent further parsing.
.. method:: require(thing, name=None)
Tries to parse `thing`, and reports an error if it cannot be done.
If `thing` is a string, tries to parse it using :func:`match`.
Otherwise, thing must be a other method on this lexer object,
which is called without arguments. If `name` is not specified,
the name of the method will be used in the message
(or `thing` if it's a string), otherwise the `name` will be used.
.. method:: eol()
True if the lexer is at the end of the line.
.. method:: expect_eol()
If we are not at the end of the line, raise an error.
.. method:: expect_noblock(stmt)
Called to indicate this statement does not expect a block.
If a block is found, raises an error. `stmt` should be a string,
it will be added to the message with an error.
.. method:: expect_block(stmt)
Called to indicate that the statement requires that a non-empty
block is present. `stmt` should be a string, it will be added
to the message with an error.
.. method:: match(re)
Matches an arbitrary regexp string.
@@ -35,7 +78,8 @@ The parse method takes a Lexer object:
All of the statements in the lexer that match things are implemented
in terms of this function. They first skip whitespace, then attempt
to match against the line. If the match succeeds, the matched text
is returned. Otherwise, None is returned.
is returned. Otherwise, None is returned, and the state of the lexer
is unchanged.
.. method:: keyword(s)
@@ -50,9 +94,9 @@ The parse method takes a Lexer object:
Matches any word, including keywords. Returns the text of the
matched word.
.. method:: image_name_component():
.. method:: image_name_component()
Matches an image name component. Unlike a a word, and image name
Matches an image name component. Unlike a word, a image name
component can begin with a number.
.. method:: string()
@@ -78,15 +122,23 @@ The parse method takes a Lexer object:
.. method:: simple_expression()
Matches a simple Python expression, returns it as a string.
This is often used when you expect a variable name.
It is not recommended to change the result. The correct action is
to evaluate the result in the future.
.. method:: delimited_python(delim)
Matches a pythio expression that ends in a delimiter, often
':' or '='.
Matches a Python expression that ends in a `delim`, for example ':'.
This is often used when you expect a condition until the delimiter.
It is not recommended to change the result. The correct action is
to evaluate the result in the future. This raises an error if
end of line is reached before the delimiter.
.. method:: arguments()
Returns an object representing the arguments to a function
This must be called before the parentheses with the arguments list,
if they are not specified returns None, otherwise
returns an object representing the arguments to a function
call. This object has an ``evaluate`` method on it that
takes an optional `scope` dictionary, and returns a tuple
in which the first component is a tuple of positional arguments,
@@ -113,7 +165,9 @@ The parse method takes a Lexer object:
.. method:: advance()
In a subblock lexer, advances to the next line. This must be called
before the first line, so the first line can be parsed.
before the first line, so the first line can be parsed. Returns True
if we've successfully advanced to a line in the block, or False
if we have advanced beyond all lines in the block.
Lint Utility Functions
+97 -4
View File
@@ -2,11 +2,105 @@
Full Changelog
==============
.. _renpy-7.2.2:
7.2.2
=====
Ren'Py now supports a new Accessibility menu, which can be accessed
by pressing the "a" key. This menu, which is intended to be used by
players, let's the player override the game font, change the scaling
and spacing of text, and enable self-voicing.
Ren'Py will now allow files in the public game directory
(/mnt/sdcard/Android/`package`/files/game) to override those included with the
game proper. This has been listed as a feature in 7.2.0, but didn't work
right.
Ren'Py will now include temporary image attributes in the say statements
generated as part of the translation process.
When uploading to itch.io, Ren'Py now downloads butler itself. This means
there is no longer a dependence on the location or structure of the Itch
app, as there was before.
The bar values :func:`DictValue`, :func:`FieldValue`, :func:`VariableValue`,
and :func:`ScreenVariableValue` all take a new `action` parameter, which
gives an action that is performed each time the value changes.
The rollback system has been optimized to reduce the amount of garbage
that needs to be collected.
.. _renpy-7.2.1:
7.2.1
=====
iOS Improvements
----------------
Ren'Py will now set the version field for the iOS application when generating
a project.
Ren'Py will now look for the files ios-icon.png and ios-launchimage.png,
and use them to generate the icon and launch image in the sizes required
for iOS.
Other Improvements
-------------------
The :func:`renpy.in_rollback` function returns True when in the rollback that
occurs immediately after a load. This makes it possible to use::
python:
if not renpy.in_rollback():
renpy.run(ShowMenu('save'))
To display a save menu during an initial playthough, but not during loading
or rollback.
:var:`config.say_attribute_transition_callback` now exists, making it possible
to select the transition to use on a per-say-statement basis.
The new ``RENPY_SEARCHPATH`` environment variable makes it possible to
override :var:`config.searchpath` on launch.
Fixes
-----
Ren'Py has been audited to make sure that the semantics of == and != match,
whenever == was redefined.
There was a fix to problems that might occur when a transform is added
to a screen using the ``add`` statement.
The way ``extend`` processes arguments was changed to ensure that newer
arguments take priority over arguments given to the statement being
extended.
Ren'Py now properly considers the scope when comparing dynamic images for
equality. This fixes an issue that prevented some dynamic images from
updating when part of a screen.
A crash when :var:`config.save_dump` is true on macOS has been fixed.
A crash when :var:`config.profile` is true has been fixed.
Ren'Py now explicitly asks for text (as opposed to email, password, phone number
etc) input on Android when the keyboard is displayed.
An issue has been fixed that prevented roll-forward from working through a
menu statement.
Fixes a bug that prevents roll-forward through a menu.
.. _renpy-7.2.0:
.. _renpy-7.2:
.. _renpy-7.1.4:
7.2
===
7.2.0
=====
Menu Arguments
--------------
@@ -143,7 +237,6 @@ Ren'Py now uses the Amazon payment APIs when sideloaded onto a device
made by Amazon, allowing their payment system to be tested on a dual-store
APK.
Ren'Py will now allow files in the public game directory (/mnt/sdcard/Android/`package`/files/game)
to override those included with the game proper.
@@ -161,7 +254,7 @@ interaction restart occurs.
Size-changing properties like :ref:`xysize` now apply to a Drag and not
Size-changing properties like :propref:`xysize` now apply to a Drag and not
the space it can move around in.
A bug that could cause a transparent, black, or gray line to appear on
+44
View File
@@ -160,6 +160,30 @@ These control transitions between various screens.
If not None, a transition to use when the image is changed by a
say statement with image attributes.
.. var:: config.say_attribute_transition_callback = ...
This is a function that return a transition to apply and a layer to
apply it on
This should be a function that takes three arguments, the image tag
being shown, a tuple of tags describing the image being shown, and a
`mode` parameter that is one of:
* "permanent", for permanent attribute change (one that lasts longer
than the current say statement).
* "temporary", for a temporary attribute change (one that is restored
at the end of the current say statement).
* "restore", for when a temporary change is being restored.
This should return a 2-component tuple, consiting of:
* The transition to use, or None if no transition should occur.
* The layer the transition should be on, either a string or None. This is
almost always None.
The default implementation of this returns (config.say_attribute_transition,
config.say_attribute_transition_layer).
.. var:: config.say_attribute_transition_layer = None
If not None, this must be a string giving the name of a layer. (Almost always
@@ -526,6 +550,13 @@ Occasionally Used
If not None, this should be a string giving the default language
that the game is translated into by the translation framework.
.. var:: config.load_failed_label = None
If a string, this is a label that is jumped to when a load fails because
the script has changed so much that Ren'Py can't recover.
Before performing the load, Ren'Py will revert to the start of the
last statement, then it will clear the call stack.
.. var:: config.locale_to_language_function = ...
A function that determines the language the game should use,
@@ -633,6 +664,13 @@ Occasionally Used
Uses nearest-neighbor filtering by default, to support pixel art or
melting players' eyes.
.. var:: config.notify = ...
This is called by :func:`renpy.notify` or :func:`Notify` with a
single `message` argument, to display the notification. The default
implementation is :funct:`renpy.display_notify`. This is intended
to allow creators to intercept notifications.
.. var:: config.optimize_texture_bounds = False
When True, Ren'Py will scan images to find the bounding box of the
@@ -680,6 +718,12 @@ Occasionally Used
a window. The default action prompts the user to see if he wants
to quit the game.
.. var:: config.reload_modules = [ ]
A list of strings giving the names of python modules that should be
reloaded along with the game. Any submodules of these modules
will also be reloaded.
.. var:: config.replace_text = None
If not None, a function that is called with a single argument, a text to
+2
View File
@@ -58,6 +58,7 @@ the omission in future versions.
* Evilantishad0w
* Felix Lampe
* Franck_v
* Frédéric Chapoton
* Gas
* George Economidis
* Giuseppe Bertone
@@ -96,6 +97,7 @@ the omission in future versions.
* Markus Koschany
* Max le Fou
* Maxwell Paul Brickner
* Meithal
* Merumelu
* mikey (ATP Projects)
* Morgan Willcock
+2 -1
View File
@@ -163,7 +163,8 @@ the @ and the temporary ones coming after. ::
e happy @ vhappy "Really! That changes everything."
To cause a transition to occur whenever the images are changed in this way, set
:var:`config.say_attribute_transition` to a transition.
:var:`config.say_attribute_transition` to a transition. For more control,
use :var:`config.say_attribute_transition_callback`.
Example Characters
+4
View File
@@ -58,6 +58,10 @@ The following environment variables control the behavior of Ren'Py:
%d substitution in it. For example, setting this to "screenshot%04d.jpg" will
cause Ren'Py to write out jpeg screenshots rather than the usual pngs.
``RENPY_SEARCHPATH``
If set, a double-colon (\:\:) separated list of additional paths that
are added to :var:`config.searchpath`.
``RENPY_SIMPLE_EXCEPTIONS``
When set, this disables Ren'Py's graphical exception handling.
+8
View File
@@ -12,6 +12,14 @@ features.
Incompatible changes to the GUI are documented at :ref:`gui-changes`, as
such changes only take effect when the GUI is regenerated.
.. _incompatible-7.2.2:
7.2.2
-----
:var:`config.say_attribute_transition_callback` has been changed to
accept a new argument, the image being displayed.
.. _incompatible-7.1.1:
+14
View File
@@ -128,4 +128,18 @@ to display the game's user interface.
Once Ren'Py finishes initializing, it will either resume the current game,
or run the splashscreen and main menu.
Generation
-----------
To ease development, it's possible to have Ren'Py generate the iOS icon
and launch image from files.
``ios-icon.png``
If present, this file is scaled to the various sizes of png file
needed to make an iOS icon.
``ios-launchimage.png``
If present, this file is used to make an iOS Launch Image. This is done
by creating an image of the required sized, filling it with the top-left
pixel of this file, and then centering this file in the center of the
Launch Image.
+1 -1
View File
@@ -839,7 +839,7 @@ style radio_vbox:
style radio_button:
properties gui.button_properties("radio_button")
foreground "gui/button/check_[prefix_]foreground.png"
foreground "gui/button/radio_[prefix_]foreground.png"
style radio_button_text:
properties gui.button_text_properties("radio_button")
+2
View File
@@ -9,6 +9,8 @@ support can be used for many things, from setting a flag to creating
new displayables. This chapter covers ways in which Ren'Py scripts can
directly invoke Python, through the various Python statements.
Ren'Py currently supports Python 2.7, though we strongly recommend you write
Python that runs in Python 2 and Python 3.
.. _python-statement:
+2 -1
View File
@@ -603,7 +603,8 @@ using a Python assignment statement. ::
Lines beginning with a dollar-sign are interpreted as Python statements. The
assignment statement here assigns a value to a variable. Ren'Py has support
for other ways of including Python, such as a multi-line Python statement,
that are discussed in other sections of this manual.
that are discussed in other sections of this manual. Ren'Py supports Python 2.7,
though we strongly recommend you write Python that runs in Python 2 and Python 3.
To check the flag, use the ``if`` statement::
+11 -4
View File
@@ -207,9 +207,8 @@ All user interface statements take the following common properties:
By default, the ``id`` is automatically-generated.
`style`
The name of the style applied to this displayable. This may be a
string name, or a style object. The style gives default
values for style properties.
A string giving the name of the style applied to this displayable. The
style gives default values for style properties.
`style_prefix`
.. _style-prefix:
@@ -259,6 +258,14 @@ All user interface statements take the following common properties:
:func:`GetTooltip` function. See the :ref:`tooltips` section for
more details.
`arguments`
A tuple or list containing additional positional arguments that
are given to the displayable.
`properties`
A dictionary containing additional properties given to the
displayable.
Many user interface statements take classes of style properties, or
transform properties. These properties can have a style prefix
associated with them, that determines when they apply. For example, if
@@ -1713,7 +1720,7 @@ them from the expression.
hbox:
for i, member in enumerate(party):
vbox:
use member.screen pass (i+1)
use expression member.screen pass (i + 1)
Use and Transclude
+153 -150
View File
@@ -1,208 +1,211 @@
<ul class='sponsors'>
<li> 017Bluefield (7.1.27.2)
<li> __skwrl__ (6.99.14.37.2)
<li> __skwrl__ (6.99.14.37.2.2)
<li> Adia Alderson (6.99.137.1.2)
<li> Aleema (6.99.137.2)
<li> <a href="http://alexdodge.net/" rel="nofollow">Alex Dodge</a> (6.99.147.2)
<li> <a href="https://rabidb.com/" rel="nofollow">AlexSSZ</a> (6.99.137.2)
<li> <a href="https://alic-szecsei.com" rel="nofollow">Alic Szecsei</a> (6.99.14.37.2)
<li> Almost Human (7.2)
<li> Ana Raffali (7.2)
<li> Andurin (7.1.27.2)
<li> Anne Camlin (7.07.2)
<li> Aleema (6.99.137.2.2)
<li> <a href="http://alexdodge.net/" rel="nofollow">Alex Dodge</a> (6.99.147.2.2)
<li> <a href="https://rabidb.com/" rel="nofollow">AlexSSZ</a> (6.99.137.2.2)
<li> <a href="https://alic-szecsei.com" rel="nofollow">Alic Szecsei</a> (6.99.14.37.2.2)
<li> Almost Human (7.27.2.2)
<li> Ana Raffali (7.27.2.2)
<li> Andurin (7.1.27.2.2)
<li> Anne Camlin (7.07.2.2)
<li> <a href="https://lemmasoft.renai.us/forums/viewtopic.php?f=51&t=34131" rel="nofollow">AR09FQF-AQ09FRF</a> (6.99.14.17.0)
<li> Asatiir (6.99.137.0)
<li> <a href="https://www.renpy.org/agegate?url=https://www.patreon.com/ASLPro3D" rel="nofollow">ASLPro3D</a> (7.07.2)
<li> <a href="https://www.atwistedspirit.com/" rel="nofollow">ATwistedSpirit</a> (7.17.2)
<li> <a href="https://www.renpy.org/agegate?url=https://www.patreon.com/ASLPro3D" rel="nofollow">ASLPro3D</a> (7.07.2.2)
<li> <a href="https://www.atwistedspirit.com/" rel="nofollow">ATwistedSpirit</a> (7.17.2.2)
<li> <a href="https://patchworkprincess.moe/" rel="nofollow">Azura (Vanessa Parker)</a> (7.07.1)
<li> <a href="https://www.youtube.com/channel/UC3Xm0sk-rRCtD-yVp64WPWQ" rel="nofollow">BadGamer</a> (6.99.14.37.2)
<li> <a href="https://www.youtube.com/channel/UC3Xm0sk-rRCtD-yVp64WPWQ" rel="nofollow">BadGamer</a> (6.99.14.37.2.2)
<li> Belfort and Bastion (6.99.137.1)
<li> Biotikos Development (6.99.137.2)
<li> Biotikos Development (6.99.137.2.2)
<li> Bob (6.99.137.0)
<li> <a href="https://bobcgames.itch.io" rel="nofollow">Bob Conway</a> (6.99.14.17.2)
<li> Bob Reus (7.17.2)
<li> bob_xxii (7.2)
<li> Booom313's tinker corner (6.99.147.2)
<li> Boreas Bear (6.99.137.2)
<li> <a href="http://boyslaughplus.moe" rel="nofollow">Boys Laugh +</a> (6.99.137.2)
<li> <a href="https://bobcgames.itch.io" rel="nofollow">Bob Conway</a> (6.99.14.17.2.2)
<li> Bob Reus (7.17.2.2)
<li> bob_xxii (7.27.2.2)
<li> Booom313's tinker corner (6.99.147.2.2)
<li> Boreas Bear (6.99.137.2.2)
<li> <a href="http://boyslaughplus.moe" rel="nofollow">Boys Laugh +</a> (6.99.137.2.2)
<li> Brett Douglas (6.99.146.99.14.3)
<li> Brimney (6.99.137.2)
<li> <a href="https://www.renpy.org/agegate?url=https://www.brunimultimedia.com" rel="nofollow">Bruni Multimedia</a> (6.99.147.2)
<li> Brimney (6.99.137.2.2)
<li> <a href="https://www.renpy.org/agegate?url=https://www.brunimultimedia.com" rel="nofollow">Bruni Multimedia</a> (6.99.147.2.2)
<li> <a href="http://www.bura.cl" rel="nofollow">BURA</a> (6.99.13)
<li> Cameron Woodard (6.99.14.37.2)
<li> Cara Hillstock (6.99.137.2)
<li> <a href="https://www.celebrityhunter.com.br" rel="nofollow">Celebrity Hunter</a> (7.17.2)
<li> Charlene Gilbert (6.99.137.2)
<li> <a href="http://charliethegoldfish.com" rel="nofollow">Charlie Francis Cassidy</a> (6.99.137.2)
<li> Cameron Woodard (6.99.14.37.2.2)
<li> Cara Hillstock (6.99.137.2.2)
<li> <a href="https://www.celebrityhunter.com.br" rel="nofollow">Celebrity Hunter</a> (7.17.2.2)
<li> Charlene Gilbert (6.99.137.2.2)
<li> <a href="http://charliethegoldfish.com" rel="nofollow">Charlie Francis Cassidy</a> (6.99.137.2.2)
<li> Chekhov's Ghost (7.17.2)
<li> Cherry Kiss Games (6.99.14.17.2)
<li> Chrysoula Tzavelas (6.99.137.2)
<li> <a href="https://cloverfirefly.itch.io" rel="nofollow">cloverfirefly</a> (6.99.137.2)
<li> CobaltCore (7.1.17.2)
<li> CobraPL (7.17.2)
<li> <a href="https://www.renpy.org/agegate?url=https://obscurasoft.com/" rel="nofollow">Coming Out On Top</a> (6.99.137.2)
<li> <a href="http://www.computerart.club/" rel="nofollow">computerart.club</a> (6.99.147.2)
<li> CookieNomNom (7.1.17.2)
<li> Crazy Cactus (6.99.14.37.2)
<li> <a href="https://www.renpy.org/agegate?url=http://cricketbat.me" rel="nofollow">cricketbat_</a> (7.07.2)
<li> Cherry Kiss Games (6.99.14.17.2.2)
<li> Chrysoula Tzavelas (6.99.137.2.2)
<li> <a href="https://cloverfirefly.itch.io" rel="nofollow">cloverfirefly</a> (6.99.137.2.2)
<li> CobaltCore (7.1.17.2.2)
<li> CobraPL (7.17.2.2)
<li> <a href="https://www.renpy.org/agegate?url=https://obscurasoft.com/" rel="nofollow">Coming Out On Top</a> (6.99.137.2.2)
<li> <a href="http://www.computerart.club/" rel="nofollow">computerart.club</a> (6.99.147.2.2)
<li> CookieNomNom (7.1.17.2.2)
<li> Crazy Cactus (6.99.14.37.2.2)
<li> <a href="https://www.renpy.org/agegate?url=http://cricketbat.me" rel="nofollow">cricketbat_</a> (7.07.2.2)
<li> <a href="http://cultofape.com" rel="nofollow">CultOfApe</a> (6.99.147.0)
<li> Culture4Jam (6.99.137.2)
<li> Culture4Jam (6.99.137.2.2)
<li> Daniel Burns (7.17.2)
<li> <a href="https://www.renpy.org/agegate?url=http://www.darkministry.net" rel="nofollow">Dark Ministry</a> (7.2)
<li> <a href="https://www.renpy.org/agegate?url=http://www.darkministry.net" rel="nofollow">Dark Ministry</a> (7.27.2.2)
<li> <a href="http://dark-aii.net" rel="nofollow">Dark-Aii</a> (6.99.14.16.99.14.3)
<li> <a href="http://www.darksirengames.com" rel="nofollow">DarkSirenGames</a> (6.99.137.2)
<li> Darryl Taylor (7.07.2)
<li> David Koster (6.99.147.2)
<li> David Yingling (6.99.14.17.2)
<li> Dee (7.17.2)
<li> Denny Cheng (6.99.14.27.2)
<li> Dharker Studios Ltd (6.99.136.99.14, 7.1.17.2)
<li> <a href="http://coriginate.com" rel="nofollow">Ds110</a> (6.99.137.2)
<li> DuoDevelopers (7.1.17.2)
<li> E. William Brown (6.99.137.2)
<li> <a href="http://www.darksirengames.com" rel="nofollow">DarkSirenGames</a> (6.99.137.2.2)
<li> Darryl Taylor (7.07.2.2)
<li> David Koster (6.99.147.2.2)
<li> David Yingling (6.99.14.17.2.2)
<li> Dee (7.17.2.2)
<li> Denny Cheng (6.99.14.27.2.2)
<li> Dharker Studios Ltd (6.99.136.99.14, 7.1.17.2.2)
<li> <a href="http://coriginate.com" rel="nofollow">Ds110</a> (6.99.137.2.2)
<li> DuoDevelopers (7.1.17.2.2)
<li> E. William Brown (6.99.137.2.2)
<li> <a href="https://twitter.com/WC_EchoFrost" rel="nofollow">EchoFrost (Watercress)</a> (6.99.14.27.2)
<li> <a href="https://www.youtube.com/c/ElaineDoesCoding" rel="nofollow">ElaineDoesCoding</a> (7.07.2)
<li> eric chi hung ng (7.07.2)
<li> Euan 'Xolf' Robertson (6.99.137.2)
<li> <a href="https://www.youtube.com/c/ElaineDoesCoding" rel="nofollow">ElaineDoesCoding</a> (7.07.2.2)
<li> eric chi hung ng (7.07.2.2)
<li> Euan 'Xolf' Robertson (6.99.137.2.2)
<li> Eve Hallows (6.99.136.99.14)
<li> Exiscoming (6.99.147.2)
<li> Exiscoming (6.99.147.2.2)
<li> Fuseblower (7.07.2)
<li> George (7.17.2)
<li> George (7.17.2.2)
<li> <a href="http://www.gtbono.com" rel="nofollow">Giovanni Tempobono</a> (7.17.1.2)
<li> <a href="https://goldengamebarn.com/" rel="nofollow">Golden Game Barn</a> (6.99.137.2)
<li> <a href="https://twitter.com/goodtalesvn" rel="nofollow">Good Tales</a> (7.17.2)
<li> Great Chicken Studio (6.99.147.2)
<li> Grimiku (7.07.2)
<li> <a href="https://goldengamebarn.com/" rel="nofollow">Golden Game Barn</a> (6.99.137.2.2)
<li> <a href="https://twitter.com/goodtalesvn" rel="nofollow">Good Tales</a> (7.17.2.2)
<li> Great Chicken Studio (6.99.147.2.2)
<li> Grimiku (7.07.2.2)
<li> Gunso (6.99.137.2)
<li> HAG Productions (7.1.17.2)
<li> <a href="http://hanb.jp" rel="nofollow">HanbitGaram (MOEWORK Entertainment)</a> (7.17.2)
<li> HAG Productions (7.1.17.2.2)
<li> <a href="http://hanb.jp" rel="nofollow">HanbitGaram (MOEWORK Entertainment)</a> (7.17.2.2)
<li> Happimochi (6.99.136.99.14)
<li> HopesGaming (7.1.17.2)
<li> HopesGaming (7.1.17.2.2)
<li> <a href="https://ijemin.com" rel="nofollow">I_Jemin</a> (6.99.137.0)
<li> <a href="https://irredeemable.net/" rel="nofollow">Irredeemable Games</a> (6.99.147.2)
<li> J. C. Holder (6.99.137.2)
<li> <a href="https://irredeemable.net/" rel="nofollow">Irredeemable Games</a> (6.99.147.2.2)
<li> J. C. Holder (6.99.137.2.2)
<li> James Tyner (6.99.137.1)
<li> JeongPyo Lee (6.99.137.2)
<li> Joanna B (6.99.137.2)
<li> JoeBanks (7.17.2)
<li> Jonas Kyratzes (7.1.17.2)
<li> jonnymelabo (6.99.137.2)
<li> Joseph Denicola (7.2)
<li> JeongPyo Lee (6.99.137.2.2)
<li> Joanna B (6.99.137.2.2)
<li> JoeBanks (7.17.2.2)
<li> Jonas Kyratzes (7.1.17.2.2)
<li> jonnymelabo (6.99.137.2.2)
<li> Joseph Denicola (7.27.2.2)
<li> Josh Kaplan (6.99.136.99.14.2)
<li> K. Starbuck (6.99.136.99.14)
<li> Kampmichi (7.1.17.2)
<li> Kayde Initials (6.99.137.2)
<li> <a href="http://kexboy.com" rel="nofollow">KEXBOY</a> (6.99.137.2)
<li> Kikai Digital (7.07.2)
<li> <a href="http://www.gamesbykinmoku.com" rel="nofollow">Kinmoku</a> (6.99.137.2)
<li> <a href="http://krsm94.web.fc2.com/" rel="nofollow">Klast Halc (crAsm)</a> (6.99.14.27.2)
<li> Kosmos Games (6.99.147.2)
<li> Kampmichi (7.1.17.2.2)
<li> Katryna Starks (7.2.17.2.2)
<li> Kayde Initials (6.99.137.2.2)
<li> <a href="http://kexboy.com" rel="nofollow">KEXBOY</a> (6.99.137.2.2)
<li> Kikai Digital (7.07.2.2)
<li> <a href="http://www.gamesbykinmoku.com" rel="nofollow">Kinmoku</a> (6.99.137.2.2)
<li> <a href="http://krsm94.web.fc2.com/" rel="nofollow">Klast Halc (crAsm)</a> (6.99.14.27.2.2)
<li> Kosmos Games (6.99.147.2.2)
<li> <a href="https://de.koubaibu.tech/" rel="nofollow">Koubaibu Technology Group</a> (7.17.1.2)
<li> Kraig Halfpap (6.99.14.27.2)
<li> L C (7.2)
<li> <a href="https://www.renpy.org/agegate?url=https://la-cucaracha-studios.itch.io/" rel="nofollow">La Cucaracha Studios</a> (7.1.27.2)
<li> lain105/EckoMars (6.99.137.2)
<li> <a href="http://waltz.moe/portfolio/" rel="nofollow">Laiska</a> (7.2)
<li> LateWhiteRabbit (6.99.147.2)
<li> <a href="https://lernakow.itch.io" rel="nofollow">Lernakow</a> (6.99.14.37.2)
<li> Kraig Halfpap (6.99.14.27.2.2)
<li> L C (7.27.2.2)
<li> <a href="https://www.renpy.org/agegate?url=https://la-cucaracha-studios.itch.io/" rel="nofollow">La Cucaracha Studios</a> (7.1.27.2.2)
<li> lain105/EckoMars (6.99.137.2.2)
<li> <a href="http://waltz.moe/portfolio/" rel="nofollow">Laiska</a> (7.27.2.2)
<li> LateWhiteRabbit (6.99.147.2.2)
<li> <a href="https://lernakow.itch.io" rel="nofollow">Lernakow</a> (6.99.14.37.2.2)
<li> LewdKitty (7.07.2)
<li> <a href="https://www.renpy.org/agegate?url=https://www.lewdlab.com/" rel="nofollow">Lewdlab</a> (6.99.14.17.2)
<li> <a href="https://lemmasoft.renai.us/forums/viewtopic.php?f=66&t=48011&p=482643" rel="nofollow">Lezalith</a> (7.17.2)
<li> LewdKitty (7.2.17.2.2)
<li> <a href="https://www.renpy.org/agegate?url=https://www.lewdlab.com/" rel="nofollow">Lewdlab</a> (6.99.14.17.2.2)
<li> <a href="https://lemmasoft.renai.us/forums/viewtopic.php?f=66&t=48011&p=482643" rel="nofollow">Lezalith</a> (7.17.2.2)
<li> Lictor (6.99.14.27.1.2)
<li> Little Napoleon (7.17.2)
<li> <a href="http://trash.moe/" rel="nofollow">Lore</a> (6.99.137.2)
<li> Luke Jackson (6.99.137.2)
<li> <a href="http://luscious-spirit-studios.tumblr.com/" rel="nofollow">Luscious Spirit Studios</a> (6.99.137.2)
<li> <a href="https://www.renpy.org/agegate?url=https://www.patreon.com/Luxee" rel="nofollow">Luxee</a> (6.99.14.37.2)
<li> Maeve Bandruid (6.99.137.2)
<li> <a href="https://www.renpy.org/agegate?url=https://www.patreon.com/manitu" rel="nofollow">MANITU Games CO.</a> (6.99.137.2)
<li> <a href="https://manorstories.fr" rel="nofollow">ManorStories</a> (6.99.147.2)
<li> <a href="http://www.marionettecode.com/" rel="nofollow">Marionette</a> (6.99.147.2)
<li> Little Napoleon (7.17.2.2)
<li> <a href="http://trash.moe/" rel="nofollow">Lore</a> (6.99.137.2.2)
<li> Luke Jackson (6.99.137.2.2)
<li> <a href="http://luscious-spirit-studios.tumblr.com/" rel="nofollow">Luscious Spirit Studios</a> (6.99.137.2.2)
<li> <a href="https://www.renpy.org/agegate?url=https://www.patreon.com/Luxee" rel="nofollow">Luxee</a> (6.99.14.37.2.2)
<li> Maeve Bandruid (6.99.137.2.2)
<li> <a href="https://www.renpy.org/agegate?url=https://www.patreon.com/manitu" rel="nofollow">MANITU Games CO.</a> (6.99.137.2.2)
<li> <a href="https://manorstories.fr" rel="nofollow">ManorStories</a> (6.99.147.2.2)
<li> <a href="http://www.marionettecode.com/" rel="nofollow">Marionette</a> (6.99.147.2.2)
<li> Matthew (7.2)
<li> Matthew Preston (7.2)
<li> Matthew Preston (7.2)
<li> Matthew Preston (7.27.2.2)
<li> MAW.3D.Art (6.99.13)
<li> Max J Sher (6.99.137.2)
<li> MCatter Dev (7.17.2)
<li> <a href="https://meagantrott.com" rel="nofollow">Meagan Trott</a> (6.99.137.2)
<li> <a href="http://metasepia.icecavern.net" rel="nofollow">Metasepia Games</a> (6.99.137.2)
<li> Meyvol (6.99.14.17.2)
<li> Michael Lewis (7.2)
<li> Michael Simon (7.1.17.2)
<li> Michaela Laws (6.99.137.2)
<li> Max J Sher (6.99.137.2.2)
<li> MCatter Dev (7.17.2.2)
<li> <a href="https://meagantrott.com" rel="nofollow">Meagan Trott</a> (6.99.137.2.2)
<li> <a href="http://metasepia.icecavern.net" rel="nofollow">Metasepia Games</a> (6.99.137.2.2)
<li> Meyvol (6.99.14.17.2.2)
<li> Michael Lewis (7.27.2.2)
<li> Michael Simon (7.1.17.2.2)
<li> Michaela Laws (6.99.137.2.2)
<li> <a href="http://www.milkteestudios.com" rel="nofollow">Milktee Studios</a> (7.07.1)
<li> Miss Skizzors (7.07.2)
<li> Montse Latre (7.1.17.2)
<li> <a href="https://moonlitworks.com/" rel="nofollow">Moonlit Works</a> (7.07.2)
<li> <a href="https://mrvaldez.ph/" rel="nofollow">MrValdez</a> (7.07.2)
<li> Miss Skizzors (7.07.2.2)
<li> Montse Latre (7.1.17.2.2)
<li> <a href="https://moonlitworks.com/" rel="nofollow">Moonlit Works</a> (7.07.2.2)
<li> <a href="https://mrvaldez.ph/" rel="nofollow">MrValdez</a> (7.07.2.2)
<li> <a href="https://ms45.itch.io" rel="nofollow">Ms .45</a> (6.99.13)
<li> nasredin (6.99.14.37.2)
<li> Natalie Van Sistine (7.07.2)
<li> <a href="https://cafe.naver.com/vmo.cafe" rel="nofollow">Naver cafe VMO</a> (7.17.2)
<li> nasredin (6.99.14.37.2.2)
<li> Natalie Van Sistine (7.07.2.2)
<li> <a href="https://cafe.naver.com/vmo.cafe" rel="nofollow">Naver cafe VMO</a> (7.17.2.2)
<li> <a href="http://cafe.naver.com/vmo" rel="nofollow">NaverCafe VMO</a> (6.99.137.1)
<li> Neigan (7.0)
<li> Nicholas Swenson (6.99.147.2)
<li> <a href="https://www.patreon.com/nikraria" rel="nofollow">nikraria</a> (6.99.137.2)
<li> Nova Alamak (6.99.14.37.2)
<li> Nicholas Swenson (6.99.147.2.2)
<li> <a href="https://www.patreon.com/nikraria" rel="nofollow">nikraria</a> (6.99.137.2.2)
<li> Nova Alamak (6.99.14.37.2.2)
<li> <a href="http://devety.com" rel="nofollow">Oleksii olety Kyrylchuk</a> (6.99.14.16.99.14.2)
<li> Panda Penguin Games (7.2)
<li> Panda Penguin Games (7.27.2.2)
<li> Panthera Morag (6.99.137.2)
<li> Patchwork Princess (7.17.2)
<li> Patchwork Princess (7.17.2.2)
<li> Patricia (6.99.137.1.1)
<li> <a href="http://peachpattch.com/" rel="nofollow">peachpattch</a> (6.99.147.2)
<li> philat (6.99.14.17.2)
<li> Pi3shark (7.2)
<li> plasterbrain (6.99.137.2)
<li> <a href="http://peachpattch.com/" rel="nofollow">peachpattch</a> (6.99.147.2.2)
<li> philat (6.99.14.17.2.2)
<li> Pi3shark (7.27.2.2)
<li> plasterbrain (6.99.137.2.2)
<li> Ploppertje (6.99.136.99.14.3)
<li> Podge (6.99.137.2)
<li> Podge (6.99.137.2.2)
<li> Puriwit Sesupaponphong (6.99.13)
<li> Puriwit Sesupaponphong (6.99.137.1)
<li> Pólyí Konrád (7.17.1.1)
<li> <a href="http://www.cottoncandycyanide.com/" rel="nofollow">Quantum Suicide</a> (7.17.2)
<li> <a href="http://razzartvisual.com/" rel="nofollow">Razz</a> (6.99.137.2)
<li> <a href="http://razzartvisual.com/" rel="nofollow">Razz</a> (6.99.137.2.2)
<li> <a href="https://twitter.com/ragnarhomsar" rel="nofollow">Realga</a> (7.07.1)
<li> René Dudfield (6.99.137.2)
<li> Richard (6.99.147.2)
<li> <a href="http://impqueen.com/" rel="nofollow">Rinmaru</a> (6.99.137.2)
<li> Rizia Praja (6.99.14.17.2)
<li> Robyn Rosenfeld (7.1, 7.2)
<li> René Dudfield (6.99.137.2.2)
<li> Richard (6.99.147.2.2)
<li> <a href="http://impqueen.com/" rel="nofollow">Rinmaru</a> (6.99.137.2.2)
<li> Rizia Praja (6.99.14.17.2.2)
<li> Robyn Rosenfeld (7.1, 7.27.2.2)
<li> <a href="http://sunkoiwish.com/" rel="nofollow">Royce A Xaiyeon</a> (6.99.137.2)
<li> Ryan Balgos (6.99.147.2)
<li> Ryan Balgos (6.99.147.2.2)
<li> Ryan Whited (6.99.147.1)
<li> <a href="https://saiffyros.wordpress.com/" rel="nofollow">Saiffyros</a> (6.99.147.2)
<li> Sam Yang (6.99.137.2)
<li> <a href="https://saiffyros.wordpress.com/" rel="nofollow">Saiffyros</a> (6.99.147.2.2)
<li> Sam Yang (6.99.137.2.2)
<li> Simply Aria (6.99.147.2)
<li> <a href="http://sleepyagents.co.uk" rel="nofollow">Sleepy Agents</a> (6.99.137.2)
<li> Snowlocke (7.07.2)
<li> <a href="http://sleepyagents.co.uk" rel="nofollow">Sleepy Agents</a> (6.99.137.2.2)
<li> Snowlocke (7.07.2.2)
<li> <a href="http://store.steampowered.com/app/570350/Sorry_Entschuldigung__A_Psychological_Horror_Visual_Novel/" rel="nofollow">Sorry. (Entschuldigung)</a> (6.99.14.27.1)
<li> Spyros Panagiotopoulos (6.99.147.1)
<li> Steamgirl (6.99.136.99.14, 7.2)
<li> Steamlynx (6.99.14.17.2)
<li> Stefan Gagne (6.99.137.2)
<li> <a href="https://www.renpy.org/agegate?url=http://www.patreon.com/errilhl" rel="nofollow">Studio Errilhl</a> (6.99.14.37.2)
<li> Steamgirl (6.99.136.99.14, 7.27.2.2)
<li> Steamlynx (6.99.14.17.2.2)
<li> Stefan Gagne (6.99.137.2.2)
<li> <a href="https://www.renpy.org/agegate?url=http://www.patreon.com/errilhl" rel="nofollow">Studio Errilhl</a> (6.99.14.37.2.2)
<li> <a href="https://vnstudioelan.com/" rel="nofollow">Studio Élan</a> (7.2.17.2.2)
<li> <a href="https://play.google.com/store/apps/developer?id=Suhaimizs&hl=en" rel="nofollow">Suhaimizs</a> (7.2)
<li> Taosym (6.99.137.2)
<li> Taosym (6.99.137.2.2)
<li> Teofilo Hurtado (6.99.147.1.1)
<li> the66 (7.2)
<li> the66 (7.27.2.2)
<li> Thomas Charlie (6.99.137.2)
<li> Timothy Landreth (6.99.137.2)
<li> Timothy Young (6.99.14.17.2)
<li> TJ Huckabee (6.99.137.2)
<li> Timothy Landreth (6.99.137.2.2)
<li> Timothy Young (6.99.14.17.2.2)
<li> TJ Huckabee (6.99.137.2.2)
<li> ToastCrust (6.99.136.99.14)
<li> Tobias Wüthrich (6.99.136.99.14.3)
<li> <a href="http://tremmigames.tk/" rel="nofollow">tremmiGames</a> (7.07.2)
<li> trooper6 (6.99.137.2)
<li> <a href="https://www.youtube.com/tryinmorning" rel="nofollow">tryinmorning</a> (6.99.137.2)
<li> <a href="https://studiomugenjohncel.wordpress.com/" rel="nofollow">Uncle Mugen (Mugenjohncel</a> (6.99.14.27.2)
<li> <a href="http://www.visualsaga.com/" rel="nofollow">Visual Saga</a> (6.99.137.2)
<li> <a href="http://tremmigames.tk/" rel="nofollow">tremmiGames</a> (7.07.2.2)
<li> trooper6 (6.99.137.2.2)
<li> <a href="https://www.youtube.com/tryinmorning" rel="nofollow">tryinmorning</a> (6.99.137.2.2)
<li> <a href="https://studiomugenjohncel.wordpress.com/" rel="nofollow">Uncle Mugen (Mugenjohncel</a> (6.99.14.27.2.2)
<li> <a href="http://www.visualsaga.com/" rel="nofollow">Visual Saga</a> (6.99.137.2.2)
<li> Vollschauer (6.99.136.99.14)
<li> <a href="http://vxlloyd.com" rel="nofollow">VX Lloyd</a> (6.99.14.37.0)
<li> Wayne Wollesen (6.99.14.37.1.1)
<li> <a href="https://www.facebook.com/weeabooriot" rel="nofollow">Weeabooriot Cosplay</a> (6.99.137.2)
<li> <a href="https://arcaniinteractive.com/" rel="nofollow">William Lucht</a> (7.07.2)
<li> <a href="https://www.facebook.com/weeabooriot" rel="nofollow">Weeabooriot Cosplay</a> (6.99.137.2.2)
<li> <a href="https://arcaniinteractive.com/" rel="nofollow">William Lucht</a> (7.07.2.2)
<li> William Tumeo (6.99.137.1.2)
<li> <a href="http://winterwolves.com/ambersmagicshop.htm" rel="nofollow">Winter Wolves</a> (6.99.137.2)
<li> <a href="https://www.wolfbiteinteractive.com/" rel="nofollow">WolfBite Interactive</a> (7.2)
<li> Y Press Games (7.2)
<li> <a href="http://astralore.com/" rel="nofollow">Yakmala!</a> (6.99.137.2)
<li> <a href="http://winterwolves.com/ambersmagicshop.htm" rel="nofollow">Winter Wolves</a> (6.99.137.2.2)
<li> <a href="https://www.wolfbiteinteractive.com/" rel="nofollow">WolfBite Interactive</a> (7.27.2.2)
<li> Y Press Games (7.27.2.2)
<li> <a href="http://astralore.com/" rel="nofollow">Yakmala!</a> (6.99.137.2.2)
</ul>
+5 -1
View File
@@ -96,7 +96,7 @@ formatting syntax. Ren'Py uses [ to introduce string formatting
because { was taken by text tags.
Along with the ``!s`` and ``!r`` conversion flags supported by Python, Ren'Py
supports ``!q`` and ``!t`` conversion flags. The ``!q`` conversion flag ensures that
supports several more flags. The ``!q`` conversion flag ensures that
text tags are properly quoted, so that displaying a string will not
introduce unwanted formatting constructs. For example::
@@ -112,6 +112,10 @@ The ``!t`` flag will translate the interpolated string::
g "I'm [mood!t] to see you."
The ``!u`` flag forces the text to upper case, the ``!l`` flag forces the
text to lower-case, and the ``!c`` flag capitalizes the text.
Styling and Text Tags
=====================
+1 -1
View File
@@ -1025,7 +1025,7 @@ translate french strings:
# _errorhandling.rpym:663
old "Opens the errors.txt file in a text editor."
new "Ouvrir le fihchier « errors.txt » dans un éditeur de texte."
new "Ouvrir le fichier « errors.txt » dans un éditeur de texte."
# _errorhandling.rpym:667
old "Copies the errors.txt file to the clipboard."
Binary file not shown.

After

Width:  |  Height:  |  Size: 420 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

+1 -1
View File
@@ -1 +1 @@
{"display_name": "The Question", "add_from": true, "renamed_all": true, "renamed_steam": true, "force_recompile": true, "build_update": true, "packages": ["mac", "pc", "linux", "win", "market"], "type": "hidden"}
{"display_name": "The Question", "android_build": "Release", "type": "hidden", "renamed_all": true, "renamed_steam": true, "force_recompile": true, "build_update": true, "packages": ["mac", "pc"], "add_from": true}
+1
View File
@@ -776,6 +776,7 @@ screen preferences():
textbutton "English" text_font "DejaVuSans.ttf"action Language(None)
textbutton "Français" text_font "DejaVuSans.ttf"action Language("french")
textbutton "Русский" text_font "DejaVuSans.ttf" action Language("russian")
textbutton "Español" text_font "DejaVuSans.ttf"action Language("spanish")
# This should be last.
textbutton "Pig Latin" text_font "DejaVuSans.ttf" action Language("piglatin")
+11
View File
@@ -0,0 +1,11 @@

translate spanish strings:
# 01example.rpy:473
old "Copied the example to the clipboard."
new "Se copió el ejemplo al portapapeles."
# 01example.rpy:546
old "copy"
new "copiar"
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,211 @@

# game/indepth_character.rpy:11
translate spanish demo_character_e7e1b1bb:
# e "We've already seen how to define a Character in Ren'Py. But I want to go into a bit more detail as to what a Character is."
e "Ya hemos visto cómo definir Character en Ren'Py, pero quiero entrar en más detalles sobre qué es un Character."
# game/indepth_character.rpy:17
translate spanish demo_character_d7908a94:
# e "Here are couple of additional characters."
e "Aquí hay un par de personajes adicionales."
# game/indepth_character.rpy:19
translate spanish demo_character_275ef8b9:
# e "Each statement creates a Character object, and gives it a single argument, a name. If the name is None, no name is displayed."
e "Cada sentencia crea un objeto Character y le da un solo argumento, un nombre. Si el nombre es None(Ninguno), no se muestra ningún nombre."
# game/indepth_character.rpy:21
translate spanish demo_character_a63aea0c:
# e "This can be followed by named arguments that set properties of the character. A named argument is a property name, an equals sign, and a value."
e "Esto puede ser seguido por argumentos con nombre que establecen las propiedades del personaje. Un argumento con nombre es el nombre de propiedad, un signo igual y un valor."
# game/indepth_character.rpy:23
translate spanish demo_character_636a502e:
# e "Multiple arguments should be separated with commas, like they are here. Let's see those characters in action."
e "Los argumentos múltiples se deben separar con comas, como están aquí. Veamos esos personajes en acción."
# game/indepth_character.rpy:27
translate spanish demo_character_44b54e1d:
# e_shout "I can shout!"
e_shout "Puedo gritar"
# game/indepth_character.rpy:29
translate spanish demo_character_a9646dd8:
# e_whisper "And I can speak in a whisper."
e_whisper "Y puedo hablar susurrando."
# game/indepth_character.rpy:31
translate spanish demo_character_79793208:
# e "This example shows how the name Character is a bit of a misnomer. Here, we have multiple Characters in use, but you see it as me speaking."
e "Este ejemplo muestra cómo el nombre Character es un poco inapropiado. Aquí, tenemos varios Characters en uso, pero los ves como mi hablar."
# game/indepth_character.rpy:33
translate spanish demo_character_5d5d7482:
# e "It's best to think of a Character as repesenting a name and style, rather than a single person."
e "Es mejor pensar que un Character representa un nombre y un estilo, en lugar de una sola persona."
# game/indepth_character.rpy:37
translate spanish demo_character_66d08d98:
# e "There are a lot of properties that can be given to Characters, most of them prefixed styles."
e "Hay muchas propiedades que se pueden asignar a los Characters, la mayoría de ellos con estilos prefijados."
# game/indepth_character.rpy:39
translate spanish demo_character_7e0d75aa:
# e "Properties beginning with window apply to the textbox, those with what apply to the the dialogue, and those with who to the name of Character speaking."
e "Las propiedades que comienzan con window se aplican al cuadro de texto, aquellas con what se aplican al diálogo y aquellas con who al nombre del Character que habla."
# game/indepth_character.rpy:41
translate spanish demo_character_56703784:
# e "If you leave a prefix out, the style customizes the name of the speaker."
e "Si dejas un prefijo, el estilo personaliza el nombre del orador."
# game/indepth_character.rpy:43
translate spanish demo_character_b456f0a9:
# e "There are quite a few different properties that can be set this way. Here are some of the most useful."
e "Hay bastantes propiedades diferentes que se pueden establecer de esta manera. Éstos son algunos de los más útiles."
# game/indepth_character.rpy:54
translate spanish demo_character_18ba073d:
# e1a "If it's set to None, the textbox has no background window."
e1a "Si está configurado como None, el cuadro de texto no tiene ventana de fondo."
# game/indepth_character.rpy:59
translate spanish demo_character_5a26445c:
# e2 "The who_color and what_color properties set the color of the character's name and dialogue text, respectively."
e2 "Las propiedades who_color y what_color establecen el color del texto del nombre y diálogo del personaje, respectivamente."
# game/indepth_character.rpy:61
translate spanish demo_character_88a18c32:
# e2 "The colors are strings containing rgb hex codes, the same sort of colors understood by a web browser."
e2 "Los colores son cadenas que contienen códigos hexadecimales rgb, el mismo tipo de colores que entiende un navegador web."
# game/indepth_character.rpy:67
translate spanish demo_character_ed690751:
# e3 "Similarly, the who_font and what_font properties set the font used by the different kinds of text."
e3 "De manera similar, las propiedades who_font y what_font establecen la fuente utilizada por los diferentes tipos de texto."
# game/indepth_character.rpy:74
translate spanish demo_character_8dfa6426:
# e4 "Setting the who_bold, what_italic, and what_size properties makes the name bold, and the dialogue text italic at a size of 20 pixels."
e4 "La configuración de las propiedades who_bold, what_italic y what_size hace que el nombre aparezca en negrita y el texto del diálogo en cursiva a un tamaño de 20 píxeles."
# game/indepth_character.rpy:76
translate spanish demo_character_20e83c32:
# e4 "Of course, the what_bold, who_italic and who_size properties also exist, even if they're not used here."
e4 "Por supuesto, las propiedades what_bold, who_italic y who_size también existen, incluso si no se usan aquí."
# game/indepth_character.rpy:83
translate spanish demo_character_e4cbb1f2:
# e5 "The what_outlines property puts an outline around the text."
e5 "La propiedad what_outlines coloca un contorno alrededor del texto."
# game/indepth_character.rpy:85
translate spanish demo_character_71535ecf:
# e5 "It's a little complicated since it takes a list with a tuple in it, with the tuple being four things in parenthesis, and the list the square brackets around them."
e5 "Es un poco complicado, ya que toma una lista con una tupla, con la tupla cuatro cosas entre paréntesis, y los corchetes de la lista a su alrededor."
# game/indepth_character.rpy:87
translate spanish demo_character_e9ac7482:
# e5 "The first number is the size of the outline, in pixels. That's followed by a string giving the hex-code of the color of the outline, and the x and y offsets."
e5 "El primer número es el tamaño del contorno, en píxeles. A esto le sigue una cadena que proporciona el código hexadecimal del color del contorno y las compensaciones de x e y."
# game/indepth_character.rpy:93
translate spanish demo_character_ea72d988:
# e6 "When the outline size is 0 and the offsets are given, what_outlines can also act as a drop-shadow behind the text."
e6 "Cuando el tamaño del contorno es 0 y se dan las compensaciones, what_outlines también puede actuar como una sombra detrás del texto."
# game/indepth_character.rpy:99
translate spanish demo_character_8d35ebcd:
# e7 "The what_xalign and what_textalign properties control the alignment of text, with 0.0 being left, 0.5 being center, and 1.0 being right."
e7 "Las propiedades what_xalign y what_textalign controlan la alineación del texto, con 0.0 a la izquierda, 0,5 al centro y 1.0 a la derecha."
# game/indepth_character.rpy:101
translate spanish demo_character_7c75906c:
# e7 "The what_xalign property controls where all the text itself is placed within the textbox, while what_textalign controls where rows of text are placed relative to each other."
e7 "La propiedad what_xalign controla dónde se coloca todo el texto dentro del cuadro de texto, mientras que what_textalign controla dónde se colocan las filas de texto relativas entre sí."
# game/indepth_character.rpy:103
translate spanish demo_character_e2811c1c:
# e7 "Generally you'll want to to set them both what_xalign and what_textalign to the same value."
e7 "Por lo general, querrás establecerlos what_xalign y what_textalign en el mismo valor."
# game/indepth_character.rpy:105
translate spanish demo_character_baa52234:
# e7 "Setting what_layout to 'subtitle' puts Ren'Py in subtitle mode, which tries to even out the length of every line of text in a block."
e7 "Al establecer what_layout en 'subtitle' pones a Ren'Py en el modo de subtítulos, que intenta igualar la longitud de cada línea de texto en un bloque."
# game/indepth_character.rpy:110
translate spanish demo_character_41190f01:
# e8 "These properties can be combined to achieve many different effects."
e8 "Estas propiedades se pueden combinar para lograr muchos efectos diferentes."
# game/indepth_character.rpy:124
translate spanish demo_character_aa12d9ca:
# e8 "This example hides the background and shows dialogue centered and outlined, as if the game is being subtitled."
e8 "Este ejemplo oculta el fondo y muestra el diálogo centrado y delineado, como si el juego estuviera subtitulado."
# game/indepth_character.rpy:133
translate spanish demo_character_a7f243e5:
# e9 "There are two interesting non-style properties, what_prefix and what_suffix. These can put text at the start and end of a line of dialogue."
e9 "Hay dos propiedades interesantes que no son de estilo, what_prefix y what_suffix. Estos pueden poner texto al principio y al final de una línea de diálogo."
# game/indepth_character.rpy:139
translate spanish demo_character_f9b0052f:
# e "By using kind, you can copy properties from one character to another, changing only what you need to."
e "Al usar kind, puede copiar las propiedades de un personaje a otro, cambiando solo lo que necesitas."
# game/indepth_character.rpy:148
translate spanish demo_character_6dfce4b7:
# l8 "Like this! Finally I get some more dialogue around here."
l8 "¡Me gusta esto! Finalmente consigo un poco más de diálogo por aquí."
# game/indepth_character.rpy:157
translate spanish demo_character_68d9e46c:
# e "The last thing you have to know is that there's a special character, narrator, that speaks narration. Got it?"
e "Lo último que debes saber es que hay un personaje especial, narrador, que habla narración. ¿Entendido?"
# game/indepth_character.rpy:159
translate spanish demo_character_0c8f314a:
# "I think I do."
"Eso creo."
# game/indepth_character.rpy:48
translate spanish demo_character_31ace18e:
# e1 "The window_background property sets the image that's used for the background of the textbox, which should be the same size as the default in gui/textbox.png."
e1 "La propiedad window_background establece la imagen que se utiliza para el fondo del cuadro de texto, que debe tener el mismo tamaño que el predeterminado en gui/textbox.png."

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