Compare commits

...

128 Commits

Author SHA1 Message Date
Tom Rothamel 366a09d708 Changes to get Ren'Py compiling with pygame_sdl2/sdl2. 2014-11-04 21:15:41 -05:00
Tom Rothamel 3fe3df9f23 Link to translation best practices. 2014-10-22 22:46:21 -04:00
Tom Rothamel e942545e39 changelog: Translation updates. 2014-10-21 22:33:09 -04:00
Tom Rothamel 9fe39eb8f3 Update changelog for 6.18.3. 2014-10-21 22:27:52 -04:00
Tom Rothamel b1b17bd9cc Remove translation TODOs. 2014-10-20 21:38:32 -04:00
Tom Rothamel 1b34b59f5e Merge pull request #482 from civalin/i10n/zh_TW
Traditional Chinese translation for launcher updated.
2014-10-20 21:36:25 -04:00
Tom Rothamel 719f06339c Merge pull request #481 from tlm-2501/patch-2
Updated Russian translation for preferences.rpy.
2014-10-20 21:36:07 -04:00
Tom Rothamel 7abba42bca Document showif. 2014-10-20 21:32:44 -04:00
Tom Rothamel c1120fc5b2 transform: Apply time adjustments on _hide, as well as render.
Fixes #484.

See: http://lemmasoft.renai.us/forums/viewtopic.php?p=343556#p343556
2014-10-19 23:07:10 -04:00
Tom Rothamel 7d29b8968a When in self-voicing mode, ignore {w} and {p} text tags.
These (especially the timed variants) tend to disrupt
text-to-speech. It's best to just have them speak in a
single block, whih is closest to the human speech pattern.

Fixes #483.
2014-10-19 11:40:46 -04:00
Tom Rothamel 994adf2ca5 Add the renpy.invoke_in_thread function. 2014-10-18 15:10:17 -04:00
Tom Rothamel bd55020512 Add updater.UpdateVersion.
This is a function that contacts an update server and determines
if there is an update available. It does this in a background thread,
so as to not make the game unresponsive.
2014-10-18 15:02:45 -04:00
Tom Rothamel b4f7d641b0 showif: Integrate with screen language.
This gives us a showif statement that applies the ShowIf displayable
to its children.
2014-10-17 23:12:13 -04:00
Tom Rothamel 1197cff85b showif: Rewrite showif.
We now simply deliver "appear", "show", and "hide" to the child,
rather than trying to exploit _hide. The advantage of doing it this
way is that we can preserve the hide state going into show, without
having to transfer it between transforms.
2014-10-17 22:15:53 -04:00
Tom Rothamel 5e8fc44fb3 Maintain hide_response and replaced_response over all transform renders.
This lets us simply query a transform to determine if it's blocking
hide/replaced, rather than having to update it in an unusual way.
2014-10-17 22:14:53 -04:00
Tom Rothamel 5df33b0f56 Only force us to stay in hide/replaced if a request happens.
This lets us issue "hide" to a transform by normal means, and override
it later.
2014-10-17 22:13:56 -04:00
Civa Lin fc9e826976 i10n: Traditional Chinese language for launcher updated. 2014-10-18 09:50:36 +08:00
Civa Lin e80d6f95a9 Merge branch 'master' into i10n/zh_TW 2014-10-18 09:47:33 +08:00
tlm-2501 8815e5efb8 Updated Russian translation for preferences.rpy. 2014-10-17 21:31:08 +04:00
Tom Rothamel 9874bc4dc3 WiP of ShowIf. 2014-10-16 22:54:22 -04:00
Tom Rothamel 82b597f62b Fix {Set,Toggle}ScreenVariable.
These functions were marked as pure, but weren't actually pure
functions, since the scope they update can change. This change
has them return objects, which are pure.
2014-10-16 22:41:31 -04:00
Tom Rothamel 4b35493447 The ATL on statement now takes a list of event names.
This will be useful for the upcoming showif displayable, which might
be used with transforms that contain "on start, idle:".
2014-10-15 20:17:28 -04:00
Tom Rothamel 69b203fbaa doc: Add warning about show layer and transform state.
Fixes #479.
2014-10-14 18:05:46 -04:00
Tom Rothamel 875a585e90 sl2: Use a newly-created context when predicting SLIf.
We had incorrectly used the original context, which led to the
contents of the predicted if statement showing up if the
statement was reused:

See: http://lemmasoft.renai.us/forums/viewtopic.php?f=32&t=28793
2014-10-14 17:46:32 -04:00
Tom Rothamel 935d065712 Transfer focus based on displayable replacement.
When a displayable in a screen that has focus/grab is replaced,
we transfer the focus to its replacement. This fixes a problem where
we would transfer grab only, which could lead to a permagrab condition.

Fixes #477.
2014-10-14 00:32:05 -04:00
Tom Rothamel c0c44896a8 Add _composite_parts for composite displayables.
For composite displayables (those with _main) set, this gives a list
of all components, other than the root widget, that make up the
displayable.
2014-10-13 23:55:40 -04:00
Tom Rothamel b7a9266bf3 Update changelog with new prediction stuff. 2014-10-12 13:26:42 -04:00
Tom Rothamel 8bf5f05270 While at the main menu, predict the start label.
This should provide a better experience in the start-new-game case,
which is the first thing a player will see.

Fixes #478.
2014-10-12 12:43:24 -04:00
Tom Rothamel 996d3c2fd6 Improve prediction of call/return.
We now model the return stack as part of the prediction process,
making it possible to perform prediction through a call and return
pair.

Fixes #288.
2014-10-12 01:30:01 -04:00
Tom Rothamel f68a4caec2 Check Side for correct parameters and children.
Fixes #476.
2014-10-11 22:17:44 -04:00
Tom Rothamel f06e5861e7 Bump versio to 6.18.3. 2014-10-11 17:13:07 -04:00
Daniel Luque a93f4f1f76 Merge pull request #475 from LuqueDaniel/update-es
Update es
2014-10-11 15:30:40 +02:00
Daniel Luque 572054c6e1 Update: templates/spanish/game/tl/none/common.rpy 2014-10-11 15:24:15 +02:00
Daniel Luque bc3f659f4b Update: launcher/game/tl/spanish/common.rpy 2014-10-11 15:19:24 +02:00
Tom Rothamel db63d0db4e Update changelog. 2014-10-09 22:28:02 -04:00
Tom Rothamel e7424e1140 Merge pull request #474 from civalin/i10n/zh_TW
Add Traditional Chinese translation.
2014-10-09 17:07:53 -04:00
Tom Rothamel c345b6a84c android: Log call errors to android.txt. 2014-10-08 23:21:02 -04:00
Tom Rothamel 5fd9bbe651 Pop dynamic when a lookup_return fails. 2014-10-08 20:44:01 -04:00
Tom Rothamel d19b49d418 Restore the current screen using try/catch.
This fixes a bug where the current screen could be set at an
inopportune time, causing crashes.

See: https://gist.github.com/vaendryl/97fb82b9cbee8bda0777
2014-10-08 20:17:00 -04:00
Tom Rothamel f5103f4bf9 Update the changelog. 2014-10-07 23:34:02 -04:00
Tom Rothamel dc5357c7f2 Increase the image cache size to 16 screens.
We should be able to afford this, and computers have more RAM than they used to.
2014-10-07 23:26:26 -04:00
Tom Rothamel 61f940a2f2 Config changes:
* Increase the predict lookahead.
* Make RENPY_DEBUG_IMAGE_CACHE set the default value of config.debug_image_cache.
2014-10-07 22:26:37 -04:00
Tom Rothamel b38d0edb2c Better prediction of DynamicDisplayable.
If no predict function exists, the regular DD function is called,
and what it returns is now predicted.
2014-10-07 22:19:40 -04:00
Tom Rothamel 36834b094d Ensure rpyfn is defined when loading .rpyc files. 2014-10-07 20:12:27 -04:00
Civa Lin cefdc342b6 i10n: Traditional Chinese language for template. 2014-10-07 15:54:04 +08:00
Civa Lin 1c409025e1 i10n: Traditional Chinese language for launcher 2014-10-07 15:41:41 +08:00
Tom Rothamel 29cbac1c95 Ensure that yes_thread is always defined in AndroidInterface.
Fixes the immediate problems in #472.
2014-10-06 22:16:12 -04:00
Tom Rothamel 596cc4e6b6 Display an indicator when self-voicing is enabled.
This prevents people from getting stuck in self-voicing, and reporting
it as a bug.

Fixes #471.
2014-10-06 21:46:58 -04:00
Tom Rothamel 19d52e85f1 Update docs/changelog. 2014-10-05 21:33:30 -04:00
Tom Rothamel 75789bb8f2 memory: Add the renpy.profile_rollback() function.
This profiles the amount of memory used by the rollback system.

Fixes #470.
2014-10-05 21:13:55 -04:00
Tom Rothamel 55c00a86aa memory: Ignore the renpy.store module.
It's an alias of the store module, and we want to use the store name
to make it clear what's part of Ren'Py and what's part of the game.
2014-10-05 13:35:32 -04:00
Tom Rothamel 3f4f86f0fd memory: Try to speed up profiling. 2014-10-05 00:34:54 -04:00
Tom Rothamel 81c3e0c28e memory: Use gc.get_referents to walk objects.
This is simpler and more accurage.
2014-10-05 00:12:48 -04:00
Tom Rothamel dc058298f7 memory: Add the renpy.diff_memory() function.
This prints out the difference in memory usage since the last
time it was called.
2014-10-05 00:02:25 -04:00
Tom Rothamel 4609a1139c memory: Add the renpy.profile_memory() function.
This is a function that dumps a profile of memory to memory.txt (and
stdout if it's a tty.)
2014-10-04 23:27:39 -04:00
Tom Rothamel 9b5cb37b74 Rewrite the memory profile.
We now use sys.getsizeof to consider the actual object size,
rather than simply the number of objects that exist.
2014-10-04 22:03:09 -04:00
Tom Rothamel ff0893155d focused -> mouse_focused in swdraw. 2014-10-04 17:59:45 -04:00
Tom Rothamel 127f46c1d9 Remove unnecessary translation TODOs. 2014-10-03 23:09:35 -04:00
Tom Rothamel 2edea5b052 Write script to remove translation TODOs. 2014-10-03 23:09:14 -04:00
Tom Rothamel a256ed1c1e Merge pull request #469 from renoa/patch-7
Update common.rpy
2014-10-03 23:01:01 -04:00
Tom Rothamel 54d8b5733c Merge pull request #468 from renoa/patch-6
Update front_page.rpy
2014-10-03 23:00:51 -04:00
Tom Rothamel d59e6d0d45 Merge pull request #467 from renoa/patch-5
Update preferences.rpy
2014-10-03 23:00:44 -04:00
Tom Rothamel 66d3fbd6b0 Merge pull request #466 from renoa/patch-4
Update project.rpy
2014-10-03 23:00:36 -04:00
Tom Rothamel f83f217385 Merge pull request #459 from tlm-2501/patch-1
Updated translations in front_page.rpy.
2014-10-03 23:00:07 -04:00
Tom Rothamel c0b6d2dd3e Merge pull request #451 from renoa/patch-3
Update editor.rpy
2014-10-03 22:59:56 -04:00
Tom Rothamel 42e6d3f368 sl2: Properly analyze the iteration variable of a for loop.
Since it's defined in the loop, it should be at most a local constant,
not a global constant.
2014-10-02 23:27:36 -04:00
Tom Rothamel 9e3d1db244 Do not cache renders when sizing size_groups. 2014-10-02 21:21:05 -04:00
Tom Rothamel 8f0d132104 Ignore tts errors.
Rather than letting them crash the game.
2014-10-02 20:11:57 -04:00
Tom Rothamel 3813f0dc26 Improve reporting of duplicated labels from renamed .rpycs.
Fixes #452.
2014-10-01 20:56:52 -04:00
Renoa 1cb04cd09f Update common.rpy 2014-10-01 10:34:02 +03:00
Tom Rothamel fa8d11a13c Check return value of l.rest() when an expression/statement is expected.
This prevents problems where we let a None pass into some other
part of the code:

See: <00b201cfdc7e$c7d24530$5776cf90$@gmail.com>
2014-09-30 20:28:49 -04:00
Renoa c9c74ec020 Update common.rpy
translation
2014-09-30 23:13:06 +03:00
Renoa cccd957545 Update front_page.rpy
translation
2014-09-30 23:10:14 +03:00
Renoa 938a6f3fa8 Update preferences.rpy
translation
2014-09-30 23:06:31 +03:00
Renoa ad2763a460 Update project.rpy
translation
2014-09-30 23:04:11 +03:00
Tom Rothamel 779912dd80 Update Play.selected when get_selected() to be recalled.
This fixes a problem where selected would never become correct.
2014-09-29 22:57:46 -04:00
Tom Rothamel f058d56d0f Make Play's participation in get_selected optional.
It doesn't make sense for Play on the sound channel to participate
in get_selected(), since it's very likely the sound will quickly
end.

See: http://lemmasoft.renai.us/forums/ucp.php?i=pm&mode=view&f=0&p=148211
where Play causes Ren'Py to restart the interaction repeatedly.
2014-09-29 22:42:36 -04:00
Tom Rothamel c081ad9f78 Add renpy.music.is_music().
An internal function that returns true if a channel is a music
channel, and false otherwise.
2014-09-29 22:36:25 -04:00
Tom Rothamel bbd505b378 Add a debug style property.
This is meant for Ren'Py developer use. We can look at d.style.debug
to debug a displayable.
2014-09-29 20:11:58 -04:00
Tom Rothamel 1382c299fb Mousearea now respects focus_mask.
This makes it possible to have non-rectangular mouseareas.

Fixes #457.
2014-09-28 20:53:53 -04:00
Tom Rothamel 84429c3826 Convert focus_mask into a displayable in the style system.
This allows for render caching as necessary.
2014-09-28 20:40:47 -04:00
Tom Rothamel 971e33aa97 Remove spurious debug print. 2014-09-27 21:39:02 -04:00
Tom Rothamel 9cf92c7f34 Add keyboard_focus style property.
Setting this property to False hides a button, bar, or hotspot from the
keyboard focus mechanism. This might be useful for games where we
want to hide interface from the user, like hidden object games.
2014-09-27 21:34:36 -04:00
Tom Rothamel 20ca7fb463 Random doc fixes / generated code. 2014-09-26 21:49:15 -04:00
Tom Rothamel 5834069ddf Document the character named store.
Fixes #422.
2014-09-26 21:48:26 -04:00
Tom Rothamel ee2dc4aaea Make the define statement take an optional namespace.
This is intended to let us do:

   define character.e = Character("Eileen")
2014-09-26 21:22:46 -04:00
Tom Rothamel 7057ce19a1 The say statement looks for characters in the character namespace.
This lets us have a character with the same name as an object or
variable.
2014-09-26 20:39:10 -04:00
Tom Rothamel a190738a24 Add functions to get/set the return stack.
Fixes #464.
2014-09-25 23:27:26 -04:00
Tom Rothamel 60a9589a80 Deal with formats SDL_image can't reentrantly load.
It seems like SDL_image has problems reentrantly loading some
formats, certainly GIF and maybe TGA and BMP. So we whitelist
the common formats (png and jpeg), and lock loads of the rest.

Fixes #426.
2014-09-24 21:26:20 -04:00
Tom Rothamel 2ea6291b0d Add the crop_relative transform property.
When true, makes crop relative to the size of the image.
2014-09-24 00:42:36 -04:00
Tom Rothamel 4bd9c96d9c Bump version to 6.18.2. 2014-09-23 19:06:11 -04:00
Tom Rothamel 46c1f8067c Merge pull request #461 from javimat/master
Update Spanish template
2014-09-22 10:02:03 -04:00
javimat 4deae69732 Update screens.rpy
...from the English template. With parameters in screens definitions.
2014-09-22 15:34:02 +02:00
javimat ac09cb8363 Update options.rpy
Delete English explanations (redundant)
2014-09-22 15:32:22 +02:00
Tom Rothamel b397f4b235 Stop using the RENPY_BASE environment variable.
Fixes #460.
2014-09-21 17:37:04 -04:00
Tom Rothamel 389749b221 Use redraw() to handle invalidation.
They're really the same operation, except invalidation can be
run in the middle of a render. So let's check for that, and
use a single codepath.

This fixes a bug where when something was shown twice, the
invalidate could not take.

See: twelvethousand
2014-09-21 15:51:47 -04:00
Tom Rothamel 2da32702fe Revert "Force the animation timebase for ATL in show layer."
It's no longer necessary, now that we're only adjusting
ATL time in screens.

This reverts commit 72027f7f69.
2014-09-21 00:24:29 -04:00
Tom Rothamel f6b2161ac9 Run IAP actions in a new context.
The actions (at least the purchase action) requires us be interacting,
so we can go to sleep and let play services take over to handle the
actual transaction.

This requires us to interact in the middle of a callback, so we do
an invoke_in_new_context to make that possible.
2014-09-21 00:22:50 -04:00
Tom Rothamel 6343c2cd6b Make ATL time adjustments require take_execution_state.
The ATL time adjustments only make sense after take_execution_state.
If we're in a context where take_execution_state is never called,
we don't want to do the time adjustments.

So now, we make it a requirement that take_execution_state be called
before doing time adjustments.
2014-09-20 21:52:51 -04:00
Tom Rothamel 7960b6b7be Use only the index to look up entries in an SLFor's cache.
Using the object id proved to be error prone when new objects were
created during each iteration of the list (something we support).
This also returns the behavior to what it was in 6.17.

See: Seasons of the Wolf.
2014-09-20 19:50:16 -04:00
tlm-2501 f9875d9bad Updated translations in front_page.rpy. 2014-09-21 00:35:44 +04:00
Tom Rothamel 72027f7f69 Force the animation timebase for ATL in show layer.
We really should be propagating the transform state, but there
isn't really a good way to do that at the moment. (It isn't clear
where we'd store the old state.)

This is a reasonable, if ugly, workaround.

See: http://lemmasoft.renai.us/forums/ucp.php?i=pm&mode=view&f=0&p=147158
2014-09-19 23:00:38 -04:00
Tom Rothamel da532392d9 Fix load of .rpycs during .rpy compilation.
This fixes various missing label problems.
2014-09-19 22:21:07 -04:00
Tom Rothamel b1a392e171 Properly check that all parameters have been consumed.
And do it right this time.
2014-09-19 18:47:48 -04:00
Tom Rothamel 252baf8f1e Add renpy.munge()
This is a function that munges a string beginning with __ in the
same manner that Ren'Py munges names when parsing a file.

    jump __test

and:

    jump expression renpy.munge("__test")
2014-09-18 19:53:01 -04:00
Tom Rothamel 75d41125a1 Do not predict the main_menu image.
We don't define one by default, so if the user is debugging
prediction, this will fail.
2014-09-18 19:33:31 -04:00
Tom Rothamel 76c7c2aa86 atl: Global const transforms do not need parameters to be compiled.
This prevents an error we introduced in the last release.
2014-09-18 13:44:32 -04:00
Tom Rothamel 61466cf927 Update docs. 2014-09-18 00:09:40 -04:00
Tom Rothamel 2befad5da1 Fix check of atl parameters before compile. 2014-09-18 00:00:47 -04:00
Tom Rothamel c1c046788b Add an sv character that can be used to self-voice descriptive text. 2014-09-17 23:55:41 -04:00
Tom Rothamel 1af82edd66 sl2: Propagate state into transitions added with add.
This requires a recompile.
2014-09-17 22:12:25 -04:00
Tom Rothamel 6a9835ad43 Bump version and update changelog. 2014-09-16 23:30:03 -04:00
Tom Rothamel 5402d84831 Add german template. 2014-09-16 23:28:24 -04:00
Tom Rothamel b30e7de686 sl2: Update screens that are already showing.
This matches the SL1 behavior.
2014-09-16 22:56:48 -04:00
Tom Rothamel 312d64b361 iap: Unconditionally restore purchases @ start. 2014-09-16 17:07:18 -04:00
Tom Rothamel 8bfb4d9f00 Update the who Text after display_say changes its properties.
This is necessary to make the properties take effect after the layout
has already been built.
2014-09-15 22:49:13 -04:00
Tom Rothamel 7253663eff sl2: Displayables with id can't be constant.
That's because they may be given arguments from outside the screen that
sl2 doesn't know about. Better safe then sorry, especially in a rare
case.
2014-09-15 22:47:37 -04:00
Tom Rothamel df6e8db1f1 Document Placeholder. 2014-09-15 20:20:47 -04:00
Tom Rothamel 7bc51c30db Properly document the third argument to edgescrill.
Fixes #447.
2014-09-15 20:09:47 -04:00
Tom Rothamel d2aa4adfe4 ATL transforms now begin executing when first shown.
Previously, ATL transforms began executing when the screen containing
the ATL transform was first shown, which meant that if the transform
changed over the course of screen display, a portion of the new
transform would be skipped.
2014-09-15 19:54:38 -04:00
Tom Rothamel f29df20878 script: Commit to loading a file when it parses/unpickles.
This prevents an error that was caused by when an exception in
finish_load would cause the .rpy file to load after finish_load
had already started updating data structures.
2014-09-15 00:45:46 -04:00
Tom Rothamel 57e935e1f4 Reorder methods in Script. 2014-09-15 00:12:21 -04:00
Tom Rothamel 88cb519b4d atl: Properly compute const-ness for a function statement.
Fixes #449.
2014-09-14 09:42:52 -04:00
Renoa 4a9becb351 Update editor.rpy
translated some lines
2014-09-14 14:44:24 +03:00
Tom Rothamel 0a4e788349 Use Placeholders for missing images. 2014-09-13 19:02:39 -04:00
Tom Rothamel ed9f598098 placeholder: Add Placeholder displayable.
This displays a placeholder image. The image displayed may be chosen
by the creator, or automatically determined based on the name of the
image being placeheld.
2014-09-13 18:39:30 -04:00
Tom Rothamel 529bb95a43 Ensure hiding is set on legacy ScreenDisplayables. 2014-09-12 21:37:55 -04:00
Tom Rothamel d8a945095e Ensure hiding is always an attribute of screens.
Fixes compatibility with 6.17.

See: Lovely Little Thieves 0.5
2014-09-12 21:05:39 -04:00
142 changed files with 10317 additions and 1216 deletions
+10 -1
View File
@@ -7,6 +7,7 @@ http://www.renpy.org
Ren'Py development takes place on the ``master`` branch, and occasionally
on feature branches.
Getting Started
===============
@@ -132,10 +133,18 @@ For example::
renpy.engine.warp_drive.engage(factor)
Translating
===========
For best practices when it comes to translating the launcher and template
game, please read:
http://lemmasoft.renai.us/forums/viewtopic.php?p=321603#p321603
Contributing
============
For bug fixes, documentation improvements, and simple changes, just
make a pull request. For more complex changes, it might make sense
to file an issue first so we can discuss the design.
+9 -1
View File
@@ -234,6 +234,8 @@ init python:
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
self.yes_thread = None
try:
interface.processing(self.info_msg, show_screen=True, cancel=cancel_action)
@@ -241,7 +243,12 @@ init python:
if yes:
kwargs["stdin"] = subprocess.PIPE
self.process = subprocess.Popen(cmd, cwd=RAPT_PATH, stdout=f, stderr=f, startupinfo=startupinfo, **kwargs)
try:
self.process = subprocess.Popen(cmd, cwd=RAPT_PATH, stdout=f, stderr=f, startupinfo=startupinfo, **kwargs)
except:
import traceback
traceback.print_exc(file=f)
raise
if yes:
import threading
@@ -260,6 +267,7 @@ init python:
self.yes_thread.join()
self.process = None
self.yes_thread = None
def check_process(self):
rv = self.process.poll()
-1
View File
@@ -1,4 +1,3 @@
# TODO: Translation updated at 2013-04-30 07:54
translate arabic strings:
-1
View File
@@ -1,4 +1,3 @@
# TODO: Translation updated at 2013-04-30 07:54
translate arabic strings:
-1
View File
@@ -1,4 +1,3 @@
# TODO: Translation updated at 2013-04-30 07:54
translate arabic strings:
+8
View File
@@ -518,3 +518,11 @@ translate arabic strings:
old "<repr() failed>"
new "<repr() failed>"
# Translation updated at 2014-09-30 23:01
translate arabic strings:
# renpy/common/_developer/inspector.rpym:80
old "Location"
new "الموقع"
@@ -1,4 +1,3 @@
# TODO: Translation updated at 2013-04-30 07:54
translate arabic strings:
+7 -7
View File
@@ -1,14 +1,14 @@
# TODO: Translation updated at 2013-04-30 07:54
# Translation updated at 2013-04-30 07:54
translate arabic strings:
# game/editor.rpy:120
old "{b}Recommended.{/b} A beta editor with an easy to use interface and features that aid in development, such as spell-checking. Editra currently lacks the IME support required for Chinese, Japanese, and Korean text input."
new "{b}Recommended.{/b} A beta editor with an easy to use interface and features that aid in development, such as spell-checking. Editra currently lacks the IME support required for Chinese, Japanese, and Korean text input."
new "{b}نقترح.{/b} محرر نص له واجهة سهلة الاستعمال ويعين على كتابة النصوص البرمجية يفضل برنامج يحتوي على مدقق لغوي. Editraحالياً لا يدعم اللغات الأجنبية مثل اللغه الكورية و الصينية و اليابانية."
# game/editor.rpy:121
old "{b}Recommended.{/b} A beta editor with an easy to use interface and features that aid in development, such as spell-checking. Editra currently lacks the IME support required for Chinese, Japanese, and Korean text input. On Linux, Editra requires wxPython."
new "{b}Recommended.{/b} A beta editor with an easy to use interface and features that aid in development, such as spell-checking. Editra currently lacks the IME support required for Chinese, Japanese, and Korean text input. On Linux, Editra requires wxPython."
new "{b}نقترح.{/b} محرر نص له واجهة سهلة الاستعمال ويعين على كتابة النصوص البرمجية يفضل برنامج يحتوي على مدقق لغوي. Editraحالياً لا يدعم اللغات الأجنبية مثل اللغه الكورية و الصينية و اليابانية. على نظام لينوكس, Editra يحتاج wxPython."
# game/editor.rpy:137
old "The may have occured because wxPython is not installed on this system."
@@ -16,11 +16,11 @@ translate arabic strings:
# game/editor.rpy:144
old "Up to 22 MB download required."
new "Up to 22 MB download required."
new "مطلوب تحميل ملف بحجم 22 ميغا بايت."
# game/editor.rpy:157
old "1.8 MB download required."
new "1.8 MB download required."
new "مطلوب تحميل ملف بحجم 1.8 ميغا بايت."
# game/editor.rpy:158
old "This may have occured because Java is not installed on this system."
@@ -36,13 +36,13 @@ translate arabic strings:
# game/editor.rpy:393
old "A text editor is the program you'll use to edit Ren'Py script files. Here, you can select the editor Ren'Py will use. If not already present, the editor will be automatically downloaded and installed."
new ""
new "محرر النصوص هو برنامج يساعدك على تعديل ملفات رينباي البرمجية والحوار. هنا, يمكنك اختيار المحرر الذي سيستعلمه رينباي. إذا لم يكن لديك مسبقاً, سيتم تحميله و تنصيبه بشكل اوتوماتيكي."
# game/editor.rpy:415
old "Cancel"
new "إلغاء الامر"
# TODO: Translation updated at 2013-11-17 23:18
# Translation updated at 2013-11-17 23:18
translate arabic strings:
+20
View File
@@ -94,3 +94,23 @@ translate arabic strings:
old "[p.name!q] (template)"
new "[p.name!q] (template)"
# Translation updated at 2014-09-30 23:01
translate arabic strings:
# game/front_page.rpy:35
old "Open [text] directory."
new "فتح المجلد [text]"
# game/front_page.rpy:150
old "Select project [text]."
new "اختر المشروع [text]"
# game/front_page.rpy:234
old "Force Recompile"
new "إعادة حزم الملفات"
# game/front_page.rpy:285
old "Recompiling all rpy files into rpyc files..."
new "يتم إعادة حزم الملفات من صيغة rpy إلى rpyc..."
-2
View File
@@ -1,4 +1,3 @@
# TODO: Translation updated at 2013-04-30 07:54
translate arabic strings:
@@ -82,7 +81,6 @@ translate arabic strings:
old "QUESTION"
new "سؤال"
# TODO: Translation updated at 2013-11-17 23:18
translate arabic strings:
-1
View File
@@ -1,4 +1,3 @@
# TODO: Translation updated at 2013-04-30 07:54
translate arabic strings:
+17 -1
View File
@@ -58,7 +58,7 @@ translate arabic strings:
old "Language:"
new "اللغة:"
# : Translation updated at 2014-04-17 13:01
# Translation updated at 2014-04-17 13:01
translate arabic strings:
@@ -66,3 +66,19 @@ translate arabic strings:
old "Show templates"
new "عرض القوالب"
# Translation updated at 2014-09-30 23:01
translate arabic strings:
# game/preferences.rpy:91
old "Projects directory: [text]"
new "مجلد المشاريع [text]"
# game/preferences.rpy:114
old "Text editor: [text]"
new "محرر النصوص [text]"
# game/preferences.rpy:171
old "Large fonts"
new "خط كبير"
+8
View File
@@ -58,3 +58,11 @@ translate arabic strings:
old "Ren'Py was unable to run python with tkinter to choose the projects directory. Please install the python-tk or tkinter package."
new "رينباي لم يتمكن من تشغيل بايثون مع tkinter لكي يختار مجلد المشاريع, الرجاء تنصيب Python-tk او tkinter"
# Translation updated at 2014-09-30 23:01
translate arabic strings:
# game/project.rpy:47
old "Have you backed up your projects recently?"
new "هل قمت بعمل نسخة احتياطية من مشاريعك مؤخراً؟"
-2
View File
@@ -1,4 +1,3 @@
# TODO: Translation updated at 2013-04-30 07:54
translate arabic strings:
@@ -22,7 +21,6 @@ translate arabic strings:
old "Ren'Py has finished generating [language] translations."
new "انتهى رينباي من صناعة ملفات الترجمة إلى [language]"
# TODO: Translation updated at 2013-11-17 23:18
translate arabic strings:
-2
View File
@@ -166,7 +166,6 @@ translate german strings:
old "It looks like you're ready to start packaging games."
new "Es sieht aus, als wären Sie bereit, Spiele zu erstellen."
# TODO: Translation updated at 2014-05-02 13:54
translate german strings:
@@ -179,7 +178,6 @@ translate german strings:
old "Japanese"
new "Japanese"
# TODO: Translation updated at 2014-05-02 14:07
translate german strings:
-1
View File
@@ -451,7 +451,6 @@ translate japanese strings:
old "Done"
new "完了"
# TODO: Translation updated at 2014-07-08 18:59
translate japanese strings:
-13
View File
@@ -10,7 +10,6 @@ translate korean strings:
old "Quick save complete."
new "퀵세이브했습니다."
# TODO: Translation updated at 2014-05-02 21:49
translate korean strings:
@@ -82,7 +81,6 @@ translate korean strings:
old "jump <label>: jumps to label"
new "jump <label>: label로 점프한다"
# TODO: Translation updated at 2014-05-02 21:49
translate korean strings:
@@ -106,7 +104,6 @@ translate korean strings:
old "return"
new "돌아가기"
# TODO: Translation updated at 2014-05-02 21:49
translate korean strings:
@@ -202,7 +199,6 @@ translate korean strings:
old "Restart"
new "다시 시작"
# TODO: Translation updated at 2014-05-02 21:49
translate korean strings:
@@ -214,7 +210,6 @@ translate korean strings:
old "Autoreload"
new "자동 다시 불러오기"
# TODO: Translation updated at 2014-05-02 21:49
translate korean strings:
@@ -254,7 +249,6 @@ translate korean strings:
old "Are you sure you want to skip to unseen dialogue or the next choice?"
new "읽지 않은 대사나 다음 선택지가 나타날 때까지 스킵하겠습니까?"
# TODO: Translation updated at 2014-05-02 21:49
translate korean strings:
@@ -266,7 +260,6 @@ translate korean strings:
old "Fast Skip Mode"
new "고속 스킵 모드"
# TODO: Translation updated at 2014-05-02 21:49
translate korean strings:
@@ -306,7 +299,6 @@ translate korean strings:
old "The updates were cancelled."
new "업데이트가 취소되었습니다."
# TODO: Translation updated at 2014-05-02 21:49
translate korean strings:
@@ -322,7 +314,6 @@ translate korean strings:
old "Next"
new "앞으로"
# TODO: Translation updated at 2014-05-02 21:49
translate korean strings:
@@ -330,7 +321,6 @@ translate korean strings:
old "Joystick Mapping"
new "조이스틱 설정"
# TODO: Translation updated at 2014-05-02 21:49
translate korean strings:
@@ -398,7 +388,6 @@ translate korean strings:
old "Done"
new "완료"
# TODO: Translation updated at 2014-05-02 21:49
translate korean strings:
@@ -438,7 +427,6 @@ translate korean strings:
old "<repr() failed>"
new "<repr() 실패>"
# TODO: Translation updated at 2014-05-02 21:49
translate korean strings:
@@ -494,7 +482,6 @@ translate korean strings:
old "Opens the errors.txt file in a text editor."
new "errors.txt 파일을 스크립트 에디터로 엽니다."
# TODO: Translation updated at 2014-05-02 21:49
translate korean strings:
-10
View File
@@ -14,7 +14,6 @@ translate russian strings:
old "q"
new "q"
# TODO: Translation updated at 2013-12-08 21:52
translate russian strings:
@@ -22,7 +21,6 @@ translate russian strings:
old "Joystick Mapping"
new "Назначение джойстика"
# TODO: Translation updated at 2013-12-08 21:52
translate russian strings:
@@ -34,7 +32,6 @@ translate russian strings:
old "Quick save complete."
new "Быстрое сохранение завершено."
# TODO: Translation updated at 2013-12-08 21:52
translate russian strings:
@@ -74,7 +71,6 @@ translate russian strings:
old "Are you sure you want to skip to unseen dialogue or the next choice?"
new "Вы уверены, что хотите пропустить все до невиданного диалога или следующего выбора?"
# TODO: Translation updated at 2013-12-08 21:52
translate russian strings:
@@ -86,7 +82,6 @@ translate russian strings:
old "Next"
new "Следующий"
# TODO: Translation updated at 2013-12-08 21:52
translate russian strings:
@@ -94,7 +89,6 @@ translate russian strings:
old "Saved screenshot as %s."
new "Скришнот сохранен как %s."
# TODO: Translation updated at 2013-12-08 21:52
translate russian strings:
@@ -134,7 +128,6 @@ translate russian strings:
old "The updates were cancelled."
new "Обновления были отменены."
# TODO: Translation updated at 2013-12-08 21:52
translate russian strings:
@@ -230,7 +223,6 @@ translate russian strings:
old "Restart"
new "Перезапустить"
# TODO: Translation updated at 2013-12-08 21:52
translate russian strings:
@@ -254,7 +246,6 @@ translate russian strings:
old "return"
new "вернуться"
# TODO: Translation updated at 2013-12-08 21:52
translate russian strings:
@@ -310,7 +301,6 @@ translate russian strings:
old "Opens the errors.txt file in a text editor."
new "Открывает файл errors.txt в текстовом редакторе."
# TODO: Translation updated at 2013-12-08 21:52
translate russian strings:
+19 -1
View File
@@ -1,4 +1,3 @@
# TODO: Translation updated at 2013-12-08 21:52
translate russian strings:
@@ -90,3 +89,22 @@ translate russian strings:
old "Deleting persistent data..."
new "Удаление постоянных данных..."
translate russian strings:
# game/front_page.rpy:35
old "Open [text] directory."
new "Открыть папку [text]."
# game/front_page.rpy:150
old "Select project [text]."
new "Выбрать проект [text]."
# game/front_page.rpy:234
old "Force Recompile"
new "Перекомпилировать проект"
# game/front_page.rpy:285
old "Recompiling all rpy files into rpyc files..."
new "Перекомпиляция всех файлов rpy в файлы rpyc..."
+15 -1
View File
@@ -1,4 +1,3 @@
# TODO: Translation updated at 2013-12-08 21:52
translate russian strings:
@@ -62,3 +61,18 @@ translate russian strings:
old "Language:"
new "Язык:"
translate russian strings:
# game/preferences.rpy:91
old "Projects directory: [text]"
new "Папка проектов: [text]"
# game/preferences.rpy:114
old "Text editor: [text]"
new "Текстовый редактор: [text]"
# game/preferences.rpy:171
old "Large fonts"
new "Большие шрифты"
+4
View File
@@ -501,3 +501,7 @@ translate spanish strings:
old "Location"
new "Ubicación"
# renpy/common/00preferences.rpy:373
old "Self-voicing enabled. Press 'v' to disable."
new "Self-voicing activado. Presiona 'v' para desactivarlo."
@@ -0,0 +1,20 @@
DroidSansFallback.ttf is destributed under Apache License 2.0.
--------------------------------
Copyright (C) 2008 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
##########
This directory contains the fonts for the platform. They are licensed
under the Apache 2 license.
@@ -0,0 +1,15 @@
translate traditional_chinese strings:
# game/about.rpy:21
old "[version!q]"
new "[version!q]"
# game/about.rpy:25
old "View license"
new "檢視許可證"
# game/about.rpy:27
old "Back"
new "返回"
@@ -0,0 +1,23 @@
translate traditional_chinese strings:
# game/add_file.rpy:7
old "FILENAME"
new "檔案名稱"
# game/add_file.rpy:7
old "Enter the name of the script file to create."
new "請輸入需要建立的腳本名稱。"
# game/add_file.rpy:10
old "The filename must have the .rpy extension."
new "檔案名稱必須以 .rpy 為副檔名。"
# game/add_file.rpy:18
old "The file already exists."
new "該檔已存在。"
# game/add_file.rpy:21
old "# Ren'Py automatically loads all script files ending with .rpy. To use this\n# file, define a label and jump to it from another file.\n"
new "# Ren'Py 將自動載入以 .rpy 結尾的腳本。若要使用\n# 檔案,從其它檔案中定義一個標籤跳轉到該檔案。\n"
@@ -0,0 +1,161 @@
translate traditional_chinese strings:
# game/android.rpy:13
old "A 32-bit Java Development Kit is required to build Android packages on Windows. The JDK is different from the JRE, so it's possible you have Java without having the JDK.\n\nPlease {a=http://www.oracle.com/technetwork/java/javase/downloads/index.html}download and install the JDK{/a}, then restart the Ren'Py launcher."
new "需要一個32位元的 Java 開發套件(JDK)來在 Windows 中建立 Android 套件。JDK 不同於 JRE,所以您可能有 Java 但不包含 JDK。\n\n請 {a=http://www.oracle.com/technetwork/java/javase/downloads/index.html} 下載和安裝 JDK{/a},然後重新啟動 Ren'Py 啟動器。"
# game/android.rpy:14
old "To build Android packages, please download RAPT (from {a=http://www.renpy.org/dl/android}here{/a}), unzip it, and place it into the Ren'Py directory. Then restart the Ren'Py launcher."
new "要建立 Android 套件,請下載 RAPT(從{a=http://www.renpy.org/dl/android}這裡{/a}下載),解壓縮到 Ren'Py 目錄下。然後重新啟動 Ren'Py 啟動器。"
# game/android.rpy:15
old "RAPT has been installed, but you'll need to install the Android SDK before you can build Android packages. Choose Install SDK to do this."
new "RAPT 已經安裝了,但是您還需要安裝 Android SDK 才可以建立 Android 套件。選擇安裝 SDK 來實現它。"
# game/android.rpy:16
old "RAPT has been installed, but a key hasn't been configured. Please create a new key, or restore android.keystore."
new "RAPT 已經安裝了,但是 KEY 尚未設定。請建立一個新的 KEY,或者恢複 android.keystore。"
# game/android.rpy:17
old "The current project has not been configured. Use \"Configure\" to configure it before building."
new "當前專案尚未配置。使用「設定」,在建設之前配置它。"
# game/android.rpy:18
old "Choose \"Build\" to build the current project, or attach an Android device and choose \"Build & Install\" to build and install it on the device."
new "選擇「建立」來建立當前專案,或者選擇「建立並安裝」來添加到一個 Android 裝置中建立並安裝。"
# game/android.rpy:20
old "Attempts to emulate an Android phone.\n\nTouch input is emulated through the mouse, but only when the button is held down. Escape is mapped to the menu button, and PageUp is mapped to the back button."
new "模擬一臺 Android 手機。\n\n當按鈕被按下時,觸控輸入會模擬為滑鼠。Esc 按鍵映射為手機的選單鍵,PageUp 鍵映射為手機的返回鍵。"
# game/android.rpy:21
old "Attempts to emulate an Android tablet.\n\nTouch input is emulated through the mouse, but only when the button is held down. Escape is mapped to the menu button, and PageUp is mapped to the back button."
new "模擬一個 Android 平板。\n\n當按鈕被按下時,觸控輸入會模擬為滑鼠。Esc 按鍵映射為平板的選單鍵,PageUp 鍵映射為平板的返回鍵。"
# game/android.rpy:22
old "Attempts to emulate an OUYA console.\n\nController input is mapped to the arrow keys, Enter is mapped to the select button, Escape is mapped to the menu button, and PageUp is mapped to the back button."
new "模擬一個 OUYA 控制器。\n\n控制器的按鍵映射為 OUYA 的方向鍵,確認鍵映射為 OUYA 的 select 按鍵,Esc 按鍵映射為 OUYA 的選單鍵,PageUp 按鍵被映射到 OUYA 的返回鍵。"
# game/android.rpy:24
old "Downloads and installs the Android SDK and supporting packages. Optionally, generates the keys required to sign the package."
new "下載和安裝 Android SDK 以及支援套件。可選擇性地設定產生金鑰來簽署套件。"
# game/android.rpy:25
old "Configures the package name, version, and other information about this project."
new "設定套件名稱,版本以及其它關於專案的資訊。"
# game/android.rpy:26
old "Opens the file containing the Google Play keys in the editor.\n\nThis is only needed if the application is using an expansion APK. Read the documentation for more details."
new "在編輯器中開啟包含 Google Play keys 的檔案。\n\n這是在應用程式需要一個擴展的 APK 套件時才需要。閱讀檔案來瞭解更多細節。"
# game/android.rpy:27
old "Builds the Android package."
new "建立 Android 套件。"
# game/android.rpy:28
old "Builds the Android package, and installs it on an Android device connected to your computer."
new "建立 Android 套件,並安裝到已連線到電腦的 Android 裝置中。"
# game/android.rpy:148
old "{a=%s}%s{/a}"
new "{a=%s}%s{/a}"
# game/android.rpy:371
old "Android: [project.current.name!q]"
new "Android [project.current.name!q]"
# game/android.rpy:391
old "Emulation:"
new "模擬:"
# game/android.rpy:399
old "Phone"
new "手機"
# game/android.rpy:403
old "Tablet"
new "平板"
# game/android.rpy:407
old "Television / OUYA"
new "電視/OUYA"
# game/android.rpy:419
old "Build:"
new "建立:"
# game/android.rpy:427
old "Install SDK & Create Keys"
new "安裝 SDK 並創建金鑰"
# game/android.rpy:431
old "Configure"
new "設定"
# game/android.rpy:516
old "Other:"
new "其他:"
# game/android.rpy:435
old "Build Package"
new "建立套件"
# game/android.rpy:439
old "Build & Install"
new "建立並安裝"
translate traditional_chinese strings:
# game/android.rpy:30
old "To build Android packages, please download RAPT, unzip it, and place it into the Ren'Py directory. Then restart the Ren'Py launcher."
new "要建立 Android 套件,請下載 RAPT,解壓縮並將其放到 Ren'Py 目錄下,然後重新啟動 Ren'Py 啟動器。"
# game/android.rpy:506
old "Before packaging Android apps, you'll need to download RAPT, the Ren'Py Android Packaging Tool. Would you like to download RAPT now?"
new "在包裝 Android 應用程式前,您必須下載 RAPT(Ren'Py Android Packaging Tool)Ren'Py 的 Android 包裝程式。您要現在下載 RAPT 嗎?"
# game/android.rpy:39
old "Attempts to emulate a televison-based Android console, like the OUYA or Fire TV.\n\nController input is mapped to the arrow keys, Enter is mapped to the select button, Escape is mapped to the menu button, and PageUp is mapped to the back button."
new "模擬一臺基於 Android 的電視,例如 OUYA 或 Fire TV。\n\n控制器的按鍵映射為 OUYA 的方向鍵,確認鍵映射為 OUYA 的 select 按鍵,Esc 按鍵映射為 OUYA 的選單鍵,PageUp 按鍵被映射到 OUYA 的返回鍵。"
# game/android.rpy:524
old "Remote ADB Connect"
new "遠端 ADB 連接"
# game/android.rpy:528
old "Remote ADB Disconnect"
new "斷開遠端 ADB 連接"
# game/android.rpy:608
old "Remote ADB Address"
new "遠端 ADB 位址"
# game/android.rpy:609
old "Please enter the IP address and port number to connect to, in the form \"192.168.1.143:5555\". Consult your device's documentation to determine if it supports remote ADB, and if so, the address and port to use."
new "\"192.168.1.143:5555\" 是連接裝置的 IP 位址和埠的輸入形式。請鍵入裝置的 IP 位址和埠來連接。注意,確保裝置支援遠端 ADB,然後鍵入位址和埠來使用。"
# game/android.rpy:619
old "Invalid remote ADB address"
new "遠端 ADB 位址無效"
# game/android.rpy:619
old "The address must contain one exactly one ':'."
new "位址必須包含一個精確的 ':'。"
# game/android.rpy:623
old "The host may not contain whitespace."
new "主機位址不能包含空格。"
# game/android.rpy:629
old "The port must be a number."
new "埠必須是數位。"
# game/android.rpy:47
old "Connects to an Android device running ADB in TCP/IP mode."
new "在 TCP/IP 模式下運行 ADB Android 裝置。"
# game/android.rpy:48
old "Disconnects from an Android device running ADB in TCP/IP mode."
new "在 TCP/IP 模式下運行 ADB Android 裝置,並斷開連接。"
@@ -0,0 +1,43 @@
translate traditional_chinese strings:
# game/choose_theme.rpy:274
old "Could not change the theme. Perhaps options.rpy was changed too much."
new "無法更改介面主題。Options.rpy 也許改變了太多。"
# game/choose_theme.rpy:332
old "Display"
new "Display"
# game/choose_theme.rpy:333
old "Window"
new "Window"
# game/choose_theme.rpy:334
old "Fullscreen"
new "Fullscreen"
# game/choose_theme.rpy:335
old "Planetarium"
new "Planetarium"
# game/choose_theme.rpy:342
old "Sound Volume"
new "Sound Volume"
# game/choose_theme.rpy:376
old "Choose Theme"
new "Choose Theme"
# game/choose_theme.rpy:389
old "Theme"
new "介面主題"
# game/choose_theme.rpy:413
old "Color Scheme"
new "色彩配置"
# game/choose_theme.rpy:444
old "Continue"
new "繼續"
@@ -0,0 +1,510 @@
translate traditional_chinese strings:
# renpy/common/00action_file.rpy:118
old "%b %d, %H:%M"
new "%%m月%d日, %H:%M"
translate traditional_chinese strings:
# renpy/common/00gltest.rpy:50
old "Graphics Acceleration"
new "圖形加速"
# renpy/common/00gltest.rpy:54
old "Automatically Choose"
new "自動選擇"
# renpy/common/00gltest.rpy:59
old "Force Angle/DirectX Renderer"
new "強制 Angle/DirectX 渲染"
# renpy/common/00gltest.rpy:63
old "Force OpenGL Renderer"
new "強制 OpenGL 渲染"
# renpy/common/00gltest.rpy:67
old "Force Software Renderer"
new "強制軟體渲染"
# renpy/common/00gltest.rpy:73
old "Changes will take effect the next time this program is run."
new "更改將會在下次啟動程式時生效。"
# renpy/common/00gltest.rpy:77
old "Quit"
new "離開"
# renpy/common/00gltest.rpy:82
old "Return"
new "返回"
# renpy/common/00gltest.rpy:112
old "Performance Warning"
new "效能警告"
# renpy/common/00gltest.rpy:117
old "This computer is using software rendering."
new "該電腦正在使用軟體渲染。"
# renpy/common/00gltest.rpy:119
old "This computer is not using shaders."
new "該電腦沒有使用著色器"
# renpy/common/00gltest.rpy:121
old "This computer is displaying graphics slowly."
new "該電腦圖形顯示緩慢。"
# renpy/common/00gltest.rpy:123
old "This computer has a problem displaying graphics: [problem]."
new "該電腦圖形顯示出錯:[problem]。"
# renpy/common/00gltest.rpy:128
old "Its graphics drivers may be out of date or not operating correctly. This can lead to slow or incorrect graphics display. Updating DirectX could fix this problem."
new "目前的圖形驅動程式版本可能過舊或者運作不正確。這樣會導致圖形顯示緩慢或者錯誤。升級 DirectX 可能會解決該問題。"
# renpy/common/00gltest.rpy:130
old "Its graphics drivers may be out of date or not operating correctly. This can lead to slow or incorrect graphics display."
new "目前圖形驅動程式版本可能過舊或者運作不正確。這樣會導致圖形顯示緩慢或者錯誤。"
# renpy/common/00gltest.rpy:135
old "Update DirectX"
new "更新 DirectX"
# renpy/common/00gltest.rpy:141
old "Continue, Show this warning again"
new "繼續,下次再顯示該警告"
# renpy/common/00gltest.rpy:145
old "Continue, Don't show warning again"
new "繼續,不再顯示該警告"
# renpy/common/00gltest.rpy:171
old "Updating DirectX."
new "正在更新 DirectX。"
# renpy/common/00gltest.rpy:175
old "DirectX web setup has been started. It may start minimized in the taskbar. Please follow the prompts to install DirectX."
new "DirectX 線上安裝已啟動。將會最小化到狀態列中。請按照說明安裝 DirectX。"
# renpy/common/00gltest.rpy:179
old "{b}Note:{/b} Microsoft's DirectX web setup program will, by default, install the Bing toolbar. If you do not want this toolbar, uncheck the appropriate box."
new "{b}警告:{/b} Microsoft's DirectX 線上安裝程式會預設安裝 Bing 瀏覽器工具列。如果您不需要安裝這個工具列,請取消勾選對應選項。"
# renpy/common/00gltest.rpy:183
old "When setup finishes, please click below to restart this program."
new "當安裝完成後請點選下方來重新啟動應用程式。"
# renpy/common/00gltest.rpy:185
old "Restart"
new "重新啟動"
translate traditional_chinese strings:
# renpy/common/00keymap.rpy:167
old "Saved screenshot as %s."
new "儲存螢幕截圖為 %s。"
translate traditional_chinese strings:
# renpy/common/00layout.rpy:439
old "Are you sure?"
new "是否確認?"
# renpy/common/00layout.rpy:443
old "Are you sure you want to quit?"
new "您確定您要離開嗎?"
# renpy/common/00layout.rpy:444
old "Are you sure you want to return to the main menu?\nThis will lose unsaved progress."
new "您確定您想回到標題畫面嗎?\n這將使您失去所有未儲存的進度。"
# renpy/common/00layout.rpy:441
old "Are you sure you want to overwrite your save?"
new "您確定您要覆寫您的存檔?"
# renpy/common/00layout.rpy:442
old "Loading will lose unsaved progress.\nAre you sure you want to do this?"
new "載入將會失去所有未儲存的進度。\n您確定您要這麼做嗎?"
# renpy/common/00layout.rpy:440
old "Are you sure you want to delete this save?"
new "您確定您要刪掉這份存檔嗎?"
translate traditional_chinese strings:
# renpy/common/00library.rpy:77
old "Skip Mode"
new "跳過對話模式"
# renpy/common/00library.rpy:80
old "Fast Skip Mode"
new "快速跳過對話模式"
translate traditional_chinese strings:
# renpy/common/00updater.rpy:1258
old "Updater"
new "更新"
# renpy/common/00updater.rpy:1267
old "This program is up to date."
new "該程式已是最新版本。"
# renpy/common/00updater.rpy:1269
old "[u.version] is available. Do you want to install it?"
new "[u.version] 已釋出。是否確認安裝?"
# renpy/common/00updater.rpy:1271
old "Preparing to download the updates."
new "正在準備下載更新。"
# renpy/common/00updater.rpy:1273
old "Downloading the updates."
new "正在下載更新。"
# renpy/common/00updater.rpy:1275
old "Unpacking the updates."
new "解壓縮更新檔案。"
# renpy/common/00updater.rpy:1279
old "The updates have been installed. The program will restart."
new "該更新檔案已安裝,程式將會重新啟動。"
# renpy/common/00updater.rpy:1281
old "The updates have been installed."
new "已完成更新。"
# renpy/common/00updater.rpy:1283
old "The updates were cancelled."
new "已取消更新。"
translate traditional_chinese strings:
# renpy/common/_compat/gamemenu.rpym:180
old "Empty Slot."
new "空白欄位"
# renpy/common/_compat/gamemenu.rpym:337
old "Previous"
new "上一頁"
# renpy/common/_compat/gamemenu.rpym:344
old "Next"
new "下一頁"
translate traditional_chinese strings:
# renpy/common/_compat/preferences.rpym:411
old "Joystick Mapping"
new "遊戲桿映射"
translate traditional_chinese strings:
# renpy/common/_errorhandling.rpym:408
old "An exception has occurred."
new "發生一個異常狀況。"
# renpy/common/_errorhandling.rpym:434
old "Rollback"
new "回滾"
# renpy/common/_errorhandling.rpym:436
old "Attempts a roll back to a prior time, allowing you to save or choose a different choice."
new "嘗試回滾到先前的狀態,使您可以存檔或者選擇不同選項。"
# renpy/common/_errorhandling.rpym:439
old "Ignore"
new "忽略"
# renpy/common/_errorhandling.rpym:441
old "Ignores the exception, allowing you to continue. This often leads to additional errors."
new "忽略異常,讓您可以繼續。這通常會產生額外的錯誤。"
# renpy/common/_errorhandling.rpym:444
old "Reload"
new "重新載入"
# renpy/common/_errorhandling.rpym:446
old "Reloads the game from disk, saving and restoring game state if possible."
new "從硬碟重新載入遊戲,請您儲存遊戲狀態。"
# renpy/common/_errorhandling.rpym:448
old "Open Traceback"
new "開啟回溯"
# renpy/common/_errorhandling.rpym:450
old "Opens the traceback.txt file in a text editor."
new "在編輯器中開啟回溯檔案(traceback.txt)。"
# renpy/common/_errorhandling.rpym:456
old "Quits the game."
new "離開遊戲。"
# renpy/common/_errorhandling.rpym:483
old "Parsing the script failed."
new "解析腳本失敗。"
# renpy/common/_errorhandling.rpym:510
old "Open Parse Errors"
new "開啟解析錯誤"
# renpy/common/_errorhandling.rpym:512
old "Opens the errors.txt file in a text editor."
new "在編輯器中開啟解析錯誤檔案(errors.txt)。"
translate traditional_chinese strings:
# renpy/common/_layout/classic_load_save.rpym:152
old "a"
new "a"
# renpy/common/_layout/classic_load_save.rpym:161
old "q"
new "q"
translate traditional_chinese strings:
# renpy/common/00action_file.rpy:587
old "Quick save complete."
new "快速儲存。"
translate traditional_chinese strings:
# renpy/common/00gallery.rpy:521
old "Image [index] of [count] locked."
new "[count]個圖片中的第[index]個圖片被鎖定。"
# renpy/common/00gallery.rpy:539
old "prev"
new "上一頁"
# renpy/common/00gallery.rpy:540
old "next"
new "下一頁"
# renpy/common/00gallery.rpy:541
old "slideshow"
new "幻燈片"
# renpy/common/00gallery.rpy:542
old "return"
new "返回"
translate traditional_chinese strings:
# renpy/common/00layout.rpy:427
old "Are you sure you want to begin skipping?"
new "是否確定開始略過?"
# renpy/common/00layout.rpy:428
old "Are you sure you want to skip to the next choice?"
new "是否確定跳到下一個選項?"
# renpy/common/00layout.rpy:429
old "Are you sure you want to skip to unseen dialogue or the next choice?"
new "是否確定略過未看過的訊息或下一選項?"
translate traditional_chinese strings:
# renpy/common/00console.rpy:179
old "%(version)s console, originally by Shiz, C, and delta.\n"
new "%(version)s 控制臺,作者: Shiz, C, delta。"
# renpy/common/00console.rpy:180
old "Press <esc> to exit console. Type help for help.\n"
new "按 <esc> 來退出控制臺。輸入 help 來檢視說明。\n"
# renpy/common/00console.rpy:184
old "Ren'Py script enabled."
new "使用 Ren'Py 腳本。"
# renpy/common/00console.rpy:186
old "Ren'Py script disabled."
new "禁用 Ren'Py 腳本。"
# renpy/common/00console.rpy:392
old "help: show this help"
new "help: 顯示說明資訊"
# renpy/common/00console.rpy:397
old "commands:\n"
new "命令列:\n"
# renpy/common/00console.rpy:407
old " <renpy script statement>: run the statement\n"
new " <renpy script statement>:運行語句\n"
# renpy/common/00console.rpy:409
old " <python expression or statement>: run the expression or statement"
new " <python expression or statement>:運行運算式或語句"
# renpy/common/00console.rpy:417
old "clear: clear the console history"
new "clear: 清除控制臺歷史記錄"
# renpy/common/00console.rpy:421
old "exit: exit the console"
new "exit: 離開控制臺"
# renpy/common/00console.rpy:429
old "load <slot>: loads the game from slot"
new "load <slot>:載入遊戲存檔(以 slot 為存檔號)"
# renpy/common/00console.rpy:442
old "save <slot>: saves the game in slot"
new "save <slot>:儲存遊戲存檔(以 slot 為存檔號)"
# renpy/common/00console.rpy:453
old "reload: reloads the game, refreshing the scripts"
new "reload: 重新載入遊戲,並重新整理腳本"
# renpy/common/00console.rpy:461
old "watch <expression>: watch a python expression"
new "watch <expression>: 監視一個 python 運算式"
# renpy/common/00console.rpy:470
old "unwatch <expression>: stop watching an expression"
new "unwatch <expression>:停止監視某個運算式"
# renpy/common/00console.rpy:478
old "unwatchall: stop watching all expressions"
new "unwatchall:停止監視索引運算式"
# renpy/common/00console.rpy:484
old "jump <label>: jumps to label"
new "jump <label>: 跳移至標籤"
translate traditional_chinese strings:
# renpy/common/_developer/developer.rpym:65
old "Developer Menu"
new "開發者選單"
# renpy/common/_developer/developer.rpym:67
old "Reload Game (Shift+R)"
new "重新載入遊戲(Shift+R"
# renpy/common/_developer/developer.rpym:69
old "Console (Shift+O)"
new "控制臺(Shift+O)"
# renpy/common/_developer/developer.rpym:71
old "Variable Viewer"
new "變數觀察器"
# renpy/common/_developer/developer.rpym:73
old "Theme Test"
new "介面主題測試"
# renpy/common/_developer/developer.rpym:75
old "Image Location Picker"
new "圖片座標撿選器"
# renpy/common/_developer/developer.rpym:77
old "Filename List"
new "檔案清單"
# renpy/common/_developer/developer.rpym:81
old "Show Image Load Log"
new "顯示圖片載入記錄"
# renpy/common/_developer/developer.rpym:84
old "Hide Image Load Log"
new "隱藏圖片載入記錄"
# renpy/common/_developer/developer.rpym:149
old "No variables have changed since the game started."
new "遊戲開始至此沒有任何變數被改變。"
# renpy/common/_developer/developer.rpym:152
old "Return to the developer menu"
new "回到開發者選單"
# renpy/c72
old "{b}Missing Images{/b}"
new "{b}丟失圖片{/b}"
# renpy/common/_developer/developer.rpym:424
old "Rectangle: %r"
new "矩形區域:%r"
# renpy/common/_developer/developer.rpym:429
old "Mouse position: %r"
new "滑鼠位置:%r"
# renpy/common/_developer/developer.rpym:431
old "Right-click or escape to quit."
new "點選滑鼠右鍵或者按 ESC 鍵來退出。"
# renpy/common/_developer/developer.rpym:482
old "Done"
new "完成"
translate traditional_chinese strings:
# renpy/common/_developer/inspector.rpym:43
old "Displayable Inspector"
new "Displayable 檢閱器"
# renpy/common/_developer/inspector.rpym:49
old "Nothing to inspect."
new "沒有東西被檢閱"
# renpy/common/_developer/inspector.rpym:58
old "Size"
new "大小"
# renpy/common/_developer/inspector.rpym:63
old "Style"
new "風格"
# renpy/common/_developer/inspector.rpym:123
old "Inspecting Styles of [displayable_name!q]"
new "檢閱[displayable_name!q]的風格"
# renpy/common/_developer/inspector.rpym:135
old "displayable:"
new "displayable"
# renpy/common/_developer/inspector.rpym:142
old " (no properties affect the displayable)"
new " (沒有偏好的可顯示屬性)"
# renpy/common/_developer/inspector.rpym:144
old " (default properties omitted)"
new " (忽略預設屬性)"
# renpy/common/_developer/inspector.rpym:174
old "<repr() failed>"
new "<repr() failed>"
# Translation updated at 2014-10-18 06:00
translate traditional_chinese strings:
# renpy/common/_developer/inspector.rpym:80
old "Location"
new "位置"
# renpy/common/00preferences.rpy:373
old "Self-voicing enabled. Press 'v' to disable."
new "Self-voicing 啟用。可按下 'v' 來禁用。"
@@ -0,0 +1,49 @@
translate traditional_chinese strings:
# game/distribute.rpy:333
old "Nothing to do."
new "什麼都不做。"
# game/distribute.rpy:337
old "Scanning project files..."
new "掃描專案檔案..."
# game/distribute.rpy:344
old "Scanning Ren'Py files..."
new "掃描 Ren'Py 檔案..."
# game/distribute.rpy:494
old "Archiving files..."
new "壓縮檔案..."
# game/distribute.rpy:745
old "Writing the [variant] [format] package."
new "寫入 [variant] [format] 套件。"
# game/distribute.rpy:758
old "Making the [variant] update zsync file."
new "新增 [variant] 更新 zsync 檔案。"
# game/distribute.rpy:854
old "Processed {b}[complete]{/b} of {b}[total]{/b} files."
new "完成處理 {b}[complete]{/b} ,共 {b}[total]{/b}個檔案。"
# game/distribute.rpy:915
old "All packages have been built.\n\nDue to the presence of permission information, unpacking and repacking the Linux and Macintosh distributions on Windows is not supported."
new "所有套件已被建立。\n\n由於含有授權資訊存在,因此在 Windows 中不支援解開和重新包裝 Linux 及 Mac 的套件。"
translate traditional_chinese strings:
# game/distribute.rpy:358
old "No packages are selected, so there's nothing to do."
new "沒有選擇套件,沒有可以處理的事情。"
translate traditional_chinese strings:
# game/distribute.rpy:335
old "Building distributions failed:\n\nThe build.directory_name variable may not include the space, colon, or semicolon characters."
new "建立釋出版失敗:\n\n build.directory_name 變數可能包含空格,冒號或分號。"
@@ -0,0 +1,47 @@
translate traditional_chinese strings:
# game/distribute_gui.rpy:139
old "Build Distributions: [project.current.name!q]"
new "建立釋出版:[project.current.name!q]"
# game/distribute_gui.rpy:154
old "Directory Name:"
new "目錄名稱:"
# game/distribute_gui.rpy:158
old "Executable Name:"
new "執行檔案名稱:"
# game/distribute_gui.rpy:167
old "Actions:"
new "動作:"
# game/distribute_gui.rpy:175
old "Edit options.rpy"
new "編輯 options.rpy"
# game/distribute_gui.rpy:176
old "Refresh"
new "重新整理"
# game/distribute_gui.rpy:193
old "Build Packages:"
new "建立套件:"
# game/distribute_gui.rpy:208
old "Build Updates"
new "建立更新檔"
# game/distribute_gui.rpy:212
old "Build"
new "建立"
# game/distribute_gui.rpy:219
old "Errors were detected when running the project. Please ensure the project runs without errors before building distributions."
new "當運行專案時發生了錯誤。在開始釋出前請確保專案運行無誤。"
# game/distribute_gui.rpy:236
old "Your project does not contain build information. Would you like to add build information to the end of options.rpy?"
new "您的專案不包含建置資訊。是否加入建置資訊到 options.rpy"
@@ -0,0 +1,66 @@
translate traditional_chinese strings:
# game/editor.rpy:120
old "{b}Recommended.{/b} A beta editor with an easy to use interface and features that aid in development, such as spell-checking. Editra currently lacks the IME support required for Chinese, Japanese, and Korean text input."
new "{b}推薦{/b} 一個有簡單易用介面和功能的測試版編輯器能夠幫助開發,例如拼字的校對功能。Editra 目前缺乏中文、日文,韓文的輸入的 IME 支援。"
# game/editor.rpy:121
old "{b}Recommended.{/b} A beta editor with an easy to use interface and features that aid in development, such as spell-checking. Editra currently lacks the IME support required for Chinese, Japanese, and Korean text input. On Linux, Editra requires wxPython."
new "{b}推薦{/b} 一個有簡單易用的介面和功能的測試版編輯器能夠幫助開發,例如拼字的校對功能。Editra 現在沒有中文,日文,韓文的輸入功能。在 linux 中 Editra 需要 wxPython(模組支援)。"
# game/editor.rpy:137
old "The may have occured because wxPython is not installed on this system."
new "這可能是由於 wxPython 未安裝在此系統中而出現的。"
# game/editor.rpy:144
old "Up to 22 MB download required."
new "22 MB 需要下載。"
# game/editor.rpy:157
old "1.8 MB download required."
new "1.8 MB 需要下載。"
# game/editor.rpy:158
old "This may have occured because Java is not installed on this system."
new "這可能是由於 Java 未安裝在此系統中而出現的。"
# game/editor.rpy:327
old "An exception occured while launching the text editor:\n[exception!q]"
new "啟動以下編輯器出現異常:\n[exception!q]"
# game/editor.rpy:378
old "Select Editor"
new "選擇編輯器"
# game/editor.rpy:393
old "A text editor is the program you'll use to edit Ren'Py script files. Here, you can select the editor Ren'Py will use. If not already present, the editor will be automatically downloaded and installed."
new "文字編輯器是您用於編輯 Ren'py 腳本的程式。在這裡您可以選擇 Ren'py 會使用到編輯器。如果選定則編輯器將會自動下載並安裝。"
# game/editor.rpy:415
old "Cancel"
new "取消"
translate traditional_chinese strings:
# game/editor.rpy:155
old "A mature editor that requires Java."
new "一個成熟的編輯器,它需要 Java。"
# game/editor.rpy:164
old "Invokes the editor your operating system has associated with .rpy files."
new "調用作業系統已與 .rpy 檔案關聯的編輯器。"
# game/editor.rpy:180
old "Prevents Ren'Py from opening a text editor."
new "防止 Ren'Py 開啟文字編輯器。"
# Translation updated at 2014-10-18 06:00
translate traditional_chinese strings:
# game/editor.rpy:167
old "This may have occured because wxPython is not installed on this system."
new "這可能是因為 wxPython 沒有被安裝在當前系統中而發生的。"
@@ -0,0 +1,113 @@
translate traditional_chinese strings:
# game/front_page.rpy:79
old "+ Create New Project"
new "+ 新增專案"
# game/front_page.rpy:90
old "Launch Project"
new "啟動專案"
# game/front_page.rpy:111
old "Tutorial"
new "教程"
# game/front_page.rpy:112
old "The Question"
new "問題"
# game/front_page.rpy:128
old "Active Project"
new "活動專案"
# game/front_page.rpy:136
old "Open Directory"
new "開啟目錄"
# game/front_page.rpy:141
old "game"
new "game"
# game/front_page.rpy:142
old "base"
new "base"
# game/front_page.rpy:148
old "Edit File"
new "編輯檔案"
# game/front_page.rpy:156
old "All script files"
new "開啟所有檔案"
# game/front_page.rpy:165
old "Navigate Script"
new "腳本導航"
# game/front_page.rpy:176
old "Check Script (Lint)"
new "檢查腳本(Lint"
# game/front_page.rpy:177
old "Change Theme"
new "變更介面主題"
# game/front_page.rpy:178
old "Delete Persistent"
new "刪除永久性資料"
# game/front_page.rpy:186
old "Build Distributions"
new "建立釋出版"
# game/front_page.rpy:188
old "Generate Translations"
new "產生翻譯"
# game/front_page.rpy:204
old "Checking script for potential problems..."
new "檢查腳本中的潛在錯誤..."
# game/front_page.rpy:219
old "Deleting persistent data..."
new "刪除永久性資料..."
translate traditional_chinese strings:
# game/front_page.rpy:69
old "PROJECTS:"
new "專案列表:"
# game/front_page.rpy:122
old "[p.name!q] (template)"
new "[p.name!q] (template)"
# game/front_page.rpy:215
old "Android"
new "Android"
# game/front_page.rpy:217
old "Extract Dialogue"
new "產生對話框"
# Translation updated at 2014-10-18 06:00
translate traditional_chinese strings:
# game/front_page.rpy:35
old "Open [text] directory."
new "開啟 [text] 目錄。"
# game/front_page.rpy:150
old "Select project [text]."
new "選擇專案 [text]。"
# game/front_page.rpy:234
old "Force Recompile"
new "強制重編譯"
# game/front_page.rpy:285
old "Recompiling all rpy files into rpyc files..."
new "重編譯所有 rpy 檔案為 rpyc 檔案……"
@@ -0,0 +1,90 @@
translate traditional_chinese strings:
# game/interface.rpy:89
old "Documentation"
new "文件"
# game/interface.rpy:90
old "Ren'Py Website"
new "Ren'Py網站"
# game/interface.rpy:91
old "Ren'Py Games List"
new "Ren'Py遊戲列表"
# game/interface.rpy:92
old "About"
new "關於"
# game/interface.rpy:99
old "update"
new "更新"
# game/interface.rpy:101
old "preferences"
new "設定"
# game/interface.rpy:102
old "quit"
new "離開"
# game/interface.rpy:149
old "Yes"
new "確定"
# game/interface.rpy:151
old "No"
new "取消"
# game/interface.rpy:181
old "Due to package format limitations, non-ASCII file and directory names are not allowed."
new "由於套件格式的限制,非 ASCII 編碼的檔案和目錄將不被支援。"
# game/interface.rpy:183
old "[title]"
new "[title]"
# game/interface.rpy:248
old "ERROR"
new "錯誤"
# game/interface.rpy:280
old "While [what!q], an error occured:"
new "[what!q]時,出現錯誤:"
# game/interface.rpy:281
old "[exception!q]"
new "[exception!q]"
# game/interface.rpy:298
old "Text input may not contain the {{ or [[ characters."
new "文字輸入可能不包含{{ 或 [[ 字元。"
# game/interface.rpy:303
old "File and directory names may not contain / or \\."
new "檔案或目錄不包含/或\\。"
# game/interface.rpy:309
old "File and directory names must consist of ASCII characters."
new "檔案名稱和目錄名稱必須是 ASCII 字元。"
# game/interface.rpy:330
old "INFORMATION"
new "資訊"
# game/interface.rpy:373
old "PROCESSING"
new "正在處理"
# game/interface.rpy:390
old "QUESTION"
new "問題"
translate traditional_chinese strings:
# game/interface.rpy:456
old "CHOICE"
new "選擇"
@@ -0,0 +1,71 @@
translate traditional_chinese strings:
# game/navigation.rpy:150
old "Navigate: [project.current.name]"
new "導航:[project.current.name]"
# game/navigation.rpy:159
old "Order: "
new "排序:"
# game/navigation.rpy:160
old "alphabetical"
new "按字母順序"
# game/navigation.rpy:162
old "by-file"
new "按檔案"
# game/navigation.rpy:164
old "natural"
new "natural"
# game/navigation.rpy:168
old "refresh"
new "重新整理"
# game/navigation.rpy:176
old "Category:"
new "類別:"
# game/navigation.rpy:178
old "files"
new "檔案"
# game/navigation.rpy:179
old "labels"
new "標籤"
# game/navigation.rpy:180
old "defines"
new "定義"
# game/navigation.rpy:181
old "transforms"
new "轉換"
# game/navigation.rpy:182
old "screens"
new "螢幕"
# game/navigation.rpy:183
old "callables"
new "可呼叫"
# game/navigation.rpy:184
old "TODOs"
new "待辦事項"
# game/navigation.rpy:223
old "+ Add script file"
new "+ 加入腳本檔案"
# game/navigation.rpy:231
old "No TODO comments found.\n\nTo create one, include \"# TODO\" in your script."
new "沒有待辦的事項被發現。\n\n要建立一個包含\"# TODO\"到您的腳本中。"
# game/navigation.rpy:238
old "The list of names is empty."
new "名稱清單為空。"
@@ -0,0 +1,45 @@
translate traditional_chinese strings:
# game/new_project.rpy:22
old "Choose Project Template"
new "選擇專案模版"
# game/new_project.rpy:40
old "Please select a template to use for your new project. Ren'Py ships with a default template that creates an English-language game with standard screens."
new "請選擇一個用於新專案的模版。Ren'Py 中預設了一個標準視窗的英文遊戲。"
# game/new_project.rpy:55
old "PROJECT NAME"
new "專案名稱"
# game/new_project.rpy:56
old "Please enter the name of your project:"
new "請輸入專案名稱:"
# game/new_project.rpy:62
old "The project name may not be empty."
new "專案名稱不能為空。"
# game/new_project.rpy:67
old "[project_name!q] already exists. Please choose a different project name."
new "[project_name!q]已存在。請選擇不同的名稱。"
# game/new_project.rpy:70
old "[project_dir!q] already exists. Please choose a different project name."
new "[project_dir!q] 已存在。請選擇不同的名稱。"
translate traditional_chinese strings:
# game/new_project.rpy:40
old "Please select a template to use for your new project. The template sets the default font and the user interface language. If your language is not supported, choose 'english'."
new "請為您新增的專案選擇一個模版。該模版將設定為預設的字型和語言。如果您的所使用的語言沒有被支援,那麼請選擇「english」。"
translate traditional_chinese strings:
# game/new_project.rpy:71
old "The projects directory could not be set. Giving up."
new "無法設定專案目錄。放棄。"
@@ -0,0 +1,82 @@
translate traditional_chinese strings:
# game/preferences.rpy:40
old "Launcher Preferences"
new "啟動器偏好"
# game/preferences.rpy:61
old "Projects Directory:"
new "專案目錄:"
# game/preferences.rpy:68
old "[persistent.projects_directory!q]"
new "[persistent.projects_directory!q]"
# game/preferences.rpy:70
old "Not Set"
new "未設定"
# game/preferences.rpy:84
old "Text Editor:"
new "文字編輯器: "
# game/preferences.rpy:106
old "Update Channel:"
new "更新頻道:"
# game/preferences.rpy:126
old "Navigation Options:"
new "導航選項:"
# game/preferences.rpy:130
old "Include private names"
new "包含私有名稱"
# game/preferences.rpy:131
old "Include library names"
new "包含資料庫名稱"
# game/preferences.rpy:141
old "Launcher Options:"
new "啟動器選項:"
# game/preferences.rpy:145
old "Hardware rendering"
new "硬體渲染"
# game/preferences.rpy:148
old "Console output"
new "控制臺輸出"
# game/preferences.rpy:169
old "Open launcher project"
new "開啟啟動器專案"
# game/preferences.rpy:183
old "Language:"
new "語言:"
translate traditional_chinese strings:
# game/preferences.rpy:146
old "Show templates"
new "顯示模版"
# game/preferences.rpy:165
old "Large fonts"
new "大字體"
# Translation updated at 2014-10-18 06:00
translate traditional_chinese strings:
# game/preferences.rpy:91
old "Projects directory: [text]"
new "專案資料夾: [text]"
# game/preferences.rpy:114
old "Text editor: [text]"
new "文字編輯器: [text]"
@@ -0,0 +1,66 @@
translate traditional_chinese strings:
# game/project.rpy:196
old "Ren'Py is scanning the project..."
new "Ren'Py 正在掃描專案..."
# game/project.rpy:485
old "PROJECTS DIRECTORY"
new "專案目錄"
# game/project.rpy:485
old "Please choose the projects directory using the directory chooser.\n{b}The directory chooser may have opened behind this window.{/b}"
new "請使用目錄選擇器選擇專案目錄。\n{b}目錄選擇視窗可能會開啟在本視窗後面。{/b}"
# game/project.rpy:485
old "This launcher will scan for projects in this directory, will create new projects in this directory, and will place built projects into this directory."
new "啟動器會掃描該目錄下的專案,新增的專案也將在該目錄下建置。專案也在該目錄下建立。"
# game/project.rpy:525
old "Ren'Py was unable to run python with tkinter to choose the projects directory."
new "Ren'Py 無法運行 python 的 tkinter 來選擇專案目錄。"
# game/project.rpy:529
old "Ren'Py has set the projects directory to:"
new "Ren'Py 已將專案目錄設定為:"
translate traditional_chinese strings:
# game/project.rpy:48
old "After making changes to the script, press shift+R to reload your game."
new "改變腳本後,請按 shift+R 來重新載入您的遊戲。"
# game/project.rpy:49
old "Press shift+O (the letter) to access the console."
new "按 shift+O (the letter)來進入控制臺。"
# game/project.rpy:50
old "Press shift+D to access the developer menu."
new "按 shift+D 來進入開發者選單"
# game/project.rpy:219
old "Launching the project failed."
new "啟動專案失敗。"
# game/project.rpy:219
old "Please ensure that your project launches normally before running this command."
new "請確保您的專案能正常啟動再執行本指令。"
# game/project.rpy:516
old "Launching"
new "正在啟動"
# game/project.rpy:585
old "Ren'Py was unable to run python with tkinter to choose the projects directory. Please install the python-tk or tkinter package."
new "Ren'Py 無法運行 python 的 tkinter 來選擇專案目錄。請安裝 python-tk 或 tkinter。"
# Translation updated at 2014-10-18 06:00
translate traditional_chinese strings:
# game/project.rpy:47
old "Have you backed up your projects recently?"
new "您最近有備份您的專案嗎?"
@@ -0,0 +1,35 @@
translate traditional_chinese python:
DROID = "tl/traditional_chinese/DroidSansFallback.ttf"
translate traditional_chinese style l_default:
font DROID
size 16
translate traditional_chinese style l_button_text:
selected_font DROID
selected_bold True
translate traditional_chinese style l_link_text:
font DROID
translate traditional_chinese style l_alternate_text:
font DROID
translate traditional_chinese style l_navigation_button_text:
font DROID
translate traditional_chinese style l_navigation_text:
font DROID
bold True
translate traditional_chinese style l_checkbox_text:
selected_font DROID
translate traditional_chinese style l_nonbox_text:
selected_font DROID
translate traditional_chinese style hyperlink_text:
font DROID
translate traditional_chinese python:
make_style_backup()
@@ -0,0 +1,38 @@
translate traditional_chinese strings:
# game/translations.rpy:10
old "Create or Update Translations"
new "新增或更新翻譯"
# game/translations.rpy:10
old "Please enter the name of the language for which you want to create or update translations."
new "請輸入您要新增或者更新語言的名稱。"
# game/translations.rpy:15
old "The language name can not be the empty string."
new "語言名稱不能為空字串。"
# game/translations.rpy:26
old "Ren'Py is generating translations...."
new "Ren'Py 正在產生翻譯..."
# game/translations.rpy:30
old "Ren'Py has finished generating [language] translations."
new "Ren'Py 已經完成[language]翻譯檔案。"
translate traditional_chinese strings:
# game/translations.rpy:44
old "What format would you like for the extracted dialogue?"
new "您要使用什麼格式來提取對話框?"
# game/translations.rpy:56
old "Ren'Py is extracting dialogue...."
new "Ren'Py 正在提取對話框..."
# game/translations.rpy:60
old "Ren'Py has finished extracting dialogue. The extracted dialogue can be found in dialogue.[format] in the base directory."
new "Ren'Py 已經完成對話框提取。提取的對話可以在底層目錄下 dialogue.[format] 中找到。"
@@ -0,0 +1,98 @@
translate traditional_chinese strings:
# game/updater.rpy:54
old "Select Update Channel"
new "選擇更新頻道"
# game/updater.rpy:65
old "The update channel controls the version of Ren'Py the updater will download. Please select an update channel:"
new "更新頻道控制 Ren'Py 更新將會下載的版本。請選擇一個更新頻道。"
# game/updater.rpy:70
old "Release"
new "Release"
# game/updater.rpy:76
old "{b}Recommended.{/b} The version of Ren'Py that should be used in all newly-released games."
new "{b}推薦{/b} Ren'py 的版本應在所有新釋出的遊戲中使用。"
# game/updater.rpy:81
old "Prerelease"
new "Prerelease"
# game/updater.rpy:87
old "A preview of the next version of Ren'Py that can be used for testing and taking advantage of new features, but not for final releases of games."
new "預覽 Ren'py 的下一個版本,適合用於測試和體驗新功能,但不適合遊戲的最終版本。"
# game/updater.rpy:93
old "Experimental"
new "Experimental"
# game/updater.rpy:99
old "Experimental versions of Ren'Py. You shouldn't select this channel unless asked by a Ren'Py developer."
new "Ren'py 的實驗版本,除非您被 Ren'py 的開發者要求,否則您不應該選擇此頻道。"
# game/updater.rpy:119
old "An error has occured:"
new "一個錯誤發生:"
# game/updater.rpy:121
old "Checking for updates."
new "檢查更新"
# game/updater.rpy:123
old "Ren'Py is up to date."
new "Ren'Py 已是最新版本"
# game/updater.rpy:125
old "[u.version] is now available. Do you want to install it?"
new "[u.version] 已經可以更新。你要下載它嗎?"
# game/updater.rpy:127
old "Preparing to download the update."
new "準備下載更新。"
# game/updater.rpy:129
old "Downloading the update."
new "正在下載更新。"
# game/updater.rpy:131
old "Unpacking the update."
new "正在解壓縮更新檔案。"
# game/updater.rpy:133
old "Finishing up."
new "即將完成。"
# game/updater.rpy:135
old "The update has been installed. Ren'Py will restart."
new "已安裝更新。Ren'Py 將會重新啟動。"
# game/updater.rpy:137
old "The update has been installed."
new "已安裝更新。"
# game/updater.rpy:139
old "The update was cancelled."
new "已取消更新。"
# game/updater.rpy:156
old "Ren'Py Update"
new "Ren'Py 更新"
# game/updater.rpy:162
old "Proceed"
new "繼續"
translate traditional_chinese strings:
# game/updater.rpy:129
old "Nightly"
new "Nightly"
# game/updater.rpy:135
old "The bleeding edge of Ren'Py development. This may have the latest features, or might not run at all."
new "Ren'Py 最新的開發版本。這可能會擁有最新的功能,或可能根本無法運作。"
+31 -34
View File
@@ -25,8 +25,7 @@
* 11/08/2004 - Compr fix, levels -1,1-7 now work - Tyler Montbriand
*/
#include <stdlib.h>
#include <SDL/SDL.h>
#include <SDL/SDL_byteorder.h>
#include <SDL.h>
#include <png.h>
#include <zlib.h>
#include "IMG_savepng.h"
@@ -38,7 +37,7 @@
int IMG_SavePNG(const char *file, SDL_Surface *surf,int compression){
SDL_RWops *fp;
int ret;
fp=SDL_RWFromFile(file,"wb");
if( fp == NULL ) {
@@ -60,24 +59,25 @@ int IMG_SavePNG_RW(SDL_RWops *src, SDL_Surface *surf,int compression){
png_infop info_ptr;
SDL_PixelFormat *fmt=NULL;
SDL_Surface *tempsurf=NULL;
int ret,funky_format,used_alpha;
unsigned int i,temp_alpha;
int ret,funky_format;
unsigned int i;
png_colorp palette;
Uint8 *palette_alpha=NULL;
png_byte **row_pointers=NULL;
png_ptr=NULL;info_ptr=NULL;palette=NULL;ret=-1;
funky_format=0;
SDL_BlendMode temp_blend;
if( !src || !surf) {
goto savedone; /* Nothing to do. */
}
row_pointers=(png_byte **)malloc(surf->h * sizeof(png_byte*));
if (!row_pointers) {
if (!row_pointers) {
SDL_SetError("Couldn't allocate memory for rowpointers");
goto savedone;
}
png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,NULL,NULL);
if (!png_ptr){
SDL_SetError("Couldn't allocate memory for PNG file");
@@ -126,19 +126,19 @@ int IMG_SavePNG_RW(SDL_RWops *src, SDL_Surface *surf,int compression){
palette[i].blue=fmt->palette->colors[i].b;
}
png_set_PLTE(png_ptr,info_ptr,palette,fmt->palette->ncolors);
if (surf->flags&SDL_SRCCOLORKEY) {
palette_alpha=(Uint8 *)malloc((fmt->colorkey+1)*sizeof(Uint8));
if (!palette_alpha) {
SDL_SetError("Couldn't create memory for palette transparency");
goto savedone;
}
/* FIXME: memset? */
for (i=0;i<(fmt->colorkey+1);i++) {
palette_alpha[i]=255;
}
palette_alpha[fmt->colorkey]=0;
png_set_tRNS(png_ptr,info_ptr,palette_alpha,fmt->colorkey+1,NULL);
}
// if (surf->flags&SDL_SRCCOLORKEY) {
// palette_alpha=(Uint8 *)malloc((fmt->colorkey+1)*sizeof(Uint8));
// if (!palette_alpha) {
// SDL_SetError("Couldn't create memory for palette transparency");
// goto savedone;
// }
// /* FIXME: memset? */
// for (i=0;i<(fmt->colorkey+1);i++) {
// palette_alpha[i]=255;
// }
// palette_alpha[fmt->colorkey]=0;
// png_set_tRNS(png_ptr,info_ptr,palette_alpha,fmt->colorkey+1,NULL);
// }
}else{ /* Truecolor */
if (fmt->Amask) {
png_set_IHDR(png_ptr,info_ptr,
@@ -172,11 +172,11 @@ int IMG_SavePNG_RW(SDL_RWops *src, SDL_Surface *surf,int compression){
}else{
/* Check for RGB/BGR/GBR/RBG/etc surfaces.*/
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
if(fmt->Rmask!=0xFF0000
if(fmt->Rmask!=0xFF0000
|| fmt->Gmask!=0x00FF00
|| fmt->Bmask!=0x0000FF){
#else
if(fmt->Rmask!=0x0000FF
if(fmt->Rmask!=0x0000FF
|| fmt->Gmask!=0x00FF00
|| fmt->Bmask!=0xFF0000){
#endif
@@ -185,7 +185,7 @@ int IMG_SavePNG_RW(SDL_RWops *src, SDL_Surface *surf,int compression){
}
}else if (fmt->BytesPerPixel==4){
if (!fmt->Amask) { /* check for 32bit but no alpha */
funky_format=1;
funky_format=1;
}else{
/* Check for ARGB/ABGR/GBAR/RABG/etc surfaces.*/
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
@@ -228,21 +228,18 @@ int IMG_SavePNG_RW(SDL_RWops *src, SDL_Surface *surf,int compression){
SDL_SetError("Couldn't allocate temp surface");
goto savedone;
}
if(surf->flags&SDL_SRCALPHA){
temp_alpha=fmt->alpha;
used_alpha=1;
SDL_SetAlpha(surf,0,255); /* Set for an opaque blit */
}else{
used_alpha=0;
}
SDL_GetSurfaceBlendMode(surf, &temp_blend);
SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_NONE);
if(SDL_BlitSurface(surf,NULL,tempsurf,NULL)!=0){
SDL_SetError("Couldn't blit surface to temp surface");
SDL_FreeSurface(tempsurf);
goto savedone;
}
if (used_alpha) {
SDL_SetAlpha(surf,SDL_SRCALPHA,(Uint8)temp_alpha); /* Restore alpha settings*/
}
SDL_SetSurfaceBlendMode(surf, temp_blend);
for(i=0;i<tempsurf->h;i++){
row_pointers[i]= ((png_byte*)tempsurf->pixels) + i*tempsurf->pitch;
}
+5 -9
View File
@@ -23,17 +23,13 @@
def version():
return (6, 12, 0)
cdef extern from "pygame/pygame.h":
cdef struct SDL_RWops:
pass
void import_pygame_rwobject()
SDL_RWops* RWopsFromPython(object obj)
from sdl2 cimport *
from pygame_sdl2 cimport *
cdef extern from "renpy.h":
void core_init()
void subpixel_init()
void save_png_core(object, SDL_RWops *, int)
@@ -425,6 +421,6 @@ def subpixel(pysrc, pydst, xoffset, yoffset, shift):
# Be sure to update scale.py when adding something new here!
import_pygame_rwobject()
import_pygame_sdl2__rwobject()
core_init()
subpixel_init()
+172 -172
View File
File diff suppressed because it is too large Load Diff
+6 -3
View File
@@ -19,7 +19,6 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <pygame/pygame.h>
#include <math.h>
#include <limits.h>
#include <libavutil/avstring.h>
@@ -35,6 +34,8 @@
#include <SDL.h>
#include <SDL_thread.h>
#include <pygame_sdl2/pygame_sdl2.surface_api.h>
#ifdef __MINGW32__
#undef main /* We don't want SDL to override our main() */
#endif
@@ -1296,7 +1297,7 @@ static int stream_component_open(VideoState *is, int stream_index)
is->video_current_pts_time = av_gettime();
packet_queue_init(&is->videoq);
is->video_tid = SDL_CreateThread(video_thread, is);
is->video_tid = SDL_CreateThread(video_thread, "video_thread", is);
break;
default:
break;
@@ -1606,7 +1607,7 @@ VideoState *ffpy_stream_open(SDL_RWops *rwops, const char *filename)
is->quit_mutex = SDL_CreateMutex();
is->quit_cond = SDL_CreateCond();
is->parse_tid = SDL_CreateThread(decode_thread, is);
is->parse_tid = SDL_CreateThread(decode_thread, "decode_thread", is);
is->first_frame = 1;
@@ -1665,6 +1666,8 @@ void ffpy_init(int rate, int status) {
ffpy_did_init = 1;
import_pygame_sdl2__surface();
show_status = status;
audio_sample_rate = rate;
+3 -1
View File
@@ -128,12 +128,13 @@ style_properties = sorted_dict(
child = 'renpy.easy.displayable_or_none',
clipping = None,
color = 'renpy.easy.color',
debug = None,
drop_shadow = None,
drop_shadow_color = 'renpy.easy.color',
first_indent = None,
first_spacing = None,
fit_first = None,
focus_mask = None,
focus_mask = 'expand_focus_mask',
focus_rect = None,
font = None,
fore_bar = 'none_is_null',
@@ -144,6 +145,7 @@ style_properties = sorted_dict(
italic = None,
justify = None,
kerning = None,
keyboard_focus = None,
language = None,
layout = None,
line_leading = None,
+10
View File
@@ -0,0 +1,10 @@
from sdl2 cimport SDL_Surface, SDL_RWops
cdef extern from "pygame_sdl2/pygame_sdl2.rwobject_api.h":
void import_pygame_sdl2__rwobject()
SDL_RWops* RWopsFromPython(object obj)
cdef extern from "pygame_sdl2/pygame_sdl2.surface_api.h":
int import_pygame_sdl2__surface()
SDL_Surface *PySurface_AsSurface(object)
File diff suppressed because it is too large Load Diff
+108 -108
View File
@@ -23,8 +23,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "pss.h"
#include <Python.h>
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>
#include <SDL.h>
#include <SDL_thread.h>
#include <stdio.h>
/* Declarations of ffdecode functions. */
@@ -37,7 +37,7 @@ int ffpy_refresh_event(struct VideoState *vs);
void ffpy_init(int rate, int status);
int ffpy_audio_decode(struct VideoState *is, Uint8 *stream, int len);
/* The current Python. */
PyInterpreterState* interp;
PyThreadState* thread = NULL;
@@ -47,7 +47,7 @@ static void incref(PyObject *ref) {
PyEval_AcquireLock();
oldstate = PyThreadState_Swap(thread);
Py_INCREF(ref);
Py_INCREF(ref);
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
}
@@ -57,7 +57,7 @@ static void decref(PyObject *ref) {
PyEval_AcquireLock();
oldstate = PyThreadState_Swap(thread);
Py_DECREF(ref);
Py_DECREF(ref);
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
}
@@ -116,9 +116,9 @@ struct Channel {
/* Is the playing sample tight? */
int playing_tight;
/* The queued up sample. */
struct VideoState *queued;
struct VideoState *queued;
/* The name of the queued up sample. */
PyObject *queued_name;
@@ -128,17 +128,17 @@ struct Channel {
/* Is the queued sample tight? */
int queued_tight;
/* Is this channel paused? */
int paused;
/* The volume of the channel. */
int volume;
/* The position (in bytes) that this channel has queued to. */
int pos;
/*
/*
* The number of bytes for each step of fade.
* 0 when no fade is in progress.
*/
@@ -152,7 +152,7 @@ struct Channel {
/* The change in fade_vol for each step. */
int fade_delta;
/* The number of bytes in which we'll stop. */
int stop_bytes;
@@ -215,7 +215,7 @@ static float interpolate_pan(struct Channel *c) {
done = 1.0 * c->pan_done / c->pan_length;
return c->pan_start + done * (c->pan_end - c->pan_start);
}
static float interpolate_vol2(struct Channel *c) {
@@ -250,7 +250,7 @@ static void start_sample(struct Channel* c, int reset_fade) {
c->pos = 0;
if (reset_fade) {
if (c->playing_fadein == 0) {
c->fade_step_len = 0;
} else {
@@ -268,7 +268,7 @@ static void start_sample(struct Channel* c, int reset_fade) {
}
c->stop_bytes = -1;
}
}
}
static void free_sample(struct VideoState *ss) {
@@ -294,7 +294,7 @@ static void mixaudio(Uint8 *dst, Uint8 *src, int length, int volume) {
}
*sdst++ = (short) sound;
ssrc++;
ssrc++;
}
}
@@ -304,7 +304,7 @@ static void fade_mixaudio(struct Channel *c,
Uint8 *dst, Uint8 *src, int length) {
while (length) {
// No fade case.
if (c->fade_step_len == 0) {
mixaudio(dst, src, length, c->volume);
@@ -316,14 +316,14 @@ static void fade_mixaudio(struct Channel *c,
int l = min(c->fade_step_len - c->fade_off, length);
mixaudio(dst, src, l, c->fade_vol);
length -= l;
dst += l;
src += l;
c->fade_off += l;
continue;
}
// Otherwise, we have no space left in the current fade step.
// Go to the next step.
c->fade_off = 0;
@@ -333,14 +333,14 @@ static void fade_mixaudio(struct Channel *c,
if (c->fade_vol <= 0) {
c->fade_vol = 0;
}
// Stop on a fadein.
if (c->fade_vol >= c->volume) {
c->fade_vol = c->volume;
c->fade_step_len = 0;
}
}
return;
}
@@ -365,14 +365,14 @@ static void pan_audio(struct Channel *c, Uint8 *stream, int length) {
float vol2;
int left = 256;
int right = 256;
for (i = 0; i < length; i++) {
if ((i & 0x1f) == 0) {
pan = interpolate_pan(c);
vol2 = interpolate_vol2(c);
// If nothing to do, skip 32 samples.
if (pan == 0.0 && vol2 == 1.0) {
i += 31;
@@ -402,7 +402,7 @@ static void pan_audio(struct Channel *c, Uint8 *stream, int length) {
c->pan_done += 1;
c->vol2_done += 1;
}
}
static void callback(void *userdata, Uint8 *stream, int length) {
@@ -410,7 +410,7 @@ static void callback(void *userdata, Uint8 *stream, int length) {
for (channel = 0; channel < num_channels; channel++) {
int mixed = 0;
struct Channel *c = &channels[channel];
@@ -430,10 +430,10 @@ static void callback(void *userdata, Uint8 *stream, int length) {
// Decode some amount of data.
bytes = ffpy_audio_decode(c->playing, buffer, mixleft);
// We have some data in the buffer.
if (c->stop_bytes && bytes) {
if (c->stop_bytes != -1)
bytes = min(c->stop_bytes, bytes);
@@ -441,12 +441,12 @@ static void callback(void *userdata, Uint8 *stream, int length) {
fade_mixaudio(c, &stream[mixed], buffer, bytes);
mixed += bytes;
if (c->stop_bytes != -1)
c->stop_bytes -= bytes;
c->pos += bytes;
continue;
}
@@ -458,14 +458,14 @@ static void callback(void *userdata, Uint8 *stream, int length) {
int old_tight = c->playing_tight;
struct Dying *d;
post_event(c);
d = malloc(sizeof(struct Dying));
d->next = dying;
d->stream = c->playing;
dying = d;
LOCK_NAME();
decref(c->playing_name);
@@ -474,17 +474,17 @@ static void callback(void *userdata, Uint8 *stream, int length) {
c->playing_name = c->queued_name;
c->playing_fadein = c->queued_fadein;
c->playing_tight = c->queued_tight;
c->queued = NULL;
c->queued_name = NULL;
c->queued_fadein = 0;
c->queued_tight = 0;
UNLOCK_NAME();
start_sample(c, ! old_tight);
continue;
continue;
}
}
@@ -508,14 +508,14 @@ static int check_channel(int c) {
if (c >= num_channels) {
channels = realloc(channels, sizeof(struct Channel) * (c + 1));
for (i = num_channels; i <= c; i++) {
channels[i].playing = NULL;
channels[i].queued = NULL;
channels[i].playing_name = NULL;
channels[i].queued_name = NULL;
channels[i].volume = SDL_MIX_MAXVOLUME;
channels[i].volume = SDL_MIX_MAXVOLUME;
channels[i].paused = 1;
channels[i].event = 0;
channels[i].pan_start = 0.0;
@@ -530,7 +530,7 @@ static int check_channel(int c) {
num_channels = c + 1;
}
return 0;
}
@@ -540,16 +540,16 @@ static int check_channel(int c) {
* failure.
*/
struct VideoState *load_sample(SDL_RWops *rw, const char *ext) {
struct VideoState *rv;
struct VideoState *rv;
rv = ffpy_stream_open(rw, ext);
return rv;
}
void PSS_play(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int fadein, int tight, int paused) {
BEGIN();
struct Channel *c;
if (check_channel(channel)) {
@@ -558,7 +558,7 @@ void PSS_play(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int f
c = &channels[channel];
ENTER();
LOCK_NAME();
/* Free playing and queued samples. */
@@ -569,7 +569,7 @@ void PSS_play(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int f
c->playing_name = NULL;
c->playing_tight = 0;
}
if (c->queued) {
free_sample(c->queued);
c->queued = NULL;
@@ -579,7 +579,7 @@ void PSS_play(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int f
}
/* Allocate playing sample. */
c->playing = load_sample(rw, ext);
if (! c->playing) {
@@ -594,11 +594,11 @@ void PSS_play(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int f
c->playing_fadein = fadein;
c->playing_tight = tight;
c->paused = paused;
start_sample(c, 1);
start_sample(c, 1);
/* update_pause(); */
UNLOCK_NAME();
EXIT();
@@ -608,7 +608,7 @@ void PSS_play(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int f
void PSS_queue(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int fadein, int tight) {
BEGIN();
struct Channel *c;
if (check_channel(channel)) {
@@ -616,18 +616,18 @@ void PSS_queue(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int
}
c = &channels[channel];
ENTER();
/* If we're not playing, then we should play instead of queue. */
if (!c->playing) {
EXIT();
PSS_play(channel, rw, ext, name, fadein, tight, 0);
return;
return;
}
/* Free queued sample. */
if (c->queued) {
free_sample(c->queued);
c->queued = NULL;
@@ -635,7 +635,7 @@ void PSS_queue(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int
c->queued_name = NULL;
c->queued_tight = 0;
}
/* Allocate queued sample. */
c->queued = load_sample(rw, ext);
@@ -649,11 +649,11 @@ void PSS_queue(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int
c->queued_name = name;
c->queued_fadein = fadein;
c->queued_tight = tight;
EXIT();
error(SUCCESS);
}
/*
* Stops all music from playing, freeing the data used by the
@@ -676,7 +676,7 @@ void PSS_stop(int channel) {
if (c->playing) {
post_event(c);
}
/* Free playing and queued samples. */
if (c->playing) {
free_sample(c->playing);
@@ -684,18 +684,18 @@ void PSS_stop(int channel) {
decref(c->playing_name);
c->playing_name = NULL;
}
if (c->queued) {
free_sample(c->queued);
c->queued = NULL;
decref(c->queued_name);
c->queued_name = NULL;
}
/* update_pause(); */
UNLOCK_NAME();
EXIT();
EXIT();
error(SUCCESS);
}
@@ -731,8 +731,8 @@ void PSS_dequeue(int channel, int even_tight) {
}
EXIT();
EXIT();
error(SUCCESS);
}
@@ -757,8 +757,8 @@ int PSS_queue_depth(int channel) {
if (c->playing) rv++;
if (c->queued) rv++;
EXIT();
EXIT();
error(SUCCESS);
return rv;
@@ -767,7 +767,7 @@ int PSS_queue_depth(int channel) {
PyObject *PSS_playing_name(int channel) {
BEGIN();
PyObject *rv;
struct Channel *c;
if (check_channel(channel)) {
@@ -795,7 +795,7 @@ PyObject *PSS_playing_name(int channel) {
ALTEXIT();
error(SUCCESS);
return rv;
}
@@ -826,7 +826,7 @@ void PSS_fadeout(int channel, int ms) {
}
fade_steps = c->volume;
c->fade_delta = -1;
c->fade_off = 0;
@@ -838,15 +838,15 @@ void PSS_fadeout(int channel, int ms) {
} else {
c->fade_step_len = 0;
}
c->stop_bytes = ms_to_bytes(ms);
c->queued_tight = 0;
if (!c->queued) {
c->playing_tight = 0;
}
EXIT();
EXIT();
error(SUCCESS);
}
@@ -856,7 +856,7 @@ void PSS_fadeout(int channel, int ms) {
*/
void PSS_pause(int channel, int pause) {
BEGIN();
struct Channel *c;
if (check_channel(channel)) {
@@ -872,13 +872,13 @@ void PSS_pause(int channel, int pause) {
EXIT();
error(SUCCESS);
}
void PSS_unpause_all(void) {
int i;
BEGIN();
ENTER();
@@ -890,7 +890,7 @@ void PSS_unpause_all(void) {
EXIT();
error(SUCCESS);
}
/*
@@ -901,7 +901,7 @@ int PSS_get_pos(int channel) {
struct Channel *c;
BEGIN();
if (check_channel(channel)) {
return -1;
}
@@ -909,7 +909,7 @@ int PSS_get_pos(int channel) {
c = &channels[channel];
ENTER();
if (c->playing) {
rv = bytes_to_ms(c->pos);
} else {
@@ -917,7 +917,7 @@ int PSS_get_pos(int channel) {
}
EXIT();
error(SUCCESS);
return rv;
}
@@ -929,7 +929,7 @@ int PSS_get_pos(int channel) {
void PSS_set_endevent(int channel, int event) {
struct Channel *c;
BEGIN();
if (check_channel(channel)) {
return;
}
@@ -937,12 +937,12 @@ void PSS_set_endevent(int channel, int event) {
c = &channels[channel];
ENTER();
c->event = event;
EXIT();
error(SUCCESS);
error(SUCCESS);
}
/*
@@ -952,7 +952,7 @@ void PSS_set_endevent(int channel, int event) {
void PSS_set_volume(int channel, float volume) {
struct Channel *c;
BEGIN();
if (check_channel(channel)) {
return;
}
@@ -960,12 +960,12 @@ void PSS_set_volume(int channel, float volume) {
c = &channels[channel];
ENTER();
c->volume = (int) (volume * SDL_MIX_MAXVOLUME);
EXIT();
error(SUCCESS);
error(SUCCESS);
}
@@ -973,10 +973,10 @@ void PSS_set_volume(int channel, float volume) {
float PSS_get_volume(int channel) {
float rv;
struct Channel *c;
BEGIN();
if (check_channel(channel)) {
return 0.0;
}
@@ -984,12 +984,12 @@ float PSS_get_volume(int channel) {
c = &channels[channel];
ENTER();
rv = 1.0 * c->volume / SDL_MIX_MAXVOLUME;
EXIT();
error(SUCCESS);
error(SUCCESS);
return rv;
}
@@ -1000,7 +1000,7 @@ float PSS_get_volume(int channel) {
void PSS_set_pan(int channel, float pan, float delay) {
struct Channel *c;
BEGIN();
if (check_channel(channel)) {
return;
}
@@ -1008,15 +1008,15 @@ void PSS_set_pan(int channel, float pan, float delay) {
c = &channels[channel];
ENTER();
c->pan_start = interpolate_pan(c);
c->pan_end = pan;
c->pan_length = (int) (audio_spec.freq * delay);
c->pan_done = 0;
EXIT();
error(SUCCESS);
error(SUCCESS);
}
/*
@@ -1025,7 +1025,7 @@ void PSS_set_pan(int channel, float pan, float delay) {
void PSS_set_secondary_volume(int channel, float vol2, float delay) {
struct Channel *c;
BEGIN();
if (check_channel(channel)) {
return;
}
@@ -1038,10 +1038,10 @@ void PSS_set_secondary_volume(int channel, float vol2, float delay) {
c->vol2_end = vol2;
c->vol2_length = (int) (audio_spec.freq * delay);
c->vol2_done = 0;
EXIT();
error(SUCCESS);
error(SUCCESS);
}
@@ -1064,12 +1064,12 @@ void PSS_init(int freq, int stereo, int samples, int status) {
interp = thread->interp;
thread = PyThreadState_New(interp);
}
if (!thread) {
error(SDL_ERROR);
return;
}
if (SDL_Init(SDL_INIT_AUDIO)) {
error(SDL_ERROR);
return;
@@ -1090,7 +1090,7 @@ void PSS_init(int freq, int stereo, int samples, int status) {
ffpy_init(audio_spec.freq, status);
SDL_PauseAudio(0);
initialized = 1;
error(SUCCESS);
@@ -1102,7 +1102,7 @@ void PSS_quit() {
if (! initialized) {
return;
}
int i;
ENTER();
@@ -1130,14 +1130,14 @@ void PSS_periodic() {
}
ENTER();
while (dying) {
struct Dying *d = dying;
ffpy_stream_close(d->stream);
dying = d->next;
free(d);
}
EXIT();
}
+1 -1
View File
@@ -25,7 +25,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define PSS_H
#include <Python.h>
#include <SDL/SDL.h>
#include <SDL.h>
void PSS_play(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int fadeout, int tight, int paused);
void PSS_queue(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int fadeout, int tight);
+5 -3
View File
@@ -20,11 +20,13 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
cdef extern from "pygame/pygame.h":
cdef extern from "SDL.h":
cdef struct SDL_RWops:
pass
void import_pygame_rwobject()
cdef extern from "pygame_sdl2/pygame_sdl2.rwobject_api.h":
void import_pygame_sdl2__rwobject()
SDL_RWops* RWopsFromPythonThreaded(object obj)
cdef extern from "pss.h":
@@ -187,4 +189,4 @@ def check_version(version):
if version < 2 or version > 4:
raise Exception("pysdlsound version mismatch.")
import_pygame_rwobject()
import_pygame_sdl2__rwobject()
+4 -3
View File
@@ -2,9 +2,10 @@
#define RENPY_H
#include <Python.h>
#include <SDL/SDL.h>
#include <SDL.h>
void core_init(void);
void subpixel_init(void);
void save_png_core(PyObject *pysurf, SDL_RWops *file, int compress);
@@ -62,7 +63,7 @@ void alphamunge_core(PyObject *pysrc,
int src_aoff, // alpha offset.
int dst_aoff, // alpha offset.
char *amap);
/* int stretch_core(PyObject *pysrc, */
/* PyObject *pydst, */
/* int x, */
@@ -114,4 +115,4 @@ int subpixel32(
float xoffset, float yoffset, int ashift);
#endif
#endif
+3 -3
View File
@@ -65,7 +65,7 @@ else:
include("zlib.h")
include("png.h")
include("SDL.h", directory="SDL")
include("SDL.h", directory="SDL2")
include("ft2build.h")
include("freetype/freetype.h", directory="freetype2", optional=True) or include("freetype.h", directory="freetype2")
include("libavutil/avstring.h")
@@ -74,7 +74,7 @@ include("libavcodec/avcodec.h")
include("libswscale/swscale.h")
include("GL/glew.h")
library("SDL")
library("SDL2")
library("png")
library("avformat")
library("avcodec")
@@ -91,7 +91,7 @@ has_angle = windows and library("EGL", optional=True) and library("GLESv2", opti
if android:
sdl = [ 'sdl', 'GLESv2', 'log' ]
else:
sdl = [ 'SDL' ]
sdl = [ 'SDL2' ]
if has_fribidi:
try:
+41 -35
View File
@@ -7,15 +7,21 @@
*/
#include "renpy.h"
#include <pygame/pygame.h>
#include <SDL.h>
#include <pygame_sdl2/pygame_sdl2.surface_api.h>
#include <stdio.h>
#include <math.h>
void subpixel_init() {
import_pygame_sdl2__surface();
}
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
#define GCC_MMX 1
#include "mmx.h"
#endif
/* MMX Register assignments (between applications of the blitter core).
*
* mm0 - new row 0
@@ -33,7 +39,7 @@
* This is the basic algorithm that does the subpixel blit
* interpolation.
*************************************************************************/
/* mm2 = destination; */
/* unpack mm0; */
@@ -83,8 +89,8 @@
#define MMX_EXPAND() \
pxor_r2r(mm2, mm2); \
punpcklbw_r2r(mm2, mm4); \
punpcklbw_r2r(mm2, mm5);
punpcklbw_r2r(mm2, mm5);
// This expects the two old pixels to be arranged like:
// mm4 mm0
@@ -140,7 +146,7 @@ int subpixel32(PyObject *pysrc, PyObject *pydst,
float xoffset, float yoffset, int ashift) {
SDL_Surface *src;
SDL_Surface *dst;
int srcpitch, dstpitch;
int srcw, srch;
int dstw, dsth;
@@ -148,7 +154,7 @@ int subpixel32(PyObject *pysrc, PyObject *pydst,
unsigned char *dstpixels;
int xfrac, yfrac;
int xo, yo;
int xo, yo;
int sx, sy;
int draw_finalx;
@@ -158,7 +164,7 @@ int subpixel32(PyObject *pysrc, PyObject *pydst,
unsigned int pixel;
unsigned int blankpixel;
unsigned char *s0;
unsigned char *s1;
unsigned char *d;
@@ -170,12 +176,12 @@ int subpixel32(PyObject *pysrc, PyObject *pydst,
if (!SDL_HasMMX()) {
return 0;
}
src = PySurface_AsSurface(pysrc);
dst = PySurface_AsSurface(pydst);
Py_BEGIN_ALLOW_THREADS
srcpixels = (unsigned char *) src->pixels;
dstpixels = (unsigned char *) dst->pixels;
srcpitch = src->pitch;
@@ -186,7 +192,7 @@ int subpixel32(PyObject *pysrc, PyObject *pydst,
dsth = dst->h;
inverted_alpha_mask = ~(0xff << ashift);
// Due to mmx.
ashift *= 2;
@@ -216,7 +222,7 @@ int subpixel32(PyObject *pysrc, PyObject *pydst,
if (sx >= srcw - 1 || sy >= srch - 1) {
goto done;
}
// Figure out how many pixels we need to draw on each line.
normal_pixels = min(srcw - sx - 1, dstw - xo);
@@ -236,7 +242,7 @@ int subpixel32(PyObject *pysrc, PyObject *pydst,
punpckldq_r2r(mm7, mm7);
movd_m2r(ashift, mm3);
if (xo >= dstw) {
goto done;
}
@@ -244,27 +250,27 @@ int subpixel32(PyObject *pysrc, PyObject *pydst,
// Draw the first line, when sy == -1.
if (sy == -1) {
if (yo >= dsth) {
goto done;
}
s1 = srcpixels + sx * 4;
if (sx < 0) {
pixel = (* (unsigned int *) (s1 + 4)) & inverted_alpha_mask;
pixel = (* (unsigned int *) (s1 + 4)) & inverted_alpha_mask;
} else {
pixel = * (unsigned int *) s1;
}
blankpixel = pixel & inverted_alpha_mask;
movd_m2r(blankpixel, mm4);
movd_m2r(pixel, mm5);
MMX_EXPAND();
s1 += 4;
d = dstpixels + xo * 4 + yo * dstpitch;
dend = d + normal_pixels * 4;
@@ -272,7 +278,7 @@ int subpixel32(PyObject *pysrc, PyObject *pydst,
pixel = * (unsigned int *) s1;
blankpixel = pixel & inverted_alpha_mask;
movd_m2r(blankpixel, mm0);
movd_m2r(pixel, mm1);
movd_m2r(pixel, mm1);
MMX_INTERP(* (unsigned int *) d);
d += 4;
s1 += 4;
@@ -300,7 +306,7 @@ int subpixel32(PyObject *pysrc, PyObject *pydst,
s0 = srcpixels + sx * 4 + (srcpitch * sy);
s1 = srcpixels + sx * 4 + (srcpitch * (sy + 1));
if (sx < 0) {
blankpixel = (* (unsigned int *) (s0 + 4)) & inverted_alpha_mask;
movd_m2r(blankpixel, mm4);
@@ -314,20 +320,20 @@ int subpixel32(PyObject *pysrc, PyObject *pydst,
}
MMX_EXPAND();
s0 += 4;
s1 += 4;
d = dstpixels + xo * 4 + yo * dstpitch;
dend = d + normal_pixels * 4;
unsigned char *dp = d;
while (d != dend) {
movd_m2r(* (unsigned int *) s0, mm0);
movd_m2r(* (unsigned int *) s1, mm1);
movd_m2r(* (unsigned int *) s0, mm0);
movd_m2r(* (unsigned int *) s1, mm1);
MMX_INTERP(* (unsigned int *) d);
d += 4;
s0 += 4;
s1 += 4;
@@ -350,7 +356,7 @@ int subpixel32(PyObject *pysrc, PyObject *pydst,
}
// The final part, where we handle the bottom line of the source surface.
if (yo >= dsth) {
goto done;
}
@@ -360,29 +366,29 @@ int subpixel32(PyObject *pysrc, PyObject *pydst,
if (sx < 0) {
pixel = * (unsigned int *) (s0 + 4);
blankpixel = pixel & inverted_alpha_mask;
movd_m2r(blankpixel, mm4);
movd_m2r(blankpixel, mm5);
} else {
pixel = * (unsigned int *) s0;
blankpixel = pixel & inverted_alpha_mask;
movd_m2r(pixel, mm4);
movd_m2r(blankpixel, mm5);
}
MMX_EXPAND();
s0 += 4;
d = dstpixels + xo * 4 + yo * dstpitch;
dend = d + normal_pixels * 4;
while (d != dend) {
pixel = * (unsigned int *) s0;
blankpixel = pixel & inverted_alpha_mask;
movd_m2r(pixel, mm0);
movd_m2r(pixel, mm0);
movd_m2r(blankpixel, mm1);
MMX_INTERP(* (unsigned int *) d);
d += 4;
@@ -395,7 +401,7 @@ int subpixel32(PyObject *pysrc, PyObject *pydst,
MMX_INTERP(* (unsigned int *) d);
}
done:
// Reset the MMX unit and call it a night.
@@ -403,7 +409,7 @@ done:
Py_END_ALLOW_THREADS
return 1;
}
-1
View File
@@ -110,7 +110,6 @@ def path_to_saves(gamedir):
# the launcher, usually.)
def path_to_renpy_base():
renpy_base = os.path.dirname(os.path.realpath(sys.argv[0]))
renpy_base = os.environ.get('RENPY_BASE', renpy_base)
renpy_base = os.path.abspath(renpy_base)
return renpy_base
+5 -1
View File
@@ -40,7 +40,7 @@ except ImportError:
vc_version = 0
# The tuple giving the version number.
version_tuple = (6, 18, 0, vc_version)
version_tuple = (6, 18, 3, vc_version)
# The name of this version.
version_name = "... through shared popular culture."
@@ -323,6 +323,7 @@ def import_all():
import renpy.sl2
update_path(renpy.sl2)
import renpy.sl2.slast
import renpy.sl2.slparser
import renpy.sl2.sldisplayables
@@ -330,6 +331,9 @@ def import_all():
import renpy.warp
import renpy.editor
import renpy.memory
import renpy.exports
import renpy.character # depends on exports. @UnresolvedImport
+35 -14
View File
@@ -478,7 +478,14 @@ def eval_who(who, fast=None):
fast = bool(re.match(renpy.parser.word_regexp + "$", who))
if fast:
rv = renpy.python.store_dicts['store'].get(who, None)
if 'store.character' in renpy.python.store_dicts:
rv = renpy.python.store_dicts['store.character'].get(who, None)
else:
rv = None
if rv is None:
rv = renpy.python.store_dicts['store'].get(who, None)
if rv is None:
raise Exception("Sayer '%s' is not defined." % who.encode("utf-8"))
@@ -1276,10 +1283,13 @@ class Call(Node):
renpy.store._kwargs = kwargs
def predict(self):
label = self.label
if self.expression:
return [ ]
else:
return [ renpy.game.script.lookup(self.label) ]
label = renpy.python.py_eval(label)
return [ renpy.game.context().predict_call(label, self.next.name) ]
def scry(self):
rv = Node.scry(self)
@@ -1321,11 +1331,8 @@ class Return(Node):
next_node(renpy.game.context().lookup_return(pop=True))
def predict(self):
site = renpy.game.context().lookup_return(pop=False)
if site:
return [ site ]
else:
return [ ]
return [ renpy.game.context().predict_return() ]
def scry(self):
rv = Node.scry(self)
@@ -1675,9 +1682,14 @@ class Define(Node):
__slots__ = [
'varname',
'code',
'store',
]
def __init__(self, loc, name, expr):
def __setstate__(self, state):
self.store = 'store'
Node.__setstate__(self, state)
def __init__(self, loc, store, name, expr):
"""
@param name: The name of the image being defined.
@@ -1687,11 +1699,15 @@ class Define(Node):
super(Define, self).__init__(loc)
self.store = store
self.varname = name
self.code = PyCode(expr, loc=loc, mode='eval')
def diff_info(self):
return (Define, tuple(self.varname))
return (Define, self.store, self.varname)
def early_execute(self):
renpy.python.create_store(self.store)
def execute(self):
@@ -1699,9 +1715,14 @@ class Define(Node):
statement_name("define")
value = renpy.python.py_eval_bytecode(self.code.bytecode)
renpy.dump.definitions.append((self.varname, self.filename, self.linenumber))
renpy.exports.pure(self.varname)
renpy.python.store_dicts["store"][self.varname] = value
if self.store == 'store':
renpy.exports.pure(self.varname)
renpy.dump.definitions.append((self.varname, self.filename, self.linenumber))
else:
renpy.dump.definitions.append((self.store[6:] + "." + self.varname, self.filename, self.linenumber))
renpy.python.store_dicts[self.store][self.varname] = value
class Screen(Node):
+50 -19
View File
@@ -87,6 +87,7 @@ PROPERTIES = {
"angle" : float,
"radius" : float,
"crop" : (float, float, float, float),
"crop_relative" : bool,
"size" : (int, int),
"corner1" : (float, float),
"corner2" : (float, float),
@@ -193,7 +194,7 @@ def compile_all():
global compile_queue
for i in compile_queue:
if i.atl.constant:
if i.atl.constant == GLOBAL_CONST:
i.compile()
compile_queue = [ ]
@@ -224,6 +225,7 @@ class ATLTransformBase(renpy.object.Object):
# Compatibility with older saves.
parameters = renpy.ast.ParameterInfo([ ], [ ], None, None)
parent_transform = None
atl_st_offset = 0
nosave = [ 'parent_transform' ]
@@ -273,6 +275,9 @@ class ATLTransformBase(renpy.object.Object):
# The parent transform that was called to create this transform.
self.parent_transform = None
# The offset between st and when this ATL block first executed.
self.atl_st_offset = 0
if renpy.game.context().init_phase:
compile_queue.append(self)
@@ -284,7 +289,11 @@ class ATLTransformBase(renpy.object.Object):
super(ATLTransformBase, self).take_execution_state(t)
if t.atl is not self.atl:
self.atl_st_offset = None
if not isinstance(t, ATLTransformBase):
return
elif t.atl is not self.atl:
return
# Important to do it this way, so we use __eq__. The exception handling
@@ -307,6 +316,8 @@ class ATLTransformBase(renpy.object.Object):
self.st_offset = t.st_offset
self.at_offset = t.at_offset
self.atl_st_offset = t.atl_st_offset
if self.child is renpy.display.motion.null:
self.child = t.child
self.raw_child = t.raw_child
@@ -361,7 +372,7 @@ class ATLTransformBase(renpy.object.Object):
child = renpy.display.motion.get_null()
# Create a new ATL Transform.
parameters = renpy.ast.ParameterInfo({}, positional, None, None)
parameters = renpy.ast.ParameterInfo({ }, positional, None, None)
rv = renpy.display.motion.ATLTransform(
atl=self.atl,
@@ -382,10 +393,18 @@ class ATLTransformBase(renpy.object.Object):
properties.
"""
if self.parameters.positional and self.parameters.positional[0][1] is None:
raise Exception("Cannot compile ATL Transform, as it's missing positional parameter %s." % self.parameters.positional[0])
constant = (self.atl.constant == GLOBAL_CONST)
if self.atl.constant and self.parent_transform:
if not constant:
for p in self.parameters.positional:
if p not in self.context.context:
raise Exception("Cannot compile ATL Transform at %s:%d, as it's missing positional parameter %s." % (
self.atl.loc[0],
self.atl.loc[1],
self.parameters.positional[0],
))
if constant and self.parent_transform:
if self.parent_transform.block:
self.block = self.parent_transform.block
self.properties = self.parent_transform.properties
@@ -406,7 +425,7 @@ class ATLTransformBase(renpy.object.Object):
renpy.game.exception_info = old_exception_info
if self.atl.constant and self.parent_transform:
if constant and self.parent_transform:
self.parent_transform.block = self.block
self.parent_transform.properties = self.properties
self.parent_transform = None
@@ -442,17 +461,18 @@ class ATLTransformBase(renpy.object.Object):
old_exception_info = renpy.game.exception_info
if (self.atl_st_offset is None) or (st - self.atl_st_offset) < 0:
self.atl_st_offset = st
if self.atl.animation:
timebase = at
else:
timebase = st
timebase = st - self.atl_st_offset
action, arg, pause = self.block.execute(trans, timebase, self.atl_state, event)
renpy.game.exception_info = old_exception_info
# print "Executing", self, self.state, self.xpos, self.ypos
if action == "continue":
self.atl_state = arg
else:
@@ -1281,10 +1301,13 @@ class Time(Statement):
class RawOn(RawStatement):
def __init__(self, loc, name, block):
def __init__(self, loc, names, block):
super(RawOn, self).__init__(loc)
self.handlers = { name : block }
self.handlers = { }
for i in names:
self.handlers[i] = block
def compile(self, ctx): #@ReservedAssignment
@@ -1332,14 +1355,14 @@ class On(Statement):
# handle it.
if event in self.handlers:
# Do not allow people to abort the hide handler with another
# event.
if name != "hide":
# Do not allow people to abort the hide or replaced event.
lock_event = (name == "hide" and trans.hide_request) or (name == "replaced" and trans.replaced_request)
if not lock_event:
name = event
start = st
cstate = None
while True:
# If we don't have a handler, return until we change event.
@@ -1427,7 +1450,7 @@ class RawFunction(RawStatement):
return Function(self.loc, ctx.eval(self.expr))
def mark_constant(self):
self.constant = is_constant_expr(self.function)
self.constant = is_constant_expr(self.expr)
class Function(Statement):
@@ -1513,14 +1536,22 @@ def parse_atl(l):
elif l.keyword('on'):
name = l.require(l.word)
names = [ l.require(l.word) ]
while l.match(','):
name = l.word()
if name is None:
break
names.append(name)
l.require(':')
l.expect_eol()
l.expect_block('on')
block = parse_atl(l.subblock_lexer())
statements.append(RawOn(loc, name, block))
statements.append(RawOn(loc, names, block))
elif l.keyword('time'):
time = l.require(l.simple_expression)
+10 -7
View File
@@ -23,12 +23,6 @@
import renpy.audio
# A list of music channels.
music_channels = [ ]
# A list of channels for which set_music has been called, either way.
music_set = [ ]
from renpy.audio.audio import get_channel, get_serial
# Part of the public api:
@@ -256,6 +250,16 @@ def set_music(channel, flag, default=False):
c.default_loop = flag
c.default_loop_set = True
def is_music(channel):
"""
Returns true if "channel" will loop by default.
"""
c = get_channel(channel)
return c.default_loop
def get_delay(time, channel="music"):
"""
Returns the number of seconds left until the given time in the
@@ -307,7 +311,6 @@ def is_playing(channel="music"):
return (get_playing(channel=channel) is not None)
def set_volume(volume, delay=0, channel="music"):
"""
:doc: audio
+2
View File
@@ -225,6 +225,8 @@ def bootstrap(renpy_base):
if renpy.macintosh:
os.startfile = mac_start
import pygame_sdl2
pygame_sdl2.import_as_pygame()
# Check that we have installed pygame properly. This also deals with
# weird cases on Windows and Linux where we can't import modules. (On
+3 -1
View File
@@ -404,7 +404,7 @@ def display_say(
# Figure out which pause we're on. (Or set the pause to None in
# order to put us in all-at-once mode.)
if not interact:
if not interact or renpy.game.preferences.self_voicing:
all_at_once = True
dtt = DialogueTextTags(what)
@@ -476,6 +476,8 @@ def display_say(
what_text.slow = slow
what_text.slow_done = slow_done
what_text.update()
for c in callback:
c("show_done", interact=interact, type=type, **cb_args)
+27 -6
View File
@@ -35,27 +35,48 @@ init -1500 python:
The channel to play the sound on.
`file`
The file to play.
`selected`
If True, buttons using this action will be marked as selected
if the file is playing on the channel. If False, this action
will not cause the button to start playing. If None, the button
is marked selected if the channel is a music channel, and not
otherwise.
Any keyword arguments are passed to :func:`renpy.music.play`
Any other keyword arguments are passed to :func:`renpy.music.play`.
"""
equality_fields = ["channel", "file", "kwargs"]
equality_fields = ["channel", "file", "kwargs", "can_be_selected" ]
def __init__(self, channel, file, **kwargs):
can_be_selected = False
def __init__(self, channel, file, selected=None, **kwargs):
self.channel = channel
self.file = file
self.kwargs = kwargs
self.selected = self.get_selected()
if selected is None:
selected = renpy.music.is_music(channel)
self.can_be_selected = selected
self.get_selected()
def __call__(self):
renpy.music.play(self.file, channel=self.channel, **self.kwargs)
renpy.restart_interaction()
def get_selected(self):
return renpy.music.get_playing(self.channel) == self.file
if not self.can_be_selected:
self.selected = False
return False
self.selected = (renpy.music.get_playing(self.channel) == self.file)
return self.selected
def periodic(self, st):
if self.selected != self.get_selected():
if not self.can_be_selected:
return None
if self.selected != (renpy.music.get_playing(self.channel) == self.file):
renpy.restart_interaction()
return .1
+81 -12
View File
@@ -84,8 +84,9 @@ init -1600 python:
return self.dict[self.key] == self.value
@renpy.pure
def SetScreenVariable(name, value):
class SetScreenVariable(Action, FieldEquality):
"""
:doc: data_action
@@ -93,11 +94,34 @@ init -1600 python:
be set to `value`.
"""
cs = renpy.current_screen()
if cs is not None:
return SetDict(cs.scope, name, value)
else:
return None
identity_fields = [ "name", "value" ]
def __init__(self, name, value):
self.name = name
self.value = value
def __call__(self):
cs = renpy.current_screen()
if cs is None:
return
cs.scope[self.name] = self.value
renpy.restart_interaction()
def get_selected(self):
cs = renpy.current_screen()
if cs is None:
return False
if self.name not in cs.scope:
return False
return cs.scope[self.name] == self.value
@renpy.pure
class ToggleField(Action, FieldEquality):
@@ -162,6 +186,8 @@ init -1600 python:
return ToggleField(store, variable, true_value=true_value, false_value=false_value)
@renpy.pure
class ToggleDict(Action, FieldEquality):
"""
@@ -202,6 +228,9 @@ init -1600 python:
renpy.restart_interaction()
def get_selected(self):
if self.key not in self.dict:
return False
rv = self.dict[self.key]
if self.true_value is not None:
@@ -209,8 +238,10 @@ init -1600 python:
return rv
@renpy.pure
def ToggleScreenVariable(name, true_value=None, false_value=None):
class ToggleScreenVariable(Action, FieldEquality):
"""
:doc: data_action
@@ -222,10 +253,48 @@ init -1600 python:
If not None, then this is the false value we use.
"""
cs = renpy.current_screen()
identity_fields = [ "name", "true_value", "false_value" ]
if cs is not None:
return ToggleDict(cs.scope, name, true_value=true_value, false_value=None)
else:
return None
def __init__(self, name, true_value=None, false_value=None):
self.name = name
self.true_value = true_value
self.false_value = false_value
def __call__(self):
cs = renpy.current_screen()
if cs is None:
return
value = cs.scope[self.name]
if self.true_value is not None:
value = (value == self.true_value)
value = not value
if self.true_value is not None:
if value:
value = self.true_value
else:
value = self.false_value
cs.scope[self.name] = value
renpy.restart_interaction()
def get_selected(self):
cs = renpy.current_screen()
if cs is None:
return False
if self.name not in cs.scope:
return False
rv = cs.scope[self.name]
if self.true_value is not None:
rv = (rv == self.true_value)
return rv
+17 -6
View File
@@ -24,6 +24,8 @@ init -1500 python in iap:
from store import persistent, Action
import time
background = "black"
class Product(object):
"""
A data object representing a product.
@@ -105,6 +107,7 @@ init -1500 python in iap:
while True:
rv = self.devicePurchase.checkPurchaseResult()
if rv:
break
@@ -121,7 +124,6 @@ init -1500 python in iap:
def purchase(self, p, interact=True):
identifier = self.identifier(p)
self.devicePurchase.beginPurchase(identifier)
return self.wait_for_result(interact=interact)
@@ -176,6 +178,15 @@ init -1500 python in iap:
p = Product(product, identifier, google, amazon)
products[product] = p
def with_background(f, *args, **kwargs):
"""
Displays the background, then invokes `f`.
"""
renpy.scene()
renpy.show(background)
return f(*args, **kwargs)
def restore(interact=True):
"""
:doc: iap
@@ -200,7 +211,7 @@ init -1500 python in iap:
"""
def __call__(self):
restore(False)
renpy.invoke_in_new_context(with_background, restore)
def get_sensitive(self):
return get_store_name()
@@ -212,7 +223,7 @@ init -1500 python in iap:
return p
def purchase(product, interact=False):
def purchase(product, interact=True):
"""
:doc: iap
@@ -247,7 +258,7 @@ init -1500 python in iap:
self.product = product
def __call__(self):
purchase(self.product, interact=False)
renpy.invoke_in_new_context(with_background, purchase, self.product)
renpy.restart_interaction()
def get_sensitive(self):
@@ -321,8 +332,8 @@ init -1500 python in iap:
else:
backend = NoneBackend()
# If there are any missing products, restore purchases.
if missing_products():
# Restore purchases.
if products:
restore(False)
init 1500 python in iap:
+44 -12
View File
@@ -92,12 +92,9 @@ init -1700 python:
config.empty_window = _default_empty_window
style.skip_indicator = Style(style.default, heavy=True, help='The skip indicator.')
style.skip_indicator.xpos = 10
style.skip_indicator.ypos = 10
init -1700 python:
##########################################################################
# Extend
config.extend_interjection = "{fast}"
@@ -122,9 +119,26 @@ init -1700 python:
extend.record_say = False
init -1700 python:
##########################################################################
# Self-voicing
def skip_indicator():
def sv(what, interact=True):
"""
Uses the narrator to speak `what` iff self-voicing is enabled.
"""
if _preferences.self_voicing:
return narrator(what, interact=interact)
##########################################################################
# Skip indicator
style.skip_indicator = Style(style.default, heavy=True, help='The skip indicator.')
style.skip_indicator.xpos = 10
style.skip_indicator.ypos = 10
def _skip_indicator():
### skip_indicator default
# (text) The style and placement of the skip indicator.
@@ -147,10 +161,30 @@ init -1700 python:
ui.add(renpy.easy.displayable(config.skip_indicator))
config.overlay_functions.append(skip_indicator)
config.overlay_functions.append(_skip_indicator)
##########################################################################
# Predictions
# A list of labels we predict at start time.
config.predict_start_labels = [ "start" ]
# Prediction of statements.
def _predict_statements(current):
if main_menu:
rv = list(config.predict_start_labels)
rv.append(current)
return rv
return [ current ]
config.predict_statements_callback = _predict_statements
# Prediction of screens.
def predict():
def _predict_screens():
s = _game_menu_screen
@@ -167,10 +201,8 @@ init -1700 python:
renpy.predict_screen(s)
return
config.predict_callbacks.append(_predict_screens)
config.predict_callbacks.append(predict)
init -1700 python:
##########################################################################
# Side Images
+202
View File
@@ -0,0 +1,202 @@
# Copyright 2004-2014 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:
class Placeholder(renpy.Displayable):
"""
:doc: placeholder
This displayable can be used to display a placeholder character or
background.
"""
def after_setstate(self):
self.child = None
def __init__(self, base=None, full=False, flip=None, **properties):
"""
`base`
The type of image to display. This should be one of:
'bg'
To display a background placeholder. This currently
fills the screen with a light-gray, and displays
the image name at the top of the screen.
'boy'
Displays a male-identified placeholder with the image
name on his chest.
'girl'
Displays a female-identified placeholder with the image
name on her chest.
None
Attempts to automatically determine the type of image
to use. If the image name begins with "bg", "cg", or
"event", uses 'bg'.
Otherwise, contacts a web service to guess gender from
the character's name, and uses that. (The 'girl' placeholder
is used when the service can't guess.)
The webservice will only be contacted when config.developer
is True.
`full`
If true, a full-body sprite is used. Otherwise, a 3/4 sprite
is used.
`flip`
If true, the sprite is flipped horizontally.
"""
super(Placeholder, self).__init__(**properties)
self.base = base
self.flip = flip
self.full = full
# A list of name components.
self.name = [ ]
# The child of this placeholder, if known.
self.child = None
def guess_base(self):
"""
Tries to guess a base image to use based on the name of
the image.
"""
if not self.name:
return 'girl'
tag = self.name[0]
if tag in ( "bg", "cg", "event" ):
return "bg"
if not config.developer:
return "girl"
if persistent._placeholder_gender is None:
persistent._placeholder_gender = { }
if tag in persistent._placeholder_gender:
return persistent._placeholder_gender[tag]
try:
import urllib2
import json
u = urllib2.urlopen("http://api.genderize.io/?name=" + tag.encode("utf-8"), timeout=3)
data = json.loads(u.read())
if data.get("gender", "") == "male":
rv = "boy"
else:
rv = "girl"
except:
rv = "girl"
persistent._placeholder_gender[tag] = rv
return rv
def get_child(self):
"""
Gets the child image to use.
"""
if self.child:
return self.child
# Determine the base.
base = self.base
if base is None:
base = self.guess_base()
# Bg bases.
if base == "bg":
rv = Fixed(
Solid("#aaa"),
Text(" ".join(self.name), style="_default", color="#333333", text_align=0.5, xalign=0.5, ypos=5),
)
self.child = rv
return rv
# Find the character image to use.
if base == "boy":
image = Image("_placeholder/boy.png")
textpos = (187, 500)
size = (380, 1800)
else:
image = Image("_placeholder/girl.png")
textpos = (182, 540)
size = (411, 1800)
# Get the portion of the image to show.
if not self.full:
size = (size[0], 1080)
# Scale to screen height.
zoom = 1.0 * config.screen_height / size[1]
crop = (0, 0) + size
size = (int(size[0] * zoom), int(size[1] * zoom))
textpos = (int(textpos[0] * zoom), int(textpos[1] * zoom))
# Flip as necessary.
if self.flip:
xzoom = -1
textpos = (size[0] - textpos[0], textpos[1])
else:
xzoom = 1
text = "\n".join(self.name)
# Figure out the child.
rv = Fixed(
Transform(image, crop=crop, size=size, xzoom=xzoom),
Text(text, pos=textpos, xanchor=0.5, yanchor=0.5, style="_default", color="#aaa", text_align=0.5),
xysize=size,
)
self.child = rv
return rv
def parameterize(self, name, parameters):
rv = Placeholder(self.base, self.full, self.flip)
rv.name = list(name) + list(parameters)
return rv
def visit(self):
return [ self.get_child() ]
def render(self, width, height, st, at):
child = self.get_child()
return renpy.render(child, width, height, st, at)
+25
View File
@@ -352,3 +352,28 @@ init -1500 python:
rv.alt = name + " [text]"
return rv
def __show_self_voicing():
has_screen = renpy.get_screen("_self_voicing")
if _preferences.self_voicing and not has_screen:
renpy.show_screen("_self_voicing")
elif not _preferences.self_voicing and has_screen:
renpy.hide_screen("_self_voicing")
config.interact_callbacks.append(__show_self_voicing)
init -1500:
# The screen that we use to indicate that self-voicing is enabled.
screen _self_voicing():
zorder 1500
text _("Self-voicing enabled. Press 'v' to disable."):
alt ""
xpos 10
ypos 35
color "#fff"
outlines [ (1, "#0008", 0, 0)]
-1
View File
@@ -155,7 +155,6 @@ label _start:
$ _old_game_menu_screen = _game_menu_screen
$ _game_menu_screen = None
$ renpy.start_predict("main_menu")
if renpy.has_label("splashscreen") and (not _restart) and (not renpy.os.environ.get("RENPY_SKIP_SPLASHSCREEN", None)):
call expression "splashscreen" from _call_splashscreen_1
+1
View File
@@ -203,6 +203,7 @@ init -1800:
# Button properties
focus_mask None
focus_rect None
keyboard_focus True
# Bar properties
fore_bar Null()
+105 -5
View File
@@ -21,7 +21,7 @@
# This code applies an update.
init -1500 python in updater:
from store import renpy, config, Action, DictEquality
from store import renpy, config, Action, DictEquality, persistent
import store.build as build
import tarfile
@@ -47,6 +47,14 @@ init -1500 python in updater:
from renpy.exports import fsencode
# A map from update URL to the last version found at that URL.
if persistent._update_version is None:
persistent._update_version = { }
# A map from update URL to the time we last checked that URL.
if persistent._update_last_checked is None:
persistent._update_last_checked = { }
# A file containing deferred update commands, one per line. Right now,
# there are two commands:
# R <path>
@@ -202,7 +210,7 @@ init -1500 python in updater:
# The update was cancelled.
CANCELLED = "CANCELLED"
def __init__(self, url, base, force=False, public_key=None, simulate=None, add=[], restart=True):
def __init__(self, url, base=None, force=False, public_key=None, simulate=None, add=[], restart=True, check_only=False):
"""
Takes the same arguments as update().
"""
@@ -236,11 +244,15 @@ init -1500 python in updater:
# Force the update?
self.force = force
# Packages to add during the update.
self.add = add
# Do we need to restart Ren'Py at the end?
self.restart = restart
# Packages to add during the update.
self.add = add
# If true, we check for an update, and update persistent._update_version
# as appropriate.
self.check_only = check_only
# The base path of the game that we're updating, and the path to the update
# directory underneath it.
@@ -359,6 +371,14 @@ init -1500 python in updater:
self.can_cancel = False
self.can_proceed = True
self.state = self.UPDATE_NOT_AVAILABLE
persistent._update_version[self.url] = None
renpy.restart_interaction()
return
persistent._update_version[self.url] = pretty_version
if self.check_only:
renpy.restart_interaction()
return
if not self.add:
@@ -370,6 +390,8 @@ init -1500 python in updater:
self.state = self.UPDATE_AVAILABLE
self.version = pretty_version
renpy.restart_interaction()
while True:
if self.cancelled or self.proceeded:
break
@@ -384,6 +406,7 @@ init -1500 python in updater:
# Perform the update.
self.new_state = dict(self.current_state)
renpy.restart_interaction()
self.progress = 0.0
self.state = self.PREPARING
@@ -393,6 +416,7 @@ init -1500 python in updater:
self.progress = 0.0
self.state = self.DOWNLOADING
renpy.restart_interaction()
for i in self.modules:
self.download(i)
@@ -402,12 +426,14 @@ init -1500 python in updater:
self.can_cancel = False
self.progress = 0.0
self.state = self.UNPACKING
renpy.restart_interaction()
for i in self.modules:
self.unpack(i)
self.progress = None
self.state = self.FINISHING
renpy.restart_interaction()
self.move_files()
self.delete_obsolete()
@@ -419,11 +445,15 @@ init -1500 python in updater:
self.can_proceed = True
self.can_cancel = False
persistent._update_version[self.url] = None
if self.restart:
self.state = self.DONE
else:
self.state = self.DONE_NO_RESTART
renpy.restart_interaction()
return
def simulation(self):
@@ -451,6 +481,14 @@ init -1500 python in updater:
self.can_cancel = False
self.can_proceed = True
self.state = self.UPDATE_NOT_AVAILABLE
persistent._update_version[self.url] = None
return
pretty_version = build.version or build.directory_name
persistent._update_version[self.url] = pretty_version
if self.check_only:
renpy.restart_interaction()
return
# Confirm with the user that the update is available.
@@ -458,7 +496,7 @@ init -1500 python in updater:
self.can_cancel = True
self.can_proceed = True
self.state = self.UPDATE_AVAILABLE
self.version = build.version or build.directory_name
self.version = pretty_version
while True:
if self.cancelled or self.proceeded:
@@ -473,22 +511,26 @@ init -1500 python in updater:
self.progress = 0.0
self.state = self.PREPARING
renpy.restart_interaction()
simulate_progress()
self.progress = 0.0
self.state = self.DOWNLOADING
renpy.restart_interaction()
simulate_progress()
self.can_cancel = False
self.progress = 0.0
self.state = self.UNPACKING
renpy.restart_interaction()
simulate_progress()
self.progress = None
self.state = self.FINISHING
renpy.restart_interaction()
time.sleep(1.5)
@@ -497,11 +539,15 @@ init -1500 python in updater:
self.can_proceed = True
self.can_cancel = False
persistent._update_version[self.url] = None
if self.restart:
self.state = self.DONE
else:
self.state = self.DONE_NO_RESTART
renpy.restart_interaction()
return
def proceed(self):
@@ -1106,6 +1152,7 @@ init -1500 python in updater:
installed_packages_cache = None
def get_installed_packages(base=None):
"""
:doc: updater
@@ -1137,6 +1184,7 @@ init -1500 python in updater:
installed_packages_cache = rv
return rv
def can_update(base=None):
"""
:doc: updater
@@ -1144,6 +1192,10 @@ init -1500 python in updater:
Returns true if it's possible that an update can succeed. Returns false
if updating is totally impossible. (For example, if the update directory
was deleted.)
Note that this does not determine if an update is actually available.
To do that, use :func:`updater.UpdateVersion`.
"""
if rsa is None:
@@ -1151,6 +1203,7 @@ init -1500 python in updater:
return not not get_installed_packages(base)
def update(url, base=None, force=False, public_key=None, simulate=None, add=[], restart=True):
"""
:doc: updater
@@ -1196,6 +1249,7 @@ init -1500 python in updater:
ui.timer(.1, repeat=True, action=renpy.restart_interaction)
renpy.call_screen("updater", u=u)
@renpy.pure
class Update(Action, DictEquality):
"""
@@ -1212,6 +1266,52 @@ init -1500 python in updater:
def __call__(self):
renpy.invoke_in_new_context(update, *self.args, **self.kwargs)
# A list of URLs that we've checked for the update version.
checked = set()
def UpdateVersion(url, check_interval=3600*6, simulate=None, **kwargs):
"""
:doc: updater
This function contacts the server at `url`, and determines if there is
a newer version of software available at that url. If there is, this
function returns the new version. Otherwise, it returns None.
Since contacting the server can take some time, this function launches
a thread in the background, and immediately returns the version from
the last time the server was contacted, or None if the server has never
been contacted. The background thread will restart the current interaction
once the server has been contacted, which will cause screens that call
this function to update.
Each url will be contacted at most once per Ren'Py session, and not
more than once every `check_interval` seconds. When the server is not
contacted, cached data will be returned.
Additional keyword arguments (including `simulate`) are passed to the
update mechanism as if they were given to :func:`updater.update`.
"""
if not can_update() and not simulate:
return None
check = True
if url in checked:
check = False
if time.time() < persistent._update_last_checked.get(url, 0) + check_interval:
check = False
if check:
checked.add(url)
persistent._update_last_checked[url] = time.time()
Updater(url, check_only=True, simulate=simulate, **kwargs)
return persistent._update_version.get(url, None)
def update_command():
import time
+12 -52
View File
@@ -245,38 +245,14 @@ label _theme_test:
return
# Not used any more, but may be in save files.
init -1050 python:
config.missing_background = "black"
style _missing_frame is _frame:
xalign 0.5
yalign 0.0
background "#0004"
xpadding 5
ypadding 5
style _missing_text is _text:
color "#fff"
xalign 0.5
# Not used any more, but may be in save files.
screen _missing_images:
zorder -1050
$ missing = renpy.context().__missing
if missing:
frame:
style "_missing_frame"
has vbox
text _("{b}Missing Images{/b}"):
style "_missing_text"
for what in sorted(missing.values()):
text "[what!q]":
style "_missing_text"
pass
init 1050 python:
@@ -287,54 +263,38 @@ init 1050 python:
# blank the screen.
__missing_scene = False
# This returns the __missing dictionary from the current
# context object.
def __missing():
try:
return renpy.context().__missing
except AttributeError:
rv = dict()
renpy.context().__missing = rv
return rv
def __missing_show_callback(name, what, layer):
global __missing_scene
if layer != 'master':
return False
global __missing_scene
if __missing_scene:
renpy.show(name, what=config.missing_background)
__missing_scene = False
what = " ".join(what)
__missing()[name[0]] = what
return True
__missing_scene = False
return Placeholder("bg").parameterize((), what)
else:
return Placeholder().parameterize((), what)
def __missing_hide_callback(name, layer):
if layer != 'master':
return False
global __missing_scene
__missing_scene = False
__missing().pop(name[0], None)
return True
def __missing_scene_callback(layer):
global __missing_scene
if layer != 'master':
return False
global __missing_scene
__missing_scene = True
__missing().clear()
return True
config.missing_scene = __missing_scene_callback
config.missing_show = __missing_show_callback
config.missing_hide = __missing_hide_callback
config.start_callbacks.append(Show("_missing_images"))
init -1050 python:
+1
View File
@@ -96,6 +96,7 @@ style _default:
# Button properties
focus_mask None
focus_rect None
keyboard_focus True
# Bar properties
fore_bar Null()
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

+7 -3
View File
@@ -91,17 +91,17 @@ savedir = None
# The number of screens worth of images that are allowed to
# live in the image cache at once.
image_cache_size = 8
image_cache_size = 16
# The number of statements we will analyze when doing predictive
# loading. Please note that this is a total number of statements in a
# BFS along all paths, rather than the depth along any particular
# path. The current node is counted in this number.
predict_statements = 16
predict_statements = 32
# Causes the contents of the image cache to be printed to stdout when
# it changes.
debug_image_cache = False
debug_image_cache = ("RENPY_DEBUG_IMAGE_CACHE" in os.environ)
# Should we allow skipping at all?
allow_skipping = True
@@ -534,6 +534,10 @@ emphasize_audio_time = 0.5
# Should we transition screens, or always use their new states.
transition_screens = True
# A function that given the current statement identifier, returns a list
# of statement identifiers that should be predicted.
predict_statements_callback = None
del renpy
del os
+34 -18
View File
@@ -23,37 +23,37 @@
import renpy
import math
from renpy.display.render cimport Render, Matrix2D, render
from renpy.display.core import absolute
################################################################################
# Surface copying
################################################################################
from pygame cimport *
def nogil_copy(src, dest):
"""
Does a gil-less blit of src to dest, with minimal locking.
"""
cdef SDL_Surface *src_surf
cdef SDL_Surface *dst_surf
src_surf = PySurface_AsSurface(src)
dest_surf = PySurface_AsSurface(dest)
old_alpha = src_surf.flags & SDL_SRCALPHA
if old_alpha:
SDL_SetAlpha(src_surf, 0, 255)
with nogil:
SDL_BlitSurface(src_surf, NULL, dest_surf, NULL)
if old_alpha:
SDL_SetAlpha(src_surf, SDL_SRCALPHA, 255)
dest.blit(src, (0, 0))
# cdef SDL_Surface *src_surf
# cdef SDL_Surface *dst_surf
#
# src_surf = PySurface_AsSurface(src)
# dest_surf = PySurface_AsSurface(dest)
#
# old_alpha = src_surf.flags & SDL_SRCALPHA
#
# if old_alpha:
# SDL_SetSurfaceAlpha(src_surf, 0, 255)
#
# with nogil:
# SDL_BlitSurface(src_surf, NULL, dest_surf, NULL)
#
# if old_alpha:
# SDL_SetAlpha(src_surf, SDL_SRCALPHA, 255)
################################################################################
# Transform render function
@@ -141,6 +141,22 @@ def transform_render(self, widtho, heighto, st, at):
if crop is not None:
if state.crop_relative:
x, y, w, h = crop
def relative(n, base, limit):
if isinstance(n, (int, absolute)):
return n
else:
return min(int(n * base), limit)
x = relative(x, width, width)
y = relative(y, height, height)
w = relative(w, width, width - x)
h = relative(h, height, height - y)
crop = (x, y, w, h)
negative_xo, negative_yo, width, height = crop
if state.rotate:
+9 -3
View File
@@ -612,7 +612,6 @@ class Button(renpy.display.layout.Window):
mask = rv
elif mask is not None:
try:
mask = renpy.easy.displayable(mask)
mask = renpy.display.render.render(mask, rv.width, rv.height, st, at)
except:
if callable(mask):
@@ -1653,6 +1652,9 @@ class Timer(renpy.display.layout.Null):
class MouseArea(renpy.display.core.Displayable):
# The offset between st and at.
at_st_offset = 0
def __init__(self, hovered=None, unhovered=None, replaces=None, **properties):
super(MouseArea, self).__init__(**properties)
@@ -1669,16 +1671,20 @@ class MouseArea(renpy.display.core.Displayable):
self.width = 0
self.height = 0
def render(self, width, height, st, at):
self.width = width
self.height = height
self.at_st_offset = at - st
return Render(width, height)
def event(self, ev, x, y, st):
if 0 <= x < self.width and 0 <= y < self.height:
if self.style.focus_mask is not None:
crend = renpy.display.render.render(self.style.focus_mask, self.width, self.height, st, self.at_st_offset + st)
is_hovered = crend.is_pixel_opaque(x, y)
elif 0 <= x < self.width and 0 <= y < self.height:
is_hovered = True
else:
is_hovered = False
+4
View File
@@ -184,6 +184,9 @@ class Displayable(renpy.object.Object):
# to.) If None, it is itself.
_main = None
# A list of the children that make up this composite displayable.
_composite_parts = [ ]
# The location the displayable was created at, if known.
_location = None
@@ -415,6 +418,7 @@ class Displayable(renpy.object.Object):
return
self.transform_event = event
if self.transform_event_responder:
renpy.display.render.redraw(self, 0)
+24 -11
View File
@@ -128,15 +128,15 @@ def focus_coordinates():
return None, None, None, None
# This is called before each interaction. It's purpose is to choose
# the widget that is focused, and to mark it as focused and all of
# the other widgets as unfocused.
# The new grab widget. (The one that replaced the old grab widget at the start
# of the interaction.)
new_grab = None
# A map from id(displayable) to the displayable that replaces it.
replaced_by = { }
def before_interact(roots):
"""
Called before each interaction to choose the focused and grabbed
displayables.
"""
global new_grab
global grab
@@ -160,13 +160,17 @@ def before_interact(roots):
f.full_focus_name = n, serial
if (f is grab) and (new_grab is None):
new_grab = f
replaced_by[id(f)] = f
# We assume id(None) is not in replaced_by.
replaced_by.pop(None, None)
# If there's something with the same full name as the current widget,
# it becomes the new current widget.
current = get_focused()
current = replaced_by.get(id(current), current)
if current is not None:
current_name = current.full_focus_name
@@ -199,8 +203,11 @@ def before_interact(roots):
if current:
current.focus(default=True)
grab = new_grab
new_grab = None
# Update the grab.
grab = replaced_by.get(id(grab), None)
# Clear replaced_by.
replaced_by.clear()
# This changes the focus to be the widget contained inside the new
# focus object.
@@ -394,6 +401,9 @@ def focus_nearest(from_x0, from_y0, from_x1, from_y1,
if f is from_focus:
continue
if not f.widget.style.keyboard_focus:
continue
if f.x is None:
placeless = f
continue
@@ -442,6 +452,9 @@ def focus_ordered(delta):
if f.arg is not None:
continue
if not f.widget.style.keyboard_focus:
continue
if f.widget is current:
current_index = index
+23 -7
View File
@@ -880,9 +880,8 @@ class SizeGroup(renpy.object.Object):
maxwidth = 0
for i in self.members:
rend = renpy.display.render.render(i, width, height, st, at)
rend = i.render(width, height, st, at)
maxwidth = max(rend.width, maxwidth)
renpy.display.render.invalidate(i)
self._width = maxwidth
self.computing_width = False
@@ -1099,12 +1098,16 @@ class DynamicDisplayable(renpy.display.core.Displayable):
return renpy.display.render.render(self.child, w, h, st, at)
def predict_one(self):
if not self.predict_function:
return
try:
if self.predict_function:
child = self.predict_function(*self.args, **self.kwargs)
else:
child, _ = self.function(0, 0, *self.args, **self.kwargs)
for i in self.predict_function(*self.args, **self.kwargs):
if i is not None:
renpy.display.predict.displayable(i)
if child is not None:
renpy.display.predict.displayable(child)
except:
pass
def get_placement(self):
if not self.child:
@@ -1608,13 +1611,26 @@ class Side(Container):
if isinstance(positions, basestring):
positions = positions.split()
seen = set()
for i in positions:
if not i in Side.possible_positions:
raise Exception("Side used with impossible position '%s'." % (i,))
if i in seen:
raise Exception("Side used with duplicate position '%s'." % (i,))
seen.add(i)
self.positions = tuple(positions)
self.sized = False
def add(self, d):
if len(self.children) >= len(self.positions):
raise Exception("Side has been given too many arguments.")
super(Side, self).add(d)
def _clear(self):
super(Side, self)._clear()
self.sized = False
+24
View File
@@ -97,6 +97,7 @@ class TransformState(renpy.object.Object):
additive = 0.0
debug = None
events = True
crop_relative = False
def __init__(self): # W0231
self.alpha = 1
@@ -124,6 +125,7 @@ class TransformState(renpy.object.Object):
self.subpixel = False
self.crop = None
self.crop_relative = False
self.corner1 = None
self.corner2 = None
self.size = None
@@ -168,6 +170,7 @@ class TransformState(renpy.object.Object):
self.yanchoraround = ts.yanchoraround
self.crop = ts.crop
self.crop_relative = ts.crop_relative
self.corner1 = ts.corner1
self.corner2 = ts.corner2
self.size = ts.size
@@ -231,6 +234,7 @@ class TransformState(renpy.object.Object):
diff2("subpixel", newts.subpixel, self.subpixel)
diff2("crop", newts.crop, self.crop)
diff2("crop_relative", newts.crop_relative, self.crop_relative)
diff2("corner1", newts.corner1, self.corner1)
diff2("corner2", newts.corner2, self.corner2)
diff2("size", newts.size, self.size)
@@ -435,6 +439,7 @@ class Transform(Container):
align = Proxy("align")
crop = Proxy("crop")
crop_relative = Proxy("crop_relative")
corner1 = Proxy("corner1")
corner2 = Proxy("corner2")
size = Proxy("size")
@@ -639,6 +644,9 @@ class Transform(Container):
Takes the transformation state from object t into this object.
"""
if not isinstance(t, Transform):
return
self.state.take_state(t.state)
# The arguments will be applied when the default function is
@@ -651,6 +659,9 @@ class Transform(Container):
overridden by renpy.atl.TransformBase.
"""
if not isinstance(t, Transform):
return
self.hide_request = t.hide_request
self.replaced_request = t.replaced_request
@@ -692,6 +703,16 @@ class Transform(Container):
if not self.child:
return None
# Prevent time from ticking backwards, as can happen if we replace a
# transform but keep its state.
if st + self.st_offset <= self.st:
self.st_offset = self.st - st
if at + self.at_offset <= self.at:
self.at_offset = self.at - at
self.st = st = st + self.st_offset
self.at = at = at + self.at_offset
if not (self.hide_request or self.replaced_request):
d = self.copy()
else:
@@ -741,6 +762,9 @@ class Transform(Container):
This updates the state to that at self.st, self.at.
"""
self.hide_response = True
self.replaced_response = True
# If we have to, call the function that updates this transform.
if self.arguments is not None:
self.default_function(self, self.st, self.at)
+21 -1
View File
@@ -24,8 +24,10 @@
import sys
import pygame
import threading
import renpy.display
# Sample surfaces, with and without alpha.
sample_alpha = None
sample_noalpha = None
@@ -125,8 +127,26 @@ copy_surface_unscaled = copy_surface
# Wrapper around image loading.
# Formats we can load reentrantly.
safe_formats = { "png", "jpg", "jpeg" }
# Lock used for loading unsafe formats.
image_load_lock = threading.RLock()
def load_image(f, filename):
surf = pygame.image.load(f, renpy.exports.fsencode(filename))
global count
_basename, _dot, ext = filename.rpartition('.')
if ext.lower() in safe_formats:
surf = pygame.image.load(f, renpy.exports.fsencode(filename))
else:
# Non-whitelisted formats may not be able to load in a reentrant
# fashion.
with image_load_lock:
surf = pygame.image.load(f, renpy.exports.fsencode(filename))
rv = copy_surface_unscaled(surf)
return rv
+14 -18
View File
@@ -91,6 +91,9 @@ def check_at_shutdown():
raise Exception("%d Renders are alive at shutdown. This is probably a memory leak bug in Ren'Py." % count)
# The number of things being rendered at the moment.
cdef int rendering
rendering = 0
cpdef render(d, object widtho, object heighto, double st, double at):
"""
@@ -113,6 +116,8 @@ cpdef render(d, object widtho, object heighto, double st, double at):
once they have been retrieved.
"""
global rendering
cdef float width, height
cdef float orig_width, orig_height
cdef tuple orig_wh, wh
@@ -164,7 +169,11 @@ cpdef render(d, object widtho, object heighto, double st, double at):
else:
wh = orig_wh
rv = d.render(widtho, heighto, st, at)
try:
rendering += 1
rv = d.render(widtho, heighto, st, at)
finally:
rendering -= 1
rv.render_of.append(d)
@@ -179,27 +188,14 @@ cpdef render(d, object widtho, object heighto, double st, double at):
return rv
# This is true if something has been invalidated, and a redraw needs
# to occur. It's automatically cleared to False at the end of each
# redraw.
invalidated = False
def invalidate(d):
"""
Removes d from the render cache. If we're not in a redraw, triggers
a redraw to start.
"""
global invalidated
id_d = id(d)
if id_d in render_cache:
for v in render_cache[id_d].values():
v.kill_cache()
invalidated = True
if not rendering:
redraw(d, 0)
def process_redraws():
@@ -213,7 +209,7 @@ def process_redraws():
redraw_queue.sort()
now = renpy.display.core.get_time()
rv = invalidated
rv = False
new_redraw_queue = [ ]
seen = set()
@@ -827,7 +823,7 @@ cdef class Render:
def kill(self):
"""
Retained for compatibility.
Retained for compatibility, but does not need to be called.
"""
def add_focus(self, d, arg=None, x=0, y=0, w=None, h=None, mx=None, my=None, mask=None):
+30 -18
View File
@@ -268,6 +268,7 @@ class ScreenDisplayable(renpy.display.layout.Container):
nosave = [ 'screen', 'child', 'children', 'transforms', 'widgets', 'old_widgets', 'hidden_widgets', 'old_transforms', 'cache', 'profile', 'phase', 'use_cache' ]
restarting = False
hiding = False
def after_setstate(self):
self.screen = get_screen_variant(self.screen_name[0])
@@ -360,8 +361,9 @@ class ScreenDisplayable(renpy.display.layout.Container):
# A dict-set of widgets (by id) that have been hidden from us.
self.hidden_widgets = { }
# Are we restarting?
# Are we restarting or hiding?
self.restarting = False
self.hiding = False
# Modal and zorder.
self.modal = renpy.python.py_eval(self.screen.modal, locals=self.scope)
@@ -525,10 +527,6 @@ class ScreenDisplayable(renpy.display.layout.Container):
if self.profile.debug:
debug = True
# Update _current_screen
global _current_screen
old_screen = _current_screen
_current_screen = self
# Cycle widgets and transforms.
self.old_widgets = self.widgets
@@ -536,24 +534,31 @@ class ScreenDisplayable(renpy.display.layout.Container):
self.widgets = { }
self.transforms = { }
# Render the child.
# Update _current_screen and renpy.ui.screen.
global _current_screen
old_screen = _current_screen
_current_screen = self
old_ui_screen = renpy.ui.screen
renpy.ui.screen = self
renpy.ui.detached()
self.child = renpy.ui.fixed(focus="_screen_" + "_".join(self.screen_name))
self.children = [ self.child ]
# Evaluate the screen.
try:
renpy.ui.detached()
self.child = renpy.ui.fixed(focus="_screen_" + "_".join(self.screen_name))
self.children = [ self.child ]
self.scope["_scope"] = self.scope
self.scope["_name"] = 0
self.scope["_debug"] = debug
self.scope["_scope"] = self.scope
self.scope["_name"] = 0
self.scope["_debug"] = debug
self.screen.function(**self.scope)
self.screen.function(**self.scope)
renpy.ui.close()
renpy.ui.close()
renpy.ui.screen = old_ui_screen
_current_screen = old_screen
finally:
renpy.ui.screen = old_ui_screen
_current_screen = old_screen
# Finish up.
self.old_widgets = None
@@ -808,8 +813,15 @@ def show_screen(_screen_name, *_args, **kwargs):
scope.update(kwargs)
d = ScreenDisplayable(screen, _tag, _layer, _widget_properties, scope)
d.cache = cache_get(screen, _args, kwargs)
d.phase = SHOW
old_d = get_screen(name, _layer)
if old_d and old_d.cache:
d.cache = old_d.cache
d.phase = UPDATE
else:
d.cache = cache_get(screen, _args, kwargs)
d.phase = SHOW
renpy.exports.show(name, tag=_tag, what=d, layer=_layer, zorder=d.zorder, transient=_transient, munge_name=False)
+1 -1
View File
@@ -857,7 +857,7 @@ class SWDraw(object):
if self.mouse_location:
updates.append(self.hide_mouse())
if tex and pos and renpy.game.interface.focused: # @UndefinedVariable
if tex and pos and renpy.game.interface.mouse_focused: # @UndefinedVariable
updates.append(self.show_mouse(pos, info))
return updates
+4 -1
View File
@@ -87,7 +87,10 @@ def tts(s):
global queue
renpy.config.tts_function(s)
try:
renpy.config.tts_function(s)
except:
pass
queue = [ ]
+78 -11
View File
@@ -392,6 +392,7 @@ class Context(renpy.object.Object):
self.return_stack.pop()
self.call_location_stack.pop()
self.pop_dynamic()
continue
@@ -422,6 +423,39 @@ class Context(renpy.object.Object):
return rv
def predict_call(self, label, return_site):
"""
This is called by the prediction code to indicate that a call to
`label` will occur.
`return_site`
The name of the return site to push on the predicted return
stack.
Returns the node corresponding to `label`
"""
self.predict_return_stack = list(self.predict_return_stack)
self.predict_return_stack.append(return_site)
return renpy.game.script.lookup(label)
def predict_return(self):
"""
This predicts that a return will occur.
It returns the node we predict will be returned to.
"""
if not self.predict_return_stack:
return None
self.predict_return_stack = list(self.predict_return_stack)
label = self.predict_return_stack.pop()
return renpy.game.script.lookup(label)
def predict(self):
"""
Performs image prediction, calling the given callback with each
@@ -432,31 +466,48 @@ class Context(renpy.object.Object):
if not self.current:
return
if renpy.config.predict_statements_callback is None:
return
old_images = self.images
nodes = [ (renpy.game.script.lookup(self.current), self.images) ]
node_set = set()
# A worklist of (node, images, return_stack) tuples.
nodes = [ ]
# The set of nodes we've seen. (We only consider each node once.)
seen = set()
# Find the roots.
for label in renpy.config.predict_statements_callback(self.current):
node = renpy.game.script.lookup(label)
if node in seen:
continue
nodes.append((node, self.images, self.return_stack))
seen.add(node)
# Predict statements.
for i in range(0, renpy.config.predict_statements):
if i >= len(nodes):
break
node, images = nodes[i]
node, images, return_stack = nodes[i]
self.images = renpy.display.image.ShownImageInfo(images)
# Ignore exceptions in prediction, so long as
# prediction is not needed.
self.predict_return_stack = return_stack
try:
for n in node.predict():
if n is None:
continue
if n not in node_set:
nodes.append((n, self.images))
node_set.add(n)
if n not in seen:
nodes.append((n, self.images, self.predict_return_stack))
seen.add(n)
except:
if renpy.config.debug_image_cache:
@@ -466,9 +517,8 @@ class Context(renpy.object.Object):
traceback.print_exc()
print "While predicting images."
# We accept that sometimes prediction won't work.
self.images = old_images
self.predict_return_stack = None
yield True
@@ -509,6 +559,23 @@ class Context(renpy.object.Object):
renpy.exports.rollback(force, checkpoints)
def get_return_stack(self):
return list(self.return_stack)
def set_return_stack(self, return_stack):
self.return_stack = list(return_stack)
while len(self.call_location_stack) > len(self.return_stack):
self.call_location_stack.pop()
d = self.dynamic_stack.pop()
d.update(self.dynamic_stack[-1])
self.dynamic_stack[-1] = d
while len(self.call_location_stack) < len(self.return_stack):
self.call_location_stack.append("unknown location")
self.dynamic_stack.append({})
def run_context(top):
"""
+90 -4
View File
@@ -84,6 +84,8 @@ import renpy.audio.music as music
from renpy.statements import register as register_statement
from renpy.text.extras import check_text_tags
from renpy.memory import profile_memory, diff_memory, profile_rollback
renpy_pure("ParameterizedText")
renpy_pure("Keymap")
renpy_pure("has_screen")
@@ -96,6 +98,7 @@ renpy_pure("check_text_tags")
import time
import sys
import threading
def public_api():
"""
@@ -135,6 +138,7 @@ def public_api():
map_event, queue_event, clear_keymap_cache
const, pure, not_const
image_exists
profile_memory, diff_memory
del public_api
@@ -481,7 +485,11 @@ def show(name, at_list=[ ], layer='master', what=None, zorder=0, tag=None, behin
base = img = renpy.display.image.ImageReference(what, style='image_placement')
if not base.find_target() and renpy.config.missing_show:
if renpy.config.missing_show(name, what, layer):
result = renpy.config.missing_show(name, what, layer)
if isinstance(result, renpy.display.core.Displayable):
base = img = result
elif result:
return
for i in at_list:
@@ -496,7 +504,6 @@ def show(name, at_list=[ ], layer='master', what=None, zorder=0, tag=None, behin
if tag and munge_name:
name = (tag,) + name[1:]
if renpy.config.missing_hide:
renpy.config.missing_hide(name, layer)
@@ -1291,8 +1298,11 @@ def has_label(name):
"""
:doc: label
Returns true if name is a valid label in the program, or false
otherwise.
Returns true if `name` is a valid label the program, or false otherwise.
`name`
Should be a string to check for the existence of a label. It can
also be an opaque tuple giving the name of a non-label statement.
"""
return renpy.game.script.has_label(name)
@@ -2730,3 +2740,79 @@ def substitute(s, scope=None, translate=True):
"""
return renpy.substitutions.substitute(s, scope=scope, translate=translate)[0]
def munge(name, filename=None):
"""
:doc: other
Munges `name`, which must begin with __.
`filename`
The filename the name is munged into. If None, the name is munged
into the filename containing the call to this function.
"""
if filename is None:
filename = sys._getframe(1).f_code.co_filename
if not name.startswith("__"):
return name
if name.endswith("__"):
return name
return renpy.parser.munge_filename(filename) + name[2:]
def get_return_stack():
"""
:doc: label
Returns a list giving the current return stack. The return stack is a
list of statement names.
The statement names will be strings (for labels), or opaque tuples (for
non-label statements).
"""
return renpy.game.context().get_return_stack()
def set_return_stack(stack):
"""
:doc: label
Sets the current return stack. The return stack is a list of statement
names.
Statement names may be strings (for labels) or opaque tuples (for
non-label statements).
"""
renpy.game.context().set_return_stack(stack)
def invoke_in_thread(fn, *args, **kwargs):
"""
:doc: other
Invokes the function `fn` in a background thread, passing it the
provided arguments and keyword arguments. Restarts the interaction
once the thread returns.
This function creates a daemon thread, which will be automatically
stopped when Ren'Py is shutting down.
"""
def run():
try:
fn(*args, **kwargs)
except:
import traceback
traceback.print_exc()
restart_interaction()
t = threading.Thread(target=run)
t.daemon = True
t.start()
+5 -1
View File
@@ -24,9 +24,12 @@
DEF ANGLE = False
from libc.stdlib cimport malloc, free
from pygame cimport *
from sdl2 cimport *
from gl cimport *
from pygame_sdl2 cimport *
import_pygame_sdl2__surface()
import renpy
import pygame
import os
@@ -40,6 +43,7 @@ cimport gltexture
import gltexture
import glblacklist
cdef extern from "glcompat.h":
GLenum glewInit()
GLubyte *glewGetErrorString(GLenum)
+10 -2
View File
@@ -25,7 +25,10 @@ DEF ANGLE = False
from gl cimport *
from gldraw cimport *
from pygame cimport *
from sdl2 cimport *
from pygame_sdl2 cimport *
import_pygame_sdl2__surface()
from cpython.string cimport PyString_FromStringAndSize
from libc.stdlib cimport calloc, free
@@ -485,6 +488,9 @@ class Texture(TextureCore):
the __del__ method is called.
"""
def __sizeof__(self):
return TextureCore.__sizeof__(self) + self.width * self.height * 4
def __getstate__(self):
if renpy.config.developer:
raise Exception("Can't pickle a texture.")
@@ -1109,7 +1115,9 @@ def premultiply(
cdef unsigned char *out = rv
# The pixels in the source image.
cdef unsigned char *pixels, *pixels_end
cdef unsigned char *pixels
cdef unsigned char *pixels_end
cdef SDL_Surface *surf
# Pointer to the current pixel.
+288 -41
View File
@@ -23,41 +23,308 @@
# called by default, but can be used when problems occur.
import time
import threading
import weakref
import types
import sys
import collections
import pygame
import gc
old_memory = { }
import renpy
def memory_profile():
memory_log = renpy.log.open("memory")
def write(s):
sys.stdout.write(s + "\n")
memory_log.write("%s", s)
def walk_memory(roots, seen=None):
"""
Calling this function displays the change in the number of instances of
each type of object.
Walks over memory, trying to account it to the objects in `roots`. Each
object in memory is attributed to at most one of the roots. We use a
breadth-first search to try to come up with the most accurate
attribution possible.
`roots`
A list of (name, object) tuples.
Returns a dictionary mapping names to the number of bytes
reachable from that name, and a dictionary mapping object ids to
names.
"""
print "- Memory Profile ---------------------------------------------------"
# A map from id(o) to the name o is accounted under.
if seen is None:
seen = { }
import gc
gc.collect()
# A list of (name, object) pairs.
worklist = [ ]
objs = gc.get_objects()
# A map from root_name to total_size.
size = collections.defaultdict(int)
c = { } # count
def add(name, o):
"""
Adds o to the worklist if it's not in seen.
"""
for i in objs:
t = type(i)
c[t] = c.get(t, 0) + 1
for name, o in roots:
id_o = id(o)
results = [ (count, ty) for ty, count in c.iteritems() ]
results.sort()
if id_o in seen:
continue
for count, ty in results:
diff = count - old_memory.get(ty, 0)
old_memory[ty] = count
if diff:
print diff, ty
seen[id_o] = name
worklist.append((name, o))
del objs
# For speed, cache name lookups.
Surface = pygame.Surface
getsizeof = sys.getsizeof
get_referents = gc.get_referents
worklist_append = worklist.append
ignore_types = (types.ModuleType, types.ClassType, types.FunctionType)
while worklist:
name, o = worklist.pop(0)
if isinstance(o, ignore_types):
continue
size[name] += getsizeof(o)
if isinstance(o, Surface):
w, h = o.get_size()
size[name] += w * h * o.get_bytesize()
for v in get_referents(o):
id_v = id(v)
if id_v in seen:
continue
seen[id_v] = name
worklist_append((name, v))
return size, seen
def profile_memory_common(packages=[ "renpy", "store" ]):
"""
Profiles object, surface, and texture memory used in the renpy and store
packages.
Returns a map from name to the number of bytes accounted for by that
name, and a dictionary mapping object ids to
names.
"""
roots = [ ]
for mod_name, mod in sorted(sys.modules.items()):
if mod is None:
continue
for p in packages:
if mod_name.startswith(p):
break
else:
continue
if not (mod_name.startswith("renpy") or mod_name.startswith("store")):
continue
if mod_name.startswith("renpy.store"):
continue
for name, o in mod.__dict__.items():
roots.append((mod_name + "." + name, o))
return walk_memory(roots)
def profile_memory(fraction=1.0, minimum=0):
"""
:doc: memory
Profiles object, surface, and texture memory use by Ren'Py and the
game. Writes an accounting of memory use by to the memory.txt file and
stdout.
The accounting is by names in the store and in the Ren'Py implementation
that the memory is reachable from. If an object is reachable from more
than one name, it's assigned to the name it's most directly reachable
from.
`fraction`
The fraction of the total memory usage to show. 1.0 will show all
memory usage, .9 will show the top 90%.
`minimum`
If a name is accounted less than `minimum` bytes of memory, it will
not be printed.
As it has to scan all memory used by Ren'Py, this function may take a
long time to complete.
"""
usage = [ (v, k) for (k, v) in profile_memory_common()[0].items() ]
usage.sort()
# The total number of bytes allocated.
total = sum(i[0] for i in usage)
# The number of bytes we have yet to process.
remaining = total
write("=" * 78)
write("")
write("Memory profile at " + time.ctime() + ":")
write("")
for size, name in usage:
if (remaining - size) < total * fraction:
if size > minimum:
write("{:13,d} {}".format(size, name))
remaining -= size
write("-" * 13)
write("{:13,d} Total object, surface, and texture memory usage (in bytes).".format(total))
write("")
old_usage = { }
old_total = 0
def diff_memory(update=True):
"""
:doc: memory
Profiles objects, surface, and texture memory use by Ren'Py and the game.
Writes (to memory.txt and stdout) the difference in memory usage from the
last time this function was called with `update` true.
The accounting is by names in the store and in the Ren'Py implementation
that the memory is reachable from. If an object is reachable from more
than one name, it's assigned to the name it's most directly reachable
from.
As it has to scan all memory used by Ren'Py, this function may take a
long time to complete.
"""
global old_usage
global old_total
usage = profile_memory_common()[0]
total = sum(usage.values())
diff = [ ]
for k, v in usage.iteritems():
diff.append((
v - old_usage.get(k, 0),
k))
diff.sort()
write("=" * 78)
write("")
write("Memory diff at " + time.ctime() + ":")
write("")
for change, name in diff:
if name == "renpy.memory.old_usage":
continue
if change:
write("{:+14,d} {:13,d} {}".format(change, usage[name], name))
write("-" * 14 + " " + "-" * 13)
write("{:+14,d} {:13,d} {}".format(total - old_total, total, "Total memory usage (in bytes)."))
write("")
if update:
old_usage = usage
old_total = total
def profile_rollback():
"""
:doc: memory
Profiles memory used by the rollback system. Writes (to memory.txt and
stdout) the memory used by the rollback system. This tries to account
for rollback memory used by various store variables, as well as by
internal aspects of the rollback system.
"""
# Profile live memory.
seen = profile_memory_common([ "store", "renpy.display" ])[1]
# Like seen, but for objects found in rollback.
new_seen = { }
log = list(renpy.game.log.log)
log.reverse()
roots = [ ]
# Walk the log, finding new roots and rollback information.
for rb in log:
for store_name, store in rb.stores.iteritems():
for var_name, o in store.iteritems():
name = store_name + "." + var_name
id_o = id(o)
if (id_o not in seen) and (id_o not in new_seen):
new_seen[id_o] = name
roots.append((name, o))
for o, roll in rb.objects:
id_o = id(o)
name = "<unknown>"
name = new_seen.get(id_o, name)
name = seen.get(id_o, name)
roots.append((name, roll))
roots.append(("<scene lists>", rb.context.scene_lists))
roots.append(("<context>", rb.context))
sizes = walk_memory(roots, seen)[0]
write("=" * 78)
write("")
write("Rollback profile at " + time.ctime() + ":")
write("")
usage = [ (v, k) for (k, v) in sizes.iteritems() ]
usage.sort()
write("Total Bytes".rjust(13) + " " + "Per Rollback".rjust(13))
write("-" * 13 + " " + "-" * 13 + " " + "-" * 50)
for size, name in usage:
if name.startswith("renpy"):
continue
if size:
write("{:13,d} {:13,d} {}".format(size, size // len(log), name))
write("")
write("{} Rollback objects exist.".format(len(log)))
write("")
################################################################################
# Legacy memory debug functions
################################################################################
def find_parents(cls):
"""
@@ -65,8 +332,6 @@ def find_parents(cls):
"""
# GC to save memory.
import gc
import types
gc.collect()
objs = gc.get_objects()
@@ -79,7 +344,6 @@ def find_parents(cls):
queue = [ ]
objects = [ ]
for _i in range(30):
objects.append(o)
@@ -144,20 +408,3 @@ def find_parents(cls):
print
print_path(o)
def memory_thread():
import sys
TextureGrid = sys.modules['renpy.gl.gltexture'].TextureGrid
while True:
print "==================================================="
print "==================================================="
find_parents(TextureGrid)
sys.stderr.write("Wrote textures.\n")
time.sleep(5)
def start_memory_thread():
t = threading.Thread(target=memory_thread)
t.daemon = True
t.start()
+19 -1
View File
@@ -1705,7 +1705,12 @@ def image_statement(l, loc):
atl = renpy.atl.parse_atl(l.subblock_lexer())
else:
l.require('=')
expr = l.rest()
if not expr:
l.error('expected expression')
atl = None
l.expect_noblock('image statement')
@@ -1728,13 +1733,22 @@ def define_statement(l, loc):
else:
priority = 0
store = 'store'
name = l.require(l.name)
while l.match(r'\.'):
store = store + "." + name
name = l.require(l.name)
l.require('=')
expr = l.rest()
if not expr:
l.error("expected expression")
l.expect_noblock('define statement')
rv = ast.Define(loc, name, expr)
rv = ast.Define(loc, store, name, expr)
if not l.init:
rv = ast.Init(loc, [ rv ], priority)
@@ -1777,6 +1791,10 @@ def transform_statement(l, loc):
@statement("$")
def one_line_python(l, loc):
python_code = l.rest()
if not python_code:
l.error('expected python code')
l.expect_noblock('one-line python statement')
l.advance()
+4
View File
@@ -519,6 +519,7 @@ position_property_names = [
"ysize",
"xysize",
"alt",
"debug",
]
position_properties = [ Style(i) for i in position_property_names ]
@@ -591,6 +592,7 @@ button_properties = [ Style(i) for i in [
"mouse",
"focus_mask",
"child",
"keyboard_focus",
] ]
bar_properties = [ Style(i) for i in [
@@ -610,6 +612,7 @@ bar_properties = [ Style(i) for i in [
"thumb_offset",
"mouse",
"unscrollable",
"keyboard_focus",
] ]
box_properties = [ Style(i) for i in [
@@ -888,6 +891,7 @@ add(position_properties)
FunctionStatementParser("mousearea", "ui.mousearea", 0)
Keyword("hovered")
Keyword("unhovered")
Style("focus_mask")
add(ui_properties)
add(position_properties)
+181 -146
View File
@@ -242,7 +242,127 @@ class Script(object):
if new.name is None:
new.name = old.name
def load_file_core(self, dir, fn): #@ReservedAssignment
def load_string(self, filename, filedata):
"""
Loads Ren'Py script from a string.
`filename`
The filename that's assigned to the data.
`filedata`
A unicode string to be loaded.
Return the list of statements making up the root block, and a
list of init statements that need to be run.
"""
stmts = renpy.parser.parse(filename, filedata)
if stmts is None:
return None, None
self.assign_names(stmts, filename)
initcode = [ ]
stmts = self.finish_load(stmts, initcode, False)
return stmts, initcode
def finish_load(self, stmts, initcode, check_names=True, filename=None):
"""
Given `stmts`, a list of AST nodes comprising the root block,
finishes loading it (this includes chaining statements and
adding them to the name map.)
`initcode`
A list we append init statements to.
`check_names`
If true, produce duplicate name errors.
`filename`
If given, a filename that overrides the filename found inside the
file.
Returns a list of statements that corresponds to the top-level block
in initcode after transformation.
"""
if not stmts:
return
# Generate translate nodes.
renpy.translation.restructure(stmts)
# All of the statements found in file, regardless of nesting
# depth.
all_stmts = collapse_stmts(stmts)
# Take the translations.
self.translator.take_translates(all_stmts)
# Chain together the statements in the file.
renpy.ast.chain_block(stmts, None)
# Fix the filename for a renamed .rpyc file.
if filename is not None:
filename = renpy.parser.elide_filename(filename)
if all_stmts[0].filename.lower() != filename.lower():
filename += "c"
for i in all_stmts:
i.filename = filename
# Check for duplicate names.
if check_names:
bad_name = None
bad_node = None
old_node = None
for node in all_stmts:
name = node.name
if name in self.namemap:
if not isinstance(bad_name, basestring):
bad_name = name
bad_node = node
old_node = self.namemap[name]
if bad_name is not None:
raise ScriptError("Name %s is defined twice, at %s:%d and %s:%d." %
(repr(bad_name),
old_node.filename, old_node.linenumber,
bad_node.filename, bad_node.linenumber))
for node in all_stmts:
name = node.name
# Add the name to the namemap.
self.namemap[name] = node
# Add any init nodes to self.initcode.
init = node.get_init()
if init:
initcode.append(init)
# Compile bytecode from the file.
self.update_bytecode()
# Exec early python.
for node in all_stmts:
node.early_execute()
if self.all_stmts is not None:
self.all_stmts.extend(all_stmts)
self.need_analysis.extend(all_stmts)
return stmts
def load_file(self, dir, fn): #@ReservedAssignment
if fn.endswith(".rpy") or fn.endswith(".rpym"):
@@ -264,7 +384,7 @@ class Script(object):
# we want to try to upgrade our .rpy file with it.
try:
self.record_pycode = False
old_data, old_stmts = self.load_file_core(dir, fn + "c")
old_data, old_stmts = self.load_file(dir, fn + "c")
self.merge_names(old_stmts, stmts)
del old_data
del old_stmts
@@ -308,12 +428,66 @@ class Script(object):
return data, stmts
def load_file(self, dir, fn, initcode): #@ReservedAssignment
def load_appropriate_file(self, compiled, source, dir, fn, initcode): #@ReservedAssignment
# This can only be a .rpyc file, since we're loading it
# from an archive.
if dir is None:
rpyfn = fn + source
lastfn = fn + compiled
data, stmts = self.load_file(dir, fn + compiled)
if data is None:
raise Exception("Could not load from archive %s." % (lastfn,))
else:
# Otherwise, we're loading from disk. So we need to decide if
# we want to load the rpy or the rpyc file.
rpyfn = dir + "/" + fn + source
rpycfn = dir + "/" + fn + compiled
renpy.loader.add_auto(rpyfn)
if os.path.exists(rpyfn) and os.path.exists(rpycfn):
# Use the source file here since it'll be loaded if it exists.
lastfn = rpyfn
rpydigest = md5.md5(file(rpyfn, "rU").read()).digest()
data, stmts = None, None
try:
f = file(rpycfn, "rb")
f.seek(-md5.digest_size, 2)
rpycdigest = f.read(md5.digest_size)
f.close()
if rpydigest == rpycdigest and \
not (renpy.game.args.command == "compile" or renpy.game.args.compile): #@UndefinedVariable
data, stmts = self.load_file(dir, fn + compiled)
if data is None:
print "Could not load " + rpycfn
except:
pass
if data is None:
data, stmts = self.load_file(dir, fn + source)
elif os.path.exists(rpycfn):
lastfn = rpycfn
data, stmts = self.load_file(dir, fn + compiled)
elif os.path.exists(rpyfn):
lastfn = rpyfn
data, stmts = self.load_file(dir, fn + source)
# Actually do the loading.
data, stmts = self.load_file_core(dir, fn)
if data is None:
return False
raise Exception("Could not load file %s." % lastfn)
# Check the key.
if self.key is None:
@@ -321,147 +495,8 @@ class Script(object):
elif self.key != data['key']:
raise Exception( fn + " does not share a key with at least one .rpyc file. To fix, delete all .rpyc files, or rerun Ren'Py with the --lock option.")
self.finish_load(stmts, initcode)
return True
self.finish_load(stmts, initcode, filename=rpyfn)
def load_string(self, filename, filedata):
"""
Loads Ren'Py script from a string.
`filename`
The filename that's assigned to the data.
`filedata`
A unicode string to be loaded.
Return the list of statements making up the root block, and a
list of init statements that need to be run.
"""
stmts = renpy.parser.parse(filename, filedata)
if stmts is None:
return None, None
self.assign_names(stmts, filename)
initcode = [ ]
stmts = self.finish_load(stmts, initcode, False)
return stmts, initcode
def finish_load(self, stmts, initcode, check_names=True):
"""
Given `stmts`, a list of AST nodes comprising the root block,
finishes loading it (this includes chaining statements and
adding them to the name map.)
`initcode`
A list we append init statements to.
`check_names`
If true, produce duplicate name errors.
Returns a list of statements that corresponds to the top-level block
in initcode after transformation.
"""
# Generate translate nodes.
renpy.translation.restructure(stmts)
# All of the statements found in file, regardless of nesting
# depth.
all_stmts = collapse_stmts(stmts)
# Take the translations.
self.translator.take_translates(all_stmts)
# Chain together the statements in the file.
renpy.ast.chain_block(stmts, None)
# Check each node individually.
for node in all_stmts:
# Check to see if the name is defined twice. If it is,
# report the error.
name = node.name
if name in self.namemap and check_names:
old = self.namemap[name]
raise ScriptError("Name %s is defined twice: at %s:%d and %s:%d." %
(repr(name),
old.filename, old.linenumber,
node.filename, node.linenumber))
# Otherwise, add the name to the namemap.
self.namemap[name] = node
# Add any init nodes to self.initcode.
init = node.get_init()
if init:
initcode.append(init)
# Compile bytecode from the file.
self.update_bytecode()
# Exec early python.
for node in all_stmts:
node.early_execute()
if self.all_stmts is not None:
self.all_stmts.extend(all_stmts)
self.need_analysis.extend(all_stmts)
return stmts
def load_appropriate_file(self, compiled, source, dir, fn, initcode): #@ReservedAssignment
# This can only be a .rpyc file, since we're loading it
# from an archive.
if dir is None:
if not self.load_file(dir, fn + compiled, initcode):
raise Exception("Could not load from archive %s.%s" % (fn, compiled))
return
# Otherwise, we're loading from disk. So we need to decide if
# we want to load the rpy or the rpyc file.
rpyfn = dir + "/" + fn + source
rpycfn = dir + "/" + fn + compiled
renpy.loader.add_auto(rpyfn)
if os.path.exists(rpyfn) and os.path.exists(rpycfn):
rpydigest = md5.md5(file(rpyfn, "rU").read()).digest()
try:
f = file(rpycfn, "rb")
f.seek(-md5.digest_size, 2)
rpycdigest = f.read(md5.digest_size)
f.close()
if rpydigest == rpycdigest and \
not (renpy.game.args.command == "compile" or renpy.game.args.compile): #@UndefinedVariable
if self.load_file(dir, fn + compiled, initcode):
return
print "Could not load " + rpycfn
except:
pass
if not self.load_file(dir, fn + source, initcode):
raise Exception("Could not load file %s." % rpyfn)
elif os.path.exists(rpycfn):
if not self.load_file(dir, fn + compiled, initcode):
raise Exception("Could not load file %s." % rpycfn)
elif os.path.exists(rpyfn):
if not self.load_file(dir, fn + source, initcode):
raise Exception("Could not load file %s." % rpyfn)
def init_bytecode(self):
+145 -36
View File
@@ -25,6 +25,7 @@ import linecache
import renpy.display
import renpy.pyanalysis
import renpy.sl2
from renpy.display.motion import Transform
from renpy.display.layout import Fixed
@@ -116,6 +117,10 @@ class SLContext(renpy.ui.Addable):
# once could be a performance problem.
self.predicted = set()
# True if we're in a true showif block, False if we're in a false showif
# block, or None if we're not in a showif block.
self.showif = None
def get_style_group(self):
style_prefix = self.style_prefix
@@ -381,6 +386,9 @@ class SLCache(object):
# or children are changed.
self.copy_on_change = False
# The ShowIf this statement was wrapped in the last time it was wrapped.
self.old_showif = None
class SLDisplayable(SLBlock):
"""
A screen language AST node that corresponds to a displayable being
@@ -479,6 +487,13 @@ class SLDisplayable(SLBlock):
# We want to preserve last_keyword, however, in case we run a
# python block.
# If we have the id property, we're not constant - since we may get
# additional keywords via id. (It's unlikely, but id should be pretty
# rare.)
for k, _expr in self.keyword:
if k == "id":
self.constant = NOT_CONST
def keywords(self, context):
# We do not want to pass keywords to our parents, so just return.
return
@@ -513,8 +528,13 @@ class SLDisplayable(SLBlock):
i._scope(context.scope, True)
else:
context.children.append(cache.constant)
screen.widgets.update(cache.constant_widgets)
d = cache.constant
if context.showif is not None:
d = self.wrap_in_showif(d, context, cache)
context.children.append(d)
return
# Create the context.
@@ -550,8 +570,6 @@ class SLDisplayable(SLBlock):
if self.constant:
if ctx.uses_scope is None:
ctx.uses_scope = [ ]
if ctx.widgets is None:
ctx.widgets = { }
SLBlock.keywords(self, ctx)
@@ -644,6 +662,8 @@ class SLDisplayable(SLBlock):
fail = True
ctx.children = [ ]
ctx.showif = None
stack = renpy.ui.stack
stack.append(ctx)
@@ -717,9 +737,14 @@ class SLDisplayable(SLBlock):
for i in ctx.children:
main.add(i)
# Migrate grabs.
if old_main and (renpy.display.focus.grab is old_main):
renpy.display.focus.new_grab = main
# Inform the focus system about replacement displayables.
if (not context.predicting) and (old_d is not None):
replaced_by = renpy.display.focus.replaced_by
replaced_by[id(old_d)] = d
if d is not main:
for old_part, new_part in zip(old_d._composite_parts, d._composite_parts):
replaced_by[id(old_part)] = new_part
cache.displayable = d
cache.children = ctx.children
@@ -741,9 +766,14 @@ class SLDisplayable(SLBlock):
else:
d = transform(d)
if context.updating and isinstance(d, Transform) and (cache.transform is not None):
d.take_state(cache.transform)
d.take_execution_state(cache.transform)
if isinstance(d, Transform):
old_transform = cache.transform
if not context.updating:
old_transform = None
d.take_state(old_transform)
d.take_execution_state(old_transform)
cache.transform = d
@@ -751,23 +781,33 @@ class SLDisplayable(SLBlock):
cache.transform = None
cache.raw_transform = None
context.children.append(d)
if self.constant:
cache.constant = d
if widget_id is not None:
ctx.widgets[widget_id] = main
if self.scope and main.uses_scope:
ctx.uses_scope.append(main)
if context.widgets is None:
cache.constant_widgets = ctx.widgets
if context.uses_scope is None:
cache.constant_uses_scope = ctx.uses_scope
if context.showif is not None:
d = self.wrap_in_showif(d, context, cache)
context.children.append(d)
def wrap_in_showif(self, d, context, cache):
"""
Wraps `d` in a ShowIf displayable.
"""
rv = renpy.sl2.sldisplayables.ShowIf(context.showif, cache.old_showif)
rv.add(d)
if not context.predicting:
cache.old_showif = rv
return rv
def report_arguments(self, cache, positional, keywords, transform):
if positional:
report = [ ]
@@ -947,7 +987,7 @@ class SLIf(SLNode):
for i in block.children:
try:
i.execute(context)
i.execute(ctx)
except:
pass
@@ -963,7 +1003,72 @@ class SLIf(SLNode):
return
def copy_on_change(self, cache):
for _cont, block in self.entries:
for _cond, block in self.entries:
block.copy_on_change(cache)
class SLShowIf(SLNode):
"""
The AST node that corresponds to the showif statement.
"""
def __init__(self, loc):
"""
An AST node that represents an if statement.
"""
SLNode.__init__(self, loc)
# A list of entries, with each consisting of an expression (or
# None, for the else block) and a SLBlock.
self.entries = [ ]
def analyze(self, analysis):
for _cond, block in self.entries:
block.analyze(analysis)
def prepare(self, analysis):
# A list of prepared entries, with each consisting of expression
# bytecode and a SLBlock.
self.prepared_entries = [ ]
for cond, block in self.entries:
if cond is not None:
node = py_compile(cond, 'eval', ast_node=True)
self.constant = min(self.constant, analysis.is_constant(node))
cond = compile_expr(node)
block.prepare(analysis)
self.constant = min(self.constant, block.constant)
self.prepared_entries.append((cond, block))
self.last_keyword = True
def execute(self, context):
first_true = True
for cond, block in self.prepared_entries:
ctx = SLContext(context)
if not first_true:
ctx.showif = False
else:
if cond is None or eval(cond, context.globals, context.scope):
ctx.showif = True
first_true = False
else:
ctx.showif = False
for i in block.children:
i.execute(ctx)
def copy_on_change(self, cache):
for _cond, block in self.entries:
block.copy_on_change(cache)
@@ -981,10 +1086,12 @@ class SLFor(SLBlock):
def analyze(self, analysis):
if analysis.is_constant_expr(self.expression):
if analysis.is_constant_expr(self.expression) == GLOBAL_CONST:
analysis.push_control(True)
analysis.mark_constant(self.variable)
else:
analysis.push_control(False)
analysis.mark_not_constant(self.variable)
SLBlock.analyze(self, analysis)
@@ -1030,20 +1137,10 @@ class SLFor(SLBlock):
ctx = SLContext(context)
count = { }
for v in value:
for index, v in enumerate(value):
ctx.scope[variable] = v
index = id(v)
n = count.get(index, -1) + 1
count[index] = n
if n:
index = (index, n)
cache = oldcaches.get(index, None)
if cache is None:
@@ -1201,6 +1298,9 @@ class SLUse(SLNode):
# Figure out the cache to use.
# True if we want to force-mark this as an update.
update = False
if (not context.predicting) and self.id:
# If we have an id, look it up in the current screen's use_cache.
@@ -1210,11 +1310,16 @@ class SLUse(SLNode):
cache = current_screen.use_cache.get(use_id, None)
if cache is None:
cache = context.cache.get(self.serial, None)
if cache is not None:
update = True
if cache is None:
cache = { }
else:
if cache is None:
cache = context.cache.get(self.serial, None)
if cache is None:
cache = { }
context.cache[self.serial] = cache
current_screen.use_cache[use_id] = cache
@@ -1268,6 +1373,10 @@ class SLUse(SLNode):
ctx = SLContext(context)
ctx.scope = scope
ctx.cache = cache
if update:
ctx.updating = True
ast.execute(ctx)
def copy_on_change(self, cache):
+92 -2
View File
@@ -25,6 +25,74 @@
import renpy.display
import renpy.text.text
class ShowIf(renpy.display.layout.Container):
"""
This is a displayable that wraps displayables that are
underneath a showif statement.
"""
def __init__(self, condition, replaces=None):
super(ShowIf, self).__init__()
self.condition = condition
if replaces is None:
if condition:
self.pending_event = "appear"
else:
self.pending_event = None
self.show_child = condition
else:
if self.condition and not replaces.condition:
self.pending_event = "show"
elif not self.condition and replaces.condition:
self.pending_event = "hide"
else:
self.pending_event = replaces.pending_event
self.show_child = replaces.show_child
def per_interact(self):
if self.pending_event:
self.child.set_transform_event(self.pending_event)
self.pending_event = None
def render(self, width, height, st, at):
if isinstance(self.child, renpy.display.motion.Transform):
if self.condition or self.show_child:
cr = renpy.display.render.render(self.child, width, height, st, at)
self.show_child = self.condition or not self.child.hide_response
else:
if self.condition:
cr = renpy.display.render.render(self.child, width, height, st, at)
self.show_child = True
else:
self.show_child = False
if self.show_child:
cw, ch = cr.get_size()
rv = renpy.display.render.Render(cw, ch)
rv.blit(cr, (0, 0))
else:
rv = renpy.display.render.Render(0, 0)
self.offsets = [ (0, 0) ]
return rv
def event(self, ev, x, y, st):
if self.condition:
return self.child.event(ev, x, y, st)
else:
return None
def get_placement(self):
return self.child.get_placement()
from renpy.sl2.slparser import Positional, Keyword, Style, PrefixStyle, add
from renpy.sl2.slparser import DisplayableParser, many
@@ -54,6 +122,7 @@ position_property_names = [
"ysize",
"xysize",
"alt",
"debug",
]
position_properties = [ Style(i) for i in position_property_names ]
@@ -126,6 +195,7 @@ button_properties = [ Style(i) for i in [
"mouse",
"focus_mask",
"child",
"keyboard_focus",
] ]
bar_properties = [ Style(i) for i in [
@@ -145,6 +215,7 @@ bar_properties = [ Style(i) for i in [
"thumb_offset",
"mouse",
"unscrollable",
"keyboard_focus",
] ]
box_properties = [ Style(i) for i in [
@@ -393,6 +464,7 @@ def sl2viewport(**kwargs):
rv = d.child
rv._main = vp
rv._composite_parts = list(rv.children)
return rv
@@ -452,7 +524,24 @@ Keyword("id")
for i in renpy.atl.PROPERTIES:
Style(i)
DisplayableParser("add", renpy.ui._add, None, 0)
def sl2add(d, replaces=None, **kwargs):
d = renpy.easy.displayable(d)
d = d.parameterize('displayable', [ ])
rv = d
Transform = renpy.display.motion.Transform
if kwargs:
rv = Transform(child=d, **kwargs)
if isinstance(rv, Transform):
rv.take_state(replaces)
rv.take_execution_state(replaces)
return rv
DisplayableParser("add", sl2add, None, 0, replaces=True)
Positional("im")
Keyword("at")
Keyword("id")
@@ -482,9 +571,10 @@ add(position_properties)
DisplayableParser("mousearea", renpy.display.behavior.MouseArea, 0, replaces=True)
Keyword("hovered")
Keyword("unhovered")
Style("focus_mask")
add(ui_properties)
add(position_properties)
DisplayableParser("on", renpy.display.behavior.OnEvent, 0)
DisplayableParser("on", renpy.display.behavior.OnEvent, None, 0)
Positional("event")
Keyword("action")
+29 -6
View File
@@ -426,19 +426,40 @@ class DisplayableParser(Parser):
class IfParser(Parser):
def __init__(self, name):
def __init__(self, name, node_type, parent_contents):
"""
`node_type`
The type of node to create.
`parent_contents`
If true, our children must be children of our parent. Otherwise,
our children must be children of ourself.
"""
super(IfParser, self).__init__(name)
self.node_type = node_type
self.parent_contents = parent_contents
if not parent_contents:
childbearing_statements.add(self)
def parse(self, loc, l, parent):
rv = slast.SLIf(loc)
if self.parent_contents:
contents_from = parent
else:
contents_from = self
rv = self.node_type(loc)
condition = l.require(l.python_expression)
l.require(':')
block = slast.SLBlock(loc)
parent.parse_contents(l, block, block_only=True)
contents_from.parse_contents(l, block, block_only=True)
rv.entries.append((condition, block))
@@ -454,7 +475,7 @@ class IfParser(Parser):
l.require(':')
block = slast.SLBlock(loc)
parent.parse_contents(l, block, block_only=True)
contents_from.parse_contents(l, block, block_only=True)
rv.entries.append((condition, block))
@@ -466,7 +487,7 @@ class IfParser(Parser):
l.require(':')
block = slast.SLBlock(loc)
parent.parse_contents(l, block, block_only=True)
contents_from.parse_contents(l, block, block_only=True)
rv.entries.append((condition, block))
@@ -480,7 +501,9 @@ class IfParser(Parser):
return rv
if_statement = IfParser("if")
if_statement = IfParser("if", slast.SLIf, True)
IfParser("showif", slast.SLShowIf, False)
class ForParser(Parser):
+10
View File
@@ -33,6 +33,16 @@ def none_is_null(o):
else:
return renpy.easy.displayable(o)
def expand_focus_mask(v):
if v is None:
return v
elif v is False:
return v
elif v is True:
return v
else:
return renpy.easy.displayable(v)
def expand_outlines(l):
rv = [ ]
+4 -1
View File
@@ -20,7 +20,10 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from pygame cimport *
from sdl2 cimport *
from pygame_sdl2 cimport *
import_pygame_sdl2__surface()
from freetype cimport *
from ttgsubtable cimport *
from textsupport cimport Glyph, SPLIT_INSTEAD
+32 -29
View File
@@ -400,9 +400,9 @@ class Wrapper(renpy.object.Object):
if not stack:
raise Exception("Can't add displayable during init phase.")
# Pull out the special kwargs, id, at, and style_group.
# Pull out the special kwargs, widget_id, at, and style_group.
id = kwargs.pop("id", None) #@ReservedAssignment
widget_id = kwargs.pop("id", None) #@ReservedAssignment
at_list = kwargs.pop("at", [ ])
if not isinstance(at_list, (list, tuple)):
@@ -428,24 +428,19 @@ class Wrapper(renpy.object.Object):
do_add = True
if screen:
if id in screen.widget_properties:
keyword.update(screen.widget_properties[id])
if widget_id in screen.widget_properties:
keyword.update(screen.widget_properties[widget_id])
if id in screen.hidden_widgets:
if widget_id in screen.hidden_widgets:
do_add = False
grab = False
if old_transfers:
if self.replaces:
w = screen.old_widgets.get(id, None)
if w is not None:
keyword["replaces"] = w
if (renpy.display.focus.grab is w) and (not screen.hiding):
grab = True
old_main = screen.old_widgets.get(widget_id, None)
if self.replaces and old_main is not None:
keyword["replaces"] = old_main
else:
old_main = None
if self.style and "style" not in keyword:
keyword["style"] = style_group_style(self.style, style_group)
@@ -461,12 +456,15 @@ class Wrapper(renpy.object.Object):
del tb # Important! Prevents memory leaks via our frame.
raise
main = w._main or w
# Migrate the focus.
if (old_main is not None) and (not screen.hiding):
renpy.display.focus.replaced_by[id(old_main)] = main
# Wrap the displayable based on the at_list and at_stack.
atw = w
if grab:
renpy.display.focus.new_grab = w
while at_stack:
at_list.append(at_stack.pop())
@@ -486,23 +484,21 @@ class Wrapper(renpy.object.Object):
elif self.many:
stack.append(Many(w, self.imagemap, style_group))
main = w._main or w
# If we have an id, record the displayable, the transform,
# If we have an widget_id, record the displayable, the transform,
# and maybe take the state from a previous transform.
if screen and id is not None:
screen.widgets[id] = main
if screen and widget_id is not None:
screen.widgets[widget_id] = main
if isinstance(atw, renpy.display.motion.Transform):
screen.transforms[id] = atw
screen.transforms[widget_id] = atw
if old_transfers:
oldt = screen.old_transforms.get(widget_id, None)
else:
oldt = None
oldt = screen.old_transforms.get(id, None)
if oldt is not None:
atw.take_state(oldt)
atw.take_execution_state(oldt)
atw.take_state(oldt)
atw.take_execution_state(oldt)
# Clear out the add_tag.
add_tag = None
@@ -917,6 +913,7 @@ def _textbutton(label, clicked=None, style=None, text_style=None, substitute=Tru
text = renpy.text.text.Text(label, style=text_style, substitute=substitute, scope=scope, **text_kwargs)
rv.add(text)
rv._main = text
rv._composite_parts = [ text ]
return rv
textbutton = Wrapper(_textbutton)
@@ -942,6 +939,7 @@ def _label(label, style=None, text_style=None, substitute=True, scope=None, **kw
text = renpy.text.text.Text(label, style=text_style, substitute=substitute, scope=scope, **text_kwargs)
rv.add(text)
rv._main = text
rv._composite_parts = [ text ]
return rv
label = Wrapper(_label)
@@ -1145,13 +1143,18 @@ def _imagemap(ground=None, hover=None, insensitive=None, idle=None, selected_hov
properties.setdefault('fit_first', True)
rv = renpy.display.layout.MultiBox(layout='fixed', **properties)
parts = [ ]
if ground:
rv.add(renpy.easy.displayable(ground))
parts.append(ground)
box = renpy.display.layout.MultiBox(layout='fixed')
rv.add(box)
parts.append(box)
rv._main = box
rv._composite_parts = parts
return rv
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env python
from __future__ import print_function
import os.path
ENDINGS = [
".rpy",
]
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."""
def process_file(fn):
for i in ENDINGS:
if fn.endswith(i):
break
else:
return
print("Processing", fn)
lines = [ ]
with open(fn, "rb") as f:
for l in f:
if l.startswith("# TODO: Translation updated"):
continue
lines.append(l)
with open(fn, "wb") as f:
f.write("".join(lines))
def process(root):
for dirname, _dirs, files in os.walk(root):
for fn in files:
fn = os.path.join(dirname, fn)
process_file(fn)
process("launcher/game/tl")
process("templates")
process("tutorial/game/tl")

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