Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 655b2aeb58 |
+147
@@ -1,3 +1,150 @@
|
||||
New in 4.6
|
||||
----------
|
||||
|
||||
The demo was updated to show some of the new features listed below.
|
||||
|
||||
There's now a third state that hovered widgets can go into. The
|
||||
activate state is enabled when a button is clicked or a menu choice is
|
||||
selected. The activate state can only be seen when a transition occurs
|
||||
immediately after a button is clicked or a menu choice is selected.
|
||||
|
||||
The margin and padding properties have been broken up, so it's now
|
||||
possible to specify margin and padding for the left, right, top, and
|
||||
bottom of a windows.
|
||||
|
||||
The biggest internal change in this version is the introduction of a
|
||||
layer system in Ren'Py. Transitions can now work on an individual
|
||||
layer (as well as the previous behavior of the whole screen), so it's
|
||||
possible to do things like sliding in and out the dialogue box.
|
||||
|
||||
It's now possible to use transitions when entering and leaving the
|
||||
game menu, by assigning a transition object to
|
||||
library.enter_transition and library.exit_transition, respectively.
|
||||
|
||||
The preferences screen can now be entered from user code, and is
|
||||
now part of the main menu.
|
||||
|
||||
Character objects now support an interact argument when called
|
||||
directly. If it's False, then no interaction occurs. This lets a
|
||||
character window become part of a larger screen.
|
||||
|
||||
Two new extras have been added:
|
||||
|
||||
[list]
|
||||
[*] button_menu.rpy changes the way menus are displayed. Each menu
|
||||
choice is displayed in its own button, roughly centered on the
|
||||
screen. A single menu caption is displayed as narration, if such
|
||||
a caption exists.
|
||||
|
||||
[*] overlay_menu.rpy changes the behavior of right click from
|
||||
showing the game menu to showing a bank of buttons that, when
|
||||
clicked, bring the user to the game menu.
|
||||
[/list]
|
||||
|
||||
The overlay code was overhauled. It's now not possible for an overlay
|
||||
function to return a list of widgets. Instead, the overlay functions
|
||||
are expected to add widgets to the screen using the ui
|
||||
functions. Calling the new function renpy.restart_interaction will,
|
||||
among other things, cause the overlay functions to be called again, if
|
||||
one wants to replace the overlay on the screen.
|
||||
|
||||
A new variable, config.overlay_during_wait, controls if overlays are
|
||||
shown during the execution of wait statements.
|
||||
|
||||
Init blocks of the same priority are now assured to run in the order
|
||||
that they appear in a script file.
|
||||
|
||||
The renpy.interact() function has been eliminated. Use ui.interact()
|
||||
instead, as it performs more error checking.
|
||||
|
||||
Fixed a bug in which some redraws were ignored, and another in which
|
||||
the input widget failed to eat characters. (So typing 'f' in an input
|
||||
widget would be passed through, and eventually cause the game to go to
|
||||
fullscreen mode. No longer.)
|
||||
|
||||
4.5's demo script had a bug that cause it to crash, after playing the
|
||||
MPEG-1 movie. It was a bug in the demo script, and not in Ren'Py
|
||||
proper. It's fixed.
|
||||
|
||||
New in 4.5
|
||||
----------
|
||||
|
||||
We now ship an extras directory, with interesting sample code. It
|
||||
includes:
|
||||
|
||||
[list]
|
||||
[*] fullscreen.rpy, containing code to automatically switch the game
|
||||
into fullscreen on the first run, while preserving the user's
|
||||
preference on later runs.
|
||||
|
||||
[*] 640x480.rpy, which shows how to customize Ren'Py for a game that
|
||||
runs at 640x480.
|
||||
|
||||
[*] kanamode.rpy, contains the code to emulate the interface of the
|
||||
various Digital Object games, which display text one line at a
|
||||
time, but keep a page of text on the screen at once.
|
||||
([url=http://www.bishoujo.us/hosted/kanamode.jpg]screenshot[/url])
|
||||
[/list]
|
||||
|
||||
Added a new CropMove transition. This single class is the root of not
|
||||
one, not two, but fourteen transitions, in the forms of wipes, slides,
|
||||
slideaways, and rectangular irises. You can see some of these
|
||||
transitions in action in the demo, which now has a new section,
|
||||
"What's new with Ren'Py?"
|
||||
|
||||
Added support for playing MPEG-1 movies. Rather than explain it all
|
||||
here, we'l just mention that you can read the new "Movies" subsection
|
||||
of the "Multimedia: Sound, Music, and Movies" section of the Ren'Py
|
||||
manual. We support both hardware-accelerated full-window movies and
|
||||
using a movie as a widget. This is also shown in the what's new
|
||||
section of Ren'Py.
|
||||
|
||||
Added ui.grid(). This is a widget that places its children in a grid.
|
||||
The children must have sizes that do not consider the amount of space
|
||||
available for the grid to work.
|
||||
|
||||
Added ui.pausebehavior(). This separates pausing from the saybehavior
|
||||
paving the way for uninterruptable pauses (a bad idea, IMO) and menus
|
||||
that dismiss themseleves after a certain amount of time.
|
||||
|
||||
A new function, renpy.exists(), can check to see if a given file can
|
||||
be found in the searchpath. This could be used, for example, to make a
|
||||
game that only tries to play music if an add-on music package is
|
||||
downloaded, and the files placed in the appropriate place.
|
||||
|
||||
Added the ability to bind mouse events to mouseup as well as
|
||||
mousedown, and changed some of the default bindings to be on mouseup
|
||||
rather than mousedown. If you make changes to config.keymaps, you'll
|
||||
need to change mouse_1 to mousedown_1, and so on for various other
|
||||
button numbers.
|
||||
|
||||
Moved the place we look for menu sounds from the style of the choices
|
||||
in the menu to the style of the menu itself, as that seems more
|
||||
rational. Also, gave menus their own style. (Can't believe I forgot
|
||||
that.)
|
||||
|
||||
Made a few changes to the library screens. First of all, we
|
||||
rationalized (to some extent, it's still pretty ugly) the names of the
|
||||
styles for library widgets. So some of that may have changed. We
|
||||
placed preferences and the yes/no dialogue inside windows, which can
|
||||
be styled by the user. We then routed all button and label creation
|
||||
through two functions, _button_factory and _label_factory. By
|
||||
overriding these functions, the user can (for example) replace the
|
||||
default textbuttons with imagebuttons.
|
||||
|
||||
Made 'f' toggle fullscreen at the main menu. This will help on virtual
|
||||
windows, where the mouse doesn't work right in fullscreen mode.
|
||||
|
||||
Improved rendering speed. Now, we aggressively cache things to
|
||||
minimize re-renders, and only draw to the screen the parts of the
|
||||
screen that have changed. In common cases, such as the case where only
|
||||
some text has changed, this can greately improve performance. In other
|
||||
cases (transitions), we have to redraw the entire screen anyway, so
|
||||
there's little improvement.
|
||||
|
||||
Fixed a bug in which hiding the UI was not working properly. (This
|
||||
broke the center mouse button behavior.)
|
||||
|
||||
New in 4.4.2
|
||||
------------
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
Copyright 2004-2005 Tom Rothamel <pytom@bishoujo.us>
|
||||
Copyright 2004-2005 PyTom <pytom@bishoujo.us>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
|
||||
+158
-64
@@ -39,6 +39,7 @@ init -500:
|
||||
library.main_menu = [
|
||||
( "Start Game", "start" ),
|
||||
( "Continue Game", "_continue" ),
|
||||
( "Preferences", "_preferences" ),
|
||||
( "Quit Game", "_quit" ),
|
||||
]
|
||||
|
||||
@@ -63,6 +64,12 @@ init -500:
|
||||
# button.
|
||||
library.exit_sound = None
|
||||
|
||||
# Transition that occurs when entering the game menu.
|
||||
library.enter_transition = None
|
||||
|
||||
# Transition that occurs when leaving the game menu.
|
||||
library.exit_transition = None
|
||||
|
||||
# True if the skip indicator should be shown.
|
||||
library.skip_indicator = True
|
||||
|
||||
@@ -76,6 +83,61 @@ init -500:
|
||||
# True if we can save, false otherwise.
|
||||
_can_save = True
|
||||
|
||||
def _button_factory(label,
|
||||
type=None,
|
||||
selected=None,
|
||||
disabled=False,
|
||||
clicked=None,
|
||||
**properties):
|
||||
"""
|
||||
This function is called to create the various buttons used
|
||||
in the game menu. By overriding this function, one can
|
||||
(for example) replace the default textbuttons with image buttons.
|
||||
When it is called, it's expected to add a button to the screen.
|
||||
|
||||
@param label: The label of this button, before translation.
|
||||
|
||||
@param type: The type of the button. One of "mm" (main menu),
|
||||
"gm_nav" (game menu), "file_picker_nav", "yesno", or "prefs".
|
||||
|
||||
@param selected: True if the button is selected, False if not,
|
||||
or None if it doesn't matter.
|
||||
|
||||
@param disabled: True if the button is disabled, False if not.
|
||||
|
||||
@param clicked: A function that should be executed when the
|
||||
button is clicked.
|
||||
|
||||
@param properties: Addtional layout properties.
|
||||
"""
|
||||
|
||||
style = type
|
||||
|
||||
if selected:
|
||||
style += "_selected"
|
||||
|
||||
if disabled:
|
||||
style += "_disabled"
|
||||
|
||||
style = style + "_button"
|
||||
text_style = style + "_text"
|
||||
|
||||
ui.textbutton(_(label), style=style, text_style=text_style, clicked=clicked, **properties)
|
||||
|
||||
def _label_factory(label, type, **properties):
|
||||
"""
|
||||
This function is called to create a new label. It can be
|
||||
overridden by the user to change how these labels are created.
|
||||
|
||||
@param label: The label of the box.
|
||||
|
||||
@param type: "prefs" or "yesno".
|
||||
|
||||
@param properties: This may contain position properties.
|
||||
"""
|
||||
|
||||
ui.text(_(label), style=type + "_label", **properties)
|
||||
|
||||
# The function that's used to translate strings in the game menu.
|
||||
def _(s):
|
||||
"""
|
||||
@@ -87,30 +149,16 @@ init -500:
|
||||
else:
|
||||
return s
|
||||
|
||||
# Called to make a screenshot happen.
|
||||
def _screenshot():
|
||||
renpy.screenshot("screenshot.bmp")
|
||||
|
||||
# Are the windows currently hidden?
|
||||
_windows_hidden = False
|
||||
|
||||
# Hides the windows.
|
||||
def _hide_windows():
|
||||
global _windows_hidden
|
||||
|
||||
if _windows_hidden:
|
||||
return
|
||||
|
||||
try:
|
||||
_windows_hidden = True
|
||||
renpy.interact(renpy.SayBehavior())
|
||||
finally:
|
||||
_windows_hidden = False
|
||||
|
||||
|
||||
# Set up the default keymap.
|
||||
python hide:
|
||||
|
||||
# Called to make a screenshot happen.
|
||||
def screenshot():
|
||||
renpy.screenshot("screenshot.bmp")
|
||||
|
||||
def invoke_game_menu():
|
||||
renpy.play(library.enter_sound)
|
||||
renpy.call_in_new_context('_game_menu')
|
||||
@@ -121,12 +169,12 @@ init -500:
|
||||
# The default keymap.
|
||||
km = renpy.Keymap(
|
||||
rollback = renpy.rollback,
|
||||
screenshot = _screenshot,
|
||||
screenshot = screenshot,
|
||||
toggle_fullscreen = renpy.toggle_fullscreen,
|
||||
toggle_music = renpy.toggle_music,
|
||||
toggle_skip = toggle_skipping,
|
||||
game_menu = invoke_game_menu,
|
||||
hide_windows = _hide_windows,
|
||||
hide_windows = renpy.curried_call_in_new_context("_hide_windows")
|
||||
)
|
||||
|
||||
config.underlay = [ km ]
|
||||
@@ -142,11 +190,24 @@ init -500:
|
||||
ui.conditional("config.skipping")
|
||||
ui.text(_("Skip Mode"), style='skip_indicator')
|
||||
|
||||
return [ ]
|
||||
|
||||
config.overlay_functions.append(skip_indicator)
|
||||
|
||||
return
|
||||
|
||||
label _hide_windows:
|
||||
|
||||
if _windows_hidden:
|
||||
return
|
||||
|
||||
python:
|
||||
_windows_hidden = True
|
||||
ui.saybehavior()
|
||||
ui.interact(suppress_overlay=True)
|
||||
_windows_hidden = False
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
# This is the true starting point of the program. Sssh... Don't
|
||||
# tell anyone.
|
||||
@@ -166,8 +227,11 @@ label _main_menu:
|
||||
|
||||
label _library_main_menu:
|
||||
|
||||
scene
|
||||
|
||||
python hide:
|
||||
|
||||
ui.add(renpy.Keymap(toggle_fullscreen = renpy.toggle_fullscreen))
|
||||
ui.keymousebehavior()
|
||||
|
||||
ui.window(style='mm_root_window')
|
||||
@@ -177,13 +241,13 @@ label _library_main_menu:
|
||||
ui.vbox()
|
||||
|
||||
for text, label in library.main_menu:
|
||||
ui.textbutton(text, clicked=ui.returns(label))
|
||||
_button_factory(text, "mm", clicked=ui.returns(label))
|
||||
|
||||
ui.close()
|
||||
ui.close()
|
||||
|
||||
store._result = renpy.interact(suppress_overlay = True,
|
||||
suppress_underlay = True)
|
||||
store._result = ui.interact(suppress_overlay = True,
|
||||
suppress_underlay = True)
|
||||
|
||||
# Computed jump to the appropriate label.
|
||||
$ renpy.jump(_result)
|
||||
@@ -195,7 +259,19 @@ label _continue:
|
||||
$ _can_save = False
|
||||
$ _at_main_menu = True
|
||||
|
||||
$ renpy.call_in_new_context("_load_menu")
|
||||
$ renpy.call_in_new_context("_game_menu_load")
|
||||
|
||||
$ _can_save = True
|
||||
$ _at_main_menu = False
|
||||
|
||||
jump _library_main_menu
|
||||
|
||||
# Used to call the game menu.
|
||||
label _preferences:
|
||||
$ _can_save = False
|
||||
$ _at_main_menu = True
|
||||
|
||||
$ renpy.call_in_new_context("_game_menu_preferences")
|
||||
|
||||
$ _can_save = True
|
||||
$ _at_main_menu = False
|
||||
@@ -213,8 +289,7 @@ init -500:
|
||||
# This is used to store scratch data that's used by the
|
||||
# library, but shouldn't be saved out as part of the savegame.
|
||||
_scratch = object()
|
||||
|
||||
|
||||
|
||||
# This returns a window containing the game menu navigation
|
||||
# buttons, set up to jump to the appropriate screen sections.
|
||||
def _game_nav(selected):
|
||||
@@ -230,32 +305,24 @@ init -500:
|
||||
ui.vbox()
|
||||
|
||||
for key, label, target, enabled in library.game_menu:
|
||||
style="gm_nav_button"
|
||||
text_style="gm_nav_button_text"
|
||||
|
||||
if key == selected:
|
||||
style = 'gm_nav_selected_button'
|
||||
text_style = 'gm_nav_selected_button_text'
|
||||
|
||||
clicked = ui.jumps(target)
|
||||
disabled = False
|
||||
|
||||
if not eval(enabled):
|
||||
style = 'gm_nav_disabled_button'
|
||||
text_style = 'gm_nav_disabled_button_text'
|
||||
|
||||
clicked = lambda : None
|
||||
disabled = True
|
||||
clicked = ui.returns(None)
|
||||
|
||||
|
||||
ui.textbutton(_(label), style=style, text_style=text_style,
|
||||
clicked=clicked)
|
||||
_button_factory(label, "gm_nav", selected=(key==selected),
|
||||
disabled=disabled, clicked=clicked)
|
||||
|
||||
ui.close()
|
||||
ui.close()
|
||||
|
||||
def _game_interact():
|
||||
|
||||
return renpy.interact(suppress_underlay=True,
|
||||
suppress_overlay=True)
|
||||
return ui.interact(suppress_underlay=True,
|
||||
suppress_overlay=True)
|
||||
|
||||
|
||||
def _render_new_slot(name, save):
|
||||
@@ -332,32 +399,29 @@ init -500:
|
||||
_game_nav(selected)
|
||||
|
||||
ui.window(style='file_picker_window')
|
||||
|
||||
ui.vbox() # whole thing.
|
||||
|
||||
# Draw the navigation.
|
||||
ui.hbox(padding=library.padding * 10, style='file_picker_navbox') # nav buttons.
|
||||
|
||||
def tb(cond, label, clicked):
|
||||
if cond:
|
||||
style = 'button'
|
||||
text_style = 'button_text'
|
||||
else:
|
||||
style = 'disabled_button'
|
||||
text_style = 'disabled_button_text'
|
||||
|
||||
ui.textbutton(label, style=style, text_style=text_style, clicked=clicked)
|
||||
|
||||
_button_factory(label, "file_picker_nav", disabled=not cond, clicked=clicked)
|
||||
|
||||
# Previous
|
||||
tb(fpi > 0, _('Previous'), ui.returns(("fpidelta", -1)))
|
||||
|
||||
# Quick Access
|
||||
for i in range(0, library.file_quick_access_pages):
|
||||
target = i * library.file_page_length
|
||||
tb(fpi != target, str(i + 1), ui.returns(("fpiset", target)))
|
||||
|
||||
# Next
|
||||
tb(True, _('Next'), ui.returns(("fpidelta", +1)))
|
||||
|
||||
ui.close() # nav buttons.
|
||||
# Done with nav buttons.
|
||||
ui.close()
|
||||
|
||||
# This draws a single slot.
|
||||
def entry(offset):
|
||||
i = fpi + offset
|
||||
|
||||
@@ -369,6 +433,7 @@ init -500:
|
||||
_render_savefile(name, saves[name], newest)
|
||||
|
||||
|
||||
# Actually draw a slot.
|
||||
ui.hbox() # slots
|
||||
|
||||
ui.vbox()
|
||||
@@ -402,9 +467,23 @@ init -500:
|
||||
|
||||
_game_nav(screen)
|
||||
|
||||
ui.text(message, style='yesno_prompt')
|
||||
ui.textbutton(_("Yes"), style='yesno_yes', clicked=ui.returns(True))
|
||||
ui.textbutton(_("No"), style='yesno_no', clicked=ui.returns(False))
|
||||
ui.window(style='yesno_window')
|
||||
ui.vbox(library.padding * 10, xpos=0.5, xanchor='center', ypos=0.5, yanchor='center')
|
||||
|
||||
_label_factory(message, "yesno", xpos=0.5, xanchor='center')
|
||||
|
||||
ui.grid(5, 1, xfill=True)
|
||||
|
||||
# The extra nulls are because we want equal whitespace surrounding
|
||||
# the two buttons. It should work as long as we have xfill=True
|
||||
ui.null()
|
||||
_button_factory("Yes", 'yesno', clicked=ui.returns(True), xpos=0.5, xanchor='center')
|
||||
ui.null()
|
||||
_button_factory("No", 'yesno', clicked=ui.returns(False), xpos=0.5, xanchor='center')
|
||||
ui.null()
|
||||
|
||||
ui.close()
|
||||
ui.close()
|
||||
|
||||
return _game_interact()
|
||||
|
||||
@@ -427,21 +506,32 @@ init -500:
|
||||
|
||||
|
||||
# Factored this all into one place, to make our lives a bit easier.
|
||||
label _take_screenshot:
|
||||
label _enter_game_menu:
|
||||
scene
|
||||
$ renpy.movie_stop()
|
||||
$ renpy.take_screenshot((library.thumbnail_width, library.thumbnail_height))
|
||||
|
||||
if library.enter_transition:
|
||||
$ renpy.transition(library.enter_transition)
|
||||
|
||||
return
|
||||
|
||||
# Entry points from the game into menu-space.
|
||||
label _load_menu:
|
||||
call _take_screenshot
|
||||
jump _load_screen
|
||||
|
||||
label _game_menu:
|
||||
call _take_screenshot
|
||||
label _game_menu_save:
|
||||
call _enter_game_menu
|
||||
jump _save_screen
|
||||
|
||||
label _game_menu_load:
|
||||
call _enter_game_menu
|
||||
jump _load_screen
|
||||
|
||||
label _game_menu_preferences:
|
||||
call _enter_game_menu
|
||||
jump _prefs_screen
|
||||
|
||||
label _confirm_quit:
|
||||
call _take_screenshot
|
||||
call _enter_game_menu
|
||||
jump _quit_screen
|
||||
|
||||
# Menu screens.
|
||||
@@ -503,6 +593,10 @@ label _noisy_return:
|
||||
|
||||
# Return to the game.
|
||||
label _return:
|
||||
|
||||
if library.exit_transition:
|
||||
$ renpy.transition(library.exit_transition)
|
||||
|
||||
return
|
||||
|
||||
# Random nice things to have.
|
||||
|
||||
+13
-14
@@ -46,28 +46,22 @@ init -450:
|
||||
if not values:
|
||||
return
|
||||
|
||||
ui.window(style='prefs_pref')
|
||||
ui.vbox(style='prefs_pref')
|
||||
ui.text(_(self.name), style='prefs_label')
|
||||
|
||||
_label_factory(self.name, "prefs")
|
||||
|
||||
cur = getattr(_preferences, self.field)
|
||||
|
||||
for name, value in values:
|
||||
|
||||
style = 'prefs_button'
|
||||
text_style = 'prefs_button_text'
|
||||
|
||||
if cur == value:
|
||||
style = 'prefs_selected_button'
|
||||
text_style = 'prefs_selected_button_text'
|
||||
|
||||
def clicked(value=value):
|
||||
setattr(_preferences, self.field, value)
|
||||
return True
|
||||
|
||||
ui.textbutton(_(name),
|
||||
style=style,
|
||||
text_style=text_style,
|
||||
clicked=clicked)
|
||||
_button_factory(name, "prefs",
|
||||
selected=cur==value,
|
||||
clicked=clicked)
|
||||
|
||||
ui.close()
|
||||
|
||||
@@ -123,16 +117,21 @@ label _prefs_screen:
|
||||
|
||||
_game_nav("prefs")
|
||||
|
||||
ui.vbox(library.padding * 3, style='prefs_left')
|
||||
ui.window(style='prefs_window')
|
||||
ui.grid(2, 1, xfill=True)
|
||||
|
||||
ui.vbox(library.padding * 3, xpos=0.5, xanchor='center')
|
||||
for i in library.left_preferences:
|
||||
i.render_preference()
|
||||
ui.close()
|
||||
|
||||
ui.vbox(library.padding * 3, style='prefs_right')
|
||||
ui.vbox(library.padding * 3, xpos=0.5, xanchor='center')
|
||||
for i in library.right_preferences:
|
||||
i.render_preference()
|
||||
ui.close()
|
||||
|
||||
ui.close()
|
||||
|
||||
_game_interact()
|
||||
|
||||
jump _prefs_screen
|
||||
|
||||
+52
-51
@@ -14,7 +14,7 @@
|
||||
# to your script. No need to mess around here, it will just make your
|
||||
# life harder when a new version of Ren'Py is released.
|
||||
|
||||
init -250:
|
||||
init -1000:
|
||||
python hide:
|
||||
|
||||
style.create('default', None,
|
||||
@@ -122,14 +122,17 @@ init -250:
|
||||
|
||||
# Styles that are used for menus.
|
||||
|
||||
style.create('menu', 'default',
|
||||
"(sound, position) The style that is used for menus themselves.")
|
||||
|
||||
style.create('menu_caption', 'default',
|
||||
"""(text) The style that is used to render a menu
|
||||
caption.""")
|
||||
"(text) The style that is used to render a menu caption.")
|
||||
|
||||
style.create('menu_choice', 'default',
|
||||
"""(text, hover, sound) The style that is used to render a menu choice.""")
|
||||
|
||||
style.menu_choice.hover_color = (255, 255, 0, 255) # yellow
|
||||
style.menu_choice.activate_color = (255, 255, 0, 255) # yellow
|
||||
style.menu_choice.idle_color = (0, 255, 255, 255) # cyan
|
||||
|
||||
style.create('menu_window', 'window',
|
||||
@@ -197,6 +200,7 @@ init -250:
|
||||
style.button_text.size = 24
|
||||
style.button_text.color = dark_cyan
|
||||
style.button_text.hover_color = bright_cyan
|
||||
style.button_text.activate_color = bright_cyan
|
||||
style.button_text.drop_shadow = (2, 2)
|
||||
|
||||
# Selected button.
|
||||
@@ -208,6 +212,7 @@ init -250:
|
||||
|
||||
style.selected_button_text.color = dark_red
|
||||
style.selected_button_text.hover_color = bright_red
|
||||
style.selected_button_text.activate_color = bright_red
|
||||
|
||||
# Disabled button.
|
||||
|
||||
@@ -222,6 +227,7 @@ init -250:
|
||||
|
||||
style.disabled_button_text.color = (128, 128, 128, 255)
|
||||
style.disabled_button_text.hover_color = (128, 128, 128, 255)
|
||||
style.disabled_button_text.activate_color = (128, 128, 128, 255)
|
||||
|
||||
|
||||
# Bar.
|
||||
@@ -301,12 +307,17 @@ init -250:
|
||||
'(position) The position of the naviation (next/previous) buttons in the file picker.')
|
||||
|
||||
style.file_picker_navbox.xmargin = 10
|
||||
|
||||
style.create('file_picker_image', 'default',
|
||||
'(position) The position of the image in each file picker entry.')
|
||||
|
||||
style.file_picker_image.xminimum = 280
|
||||
style.create('file_picker_nav_button', 'button',
|
||||
'(window, hover) The style that is used for enabled file picker navigation buttons.')
|
||||
style.create('file_picker_nav_button_text', 'button_text',
|
||||
'(text) The style that is used for the label of enabled file picker navigation buttons.')
|
||||
|
||||
style.create('file_picker_nav_disabled_button', 'disabled_button',
|
||||
'(window, hover) The style that is used for disabled file picker navigation buttons.')
|
||||
style.create('file_picker_nav_disabled_button_text', 'disabled_button_text',
|
||||
'(text) The style that is used for the label of disabled file picker navigation buttons.')
|
||||
|
||||
style.create('file_picker_entry', 'button',
|
||||
'(window, hover) The style that is used for each of the slots in the file picker.')
|
||||
|
||||
@@ -318,6 +329,7 @@ init -250:
|
||||
|
||||
style.file_picker_entry.idle_background = Solid((255, 255, 255, 255))
|
||||
style.file_picker_entry.hover_background = Solid((255, 255, 192, 255))
|
||||
style.file_picker_entry.activate_background = Solid((255, 255, 192, 255))
|
||||
|
||||
style.create('file_picker_text', 'default',
|
||||
'(text) A base style for all text that is displayed in the file picker.')
|
||||
@@ -333,6 +345,7 @@ init -250:
|
||||
'(text) The style that is applied to the old indicator in the file pciker.')
|
||||
|
||||
style.file_picker_new.hover_color = bright_red
|
||||
style.file_picker_new.activate_color = bright_red
|
||||
style.file_picker_new.idle_color = dark_red
|
||||
style.file_picker_new.minwidth = 30
|
||||
style.file_picker_old.minwidth = 30
|
||||
@@ -343,78 +356,66 @@ init -250:
|
||||
style.create('file_picker_empty_slot', 'file_picker_text',
|
||||
'(text) The style that is used for the empty slot indicator in the file picker.')
|
||||
|
||||
style.create('yesno_prompt', 'default',
|
||||
style.create('yesno_label', 'default',
|
||||
'(text, position) The style used for the prompt in a yes/no dialog.')
|
||||
|
||||
style.yesno_prompt.xpos = 0.5
|
||||
style.yesno_prompt.xanchor = 'center'
|
||||
style.yesno_label.color = green
|
||||
|
||||
style.yesno_prompt.ypos = 0.25
|
||||
style.yesno_prompt.yanchor = 'center'
|
||||
style.create('yesno_button', 'button',
|
||||
'(window, hover) The style of yes/no buttons.')
|
||||
|
||||
style.yesno_prompt.color = green
|
||||
|
||||
style.create('yesno_yes', 'button',
|
||||
'(position) The position of the yes button on the screen.')
|
||||
|
||||
style.yesno_yes.xpos = 0.33
|
||||
style.yesno_yes.xanchor = 'center'
|
||||
style.yesno_yes.ypos = 0.33
|
||||
style.yesno_yes.yanchor = 'center'
|
||||
|
||||
style.create('yesno_no', 'button',
|
||||
'(position) The position of the no button on the screen.')
|
||||
|
||||
style.yesno_no.xpos = 0.66
|
||||
style.yesno_no.xanchor = 'center'
|
||||
style.yesno_no.ypos = 0.33
|
||||
style.yesno_no.yanchor = 'center'
|
||||
style.create('yesno_button_text', 'button_text',
|
||||
'(window, hover) The style of yes/no button text.')
|
||||
|
||||
style.create('yesno_window', 'default',
|
||||
'(window) The style of a window containing a yes/no dialogue.')
|
||||
|
||||
style.yesno_window.xfill = True
|
||||
style.yesno_window.yminimum = 0.5
|
||||
|
||||
# Preferences
|
||||
|
||||
|
||||
style.create('prefs_pref', 'default',
|
||||
'(window, position) The position of the box containing an individual preference.')
|
||||
|
||||
style.prefs_pref.xpos = 0.5
|
||||
style.prefs_pref.xanchor = 'center'
|
||||
|
||||
style.create('prefs_label', 'default',
|
||||
'(text, position) The style that is applied to the label of a block of preferences.')
|
||||
|
||||
style.prefs_label.xpos = 0.5
|
||||
style.prefs_label.xanchor = "center"
|
||||
style.prefs_label.color = green
|
||||
|
||||
style.create('prefs_pref', 'default',
|
||||
'(position) The position of the box containing an individual preference.')
|
||||
|
||||
style.prefs_pref.xpos = 0.5
|
||||
style.prefs_pref.xanchor = 'center'
|
||||
|
||||
style.create('prefs_left', 'default',
|
||||
'(position) The position of the left column of preferences.')
|
||||
|
||||
style.prefs_left.xpos = 0.25
|
||||
style.prefs_left.xanchor = "center"
|
||||
style.prefs_left.ypos = 0.05
|
||||
style.prefs_left.yalign = "top"
|
||||
|
||||
style.create('prefs_right', 'default',
|
||||
'(position) The position of the right column of preferences.')
|
||||
|
||||
style.prefs_right.xpos = 0.75
|
||||
style.prefs_right.xanchor = "center"
|
||||
style.prefs_right.ypos = 0.05
|
||||
style.prefs_right.yalign = "top"
|
||||
|
||||
style.create('prefs_button', 'button',
|
||||
'(window, hover) The style of an unselected preferences button.')
|
||||
|
||||
style.prefs_button.xpos = 0.5
|
||||
style.prefs_button.xanchor = 'center'
|
||||
|
||||
style.create('prefs_button_text', 'button_text',
|
||||
'(text, hover) The style of the text of an unselected preferences button.')
|
||||
|
||||
style.create('prefs_selected_button', 'selected_button',
|
||||
'(window, hover) The style of a selected preferences button.')
|
||||
|
||||
style.prefs_selected_button.xpos = 0.5
|
||||
style.prefs_selected_button.xanchor = 'center'
|
||||
|
||||
style.create('prefs_selected_button_text', 'selected_button_text',
|
||||
'(text, hover) The style of the text of a selected preferences button.')
|
||||
|
||||
style.create('prefs_window', 'default',
|
||||
'(window, position) A window containing all preferences.')
|
||||
|
||||
style.prefs_window.xfill=True
|
||||
style.prefs_window.ypadding = 0.05
|
||||
|
||||
style.create('skip_indicator', 'default',
|
||||
'(text, position) The style of the text that is used to indicate that skipping is in progress.')
|
||||
|
||||
style.skip_indicator.xpos = 10
|
||||
style.skip_indicator.ypos = 10
|
||||
|
||||
|
||||
+10
@@ -72,6 +72,16 @@ if __name__ == "__main__":
|
||||
|
||||
type, value, tb = sys.exc_info()
|
||||
|
||||
|
||||
print >>f, type.__name__ + ":",
|
||||
print >>f, unicode(e).encode('utf-8')
|
||||
print >>f
|
||||
print >>f, renpy.game.exception_info
|
||||
|
||||
print >>f
|
||||
print >>f, "-- Full Traceback ------------------------------------------------------------"
|
||||
print >>f
|
||||
|
||||
traceback.print_tb(tb, None, sys.stdout)
|
||||
traceback.print_tb(tb, None, f)
|
||||
|
||||
|
||||
+822
@@ -0,0 +1,822 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Perforce Defect Tracking Integration Project
|
||||
# <http://www.ravenbrook.com/project/p4dti/>
|
||||
#
|
||||
# COVERAGE.PY -- COVERAGE TESTING
|
||||
#
|
||||
# Gareth Rees, Ravenbrook Limited, 2001-12-04
|
||||
# Ned Batchelder, 2004-12-12
|
||||
# http://nedbatchelder.com/code/modules/coverage.html
|
||||
#
|
||||
#
|
||||
# 1. INTRODUCTION
|
||||
#
|
||||
# This module provides coverage testing for Python code.
|
||||
#
|
||||
# The intended readership is all Python developers.
|
||||
#
|
||||
# This document is not confidential.
|
||||
#
|
||||
# See [GDR 2001-12-04a] for the command-line interface, programmatic
|
||||
# interface and limitations. See [GDR 2001-12-04b] for requirements and
|
||||
# design.
|
||||
|
||||
"""Usage:
|
||||
|
||||
coverage.py -x MODULE.py [ARG1 ARG2 ...]
|
||||
Execute module, passing the given command-line arguments, collecting
|
||||
coverage data.
|
||||
|
||||
coverage.py -e
|
||||
Erase collected coverage data.
|
||||
|
||||
coverage.py -r [-m] FILE1 FILE2 ...
|
||||
Report on the statement coverage for the given files. With the -m
|
||||
option, show line numbers of the statements that weren't executed.
|
||||
|
||||
coverage.py -a [-d dir] FILE1 FILE2 ...
|
||||
Make annotated copies of the given files, marking statements that
|
||||
are executed with > and statements that are missed with !. With
|
||||
the -d option, make the copies in that directory. Without the -d
|
||||
option, make each copy in the same directory as the original.
|
||||
|
||||
Coverage data is saved in the file .coverage by default. Set the
|
||||
COVERAGE_FILE environment variable to save it somewhere else."""
|
||||
|
||||
__version__ = "2.2.20041231" # see detailed history at the end of this file.
|
||||
|
||||
import compiler
|
||||
import compiler.visitor
|
||||
import os
|
||||
import re
|
||||
import string
|
||||
import sys
|
||||
import types
|
||||
|
||||
|
||||
# 2. IMPLEMENTATION
|
||||
#
|
||||
# This uses the "singleton" pattern.
|
||||
#
|
||||
# The word "morf" means a module object (from which the source file can
|
||||
# be deduced by suitable manipulation of the __file__ attribute) or a
|
||||
# filename.
|
||||
#
|
||||
# When we generate a coverage report we have to canonicalize every
|
||||
# filename in the coverage dictionary just in case it refers to the
|
||||
# module we are reporting on. It seems a shame to throw away this
|
||||
# information so the data in the coverage dictionary is transferred to
|
||||
# the 'cexecuted' dictionary under the canonical filenames.
|
||||
#
|
||||
# The coverage dictionary is called "c" and the trace function "t". The
|
||||
# reason for these short names is that Python looks up variables by name
|
||||
# at runtime and so execution time depends on the length of variables!
|
||||
# In the bottleneck of this application it's appropriate to abbreviate
|
||||
# names to increase speed.
|
||||
|
||||
# A dictionary with an entry for (Python source file name, line number
|
||||
# in that file) if that line has been executed.
|
||||
c = {}
|
||||
|
||||
# t(f, x, y). This method is passed to sys.settrace as a trace
|
||||
# function. See [van Rossum 2001-07-20b, 9.2] for an explanation of
|
||||
# sys.settrace and the arguments and return value of the trace function.
|
||||
# See [van Rossum 2001-07-20a, 3.2] for a description of frame and code
|
||||
# objects.
|
||||
|
||||
def t(f, w, a):
|
||||
if w == 'line':
|
||||
c[(f.f_code.co_filename, f.f_lineno)] = 1
|
||||
return t
|
||||
|
||||
class StatementFindingAstVisitor(compiler.visitor.ASTVisitor):
|
||||
def __init__(self, statements, excluded, suite_spots):
|
||||
compiler.visitor.ASTVisitor.__init__(self)
|
||||
self.statements = statements
|
||||
self.excluded = excluded
|
||||
self.suite_spots = suite_spots
|
||||
self.excluding_suite = 0
|
||||
|
||||
def doRecursive(self, node):
|
||||
self.recordNodeLine(node)
|
||||
for n in node.getChildNodes():
|
||||
self.dispatch(n)
|
||||
|
||||
visitStmt = visitModule = doRecursive
|
||||
|
||||
def doCode(self, node):
|
||||
if hasattr(node, 'decorators') and node.decorators:
|
||||
self.dispatch(node.decorators)
|
||||
self.doSuite(node, node.code)
|
||||
|
||||
visitFunction = visitClass = doCode
|
||||
|
||||
def getFirstLine(self, node):
|
||||
# Find the first line in the tree node.
|
||||
lineno = node.lineno
|
||||
for n in node.getChildNodes():
|
||||
f = self.getFirstLine(n)
|
||||
if lineno and f:
|
||||
lineno = min(lineno, f)
|
||||
else:
|
||||
lineno = lineno or f
|
||||
return lineno
|
||||
|
||||
def getLastLine(self, node):
|
||||
# Find the first line in the tree node.
|
||||
lineno = node.lineno
|
||||
for n in node.getChildNodes():
|
||||
lineno = max(lineno, self.getLastLine(n))
|
||||
return lineno
|
||||
|
||||
def doStatement(self, node):
|
||||
self.recordLine(self.getFirstLine(node))
|
||||
|
||||
visitAssert = visitAssign = visitAssTuple = visitDiscard = visitPrint = \
|
||||
visitPrintnl = visitRaise = visitSubscript = \
|
||||
visitDecorators = \
|
||||
doStatement
|
||||
|
||||
def recordNodeLine(self, node):
|
||||
return self.recordLine(node.lineno)
|
||||
|
||||
def recordLine(self, lineno):
|
||||
# Returns a bool, whether the line is included or excluded.
|
||||
if lineno:
|
||||
# Multi-line tests introducing suites have to get charged to their
|
||||
# keyword.
|
||||
if lineno in self.suite_spots:
|
||||
lineno = self.suite_spots[lineno][0]
|
||||
# If we're inside an exluded suite, record that this line was
|
||||
# excluded.
|
||||
if self.excluding_suite:
|
||||
self.excluded[lineno] = 1
|
||||
return 0
|
||||
# If this line is excluded, or suite_spots maps this line to
|
||||
# another line that is exlcuded, then we're excluded.
|
||||
elif self.excluded.has_key(lineno) or \
|
||||
self.suite_spots.has_key(lineno) and \
|
||||
self.excluded.has_key(self.suite_spots[lineno][1]):
|
||||
return 0
|
||||
# Otherwise, this is an executable line.
|
||||
else:
|
||||
self.statements[lineno] = 1
|
||||
return 1
|
||||
return 0
|
||||
|
||||
default = recordNodeLine
|
||||
|
||||
def recordAndDispatch(self, node):
|
||||
self.recordNodeLine(node)
|
||||
self.dispatch(node)
|
||||
|
||||
def doSuite(self, intro, body, exclude=0):
|
||||
exsuite = self.excluding_suite
|
||||
if exclude or (intro and not self.recordNodeLine(intro)):
|
||||
self.excluding_suite = 1
|
||||
self.recordAndDispatch(body)
|
||||
self.excluding_suite = exsuite
|
||||
|
||||
def doPlainWordSuite(self, prevsuite, suite):
|
||||
# Finding the exclude lines for else's is tricky, because they aren't
|
||||
# present in the compiler parse tree. Look at the previous suite,
|
||||
# and find its last line. If any line between there and the else's
|
||||
# first line are excluded, then we exclude the else.
|
||||
lastprev = self.getLastLine(prevsuite)
|
||||
firstelse = self.getFirstLine(suite)
|
||||
for l in range(lastprev+1, firstelse):
|
||||
if self.suite_spots.has_key(l):
|
||||
self.doSuite(None, suite, exclude=self.excluded.has_key(l))
|
||||
break
|
||||
else:
|
||||
self.doSuite(None, suite)
|
||||
|
||||
def doElse(self, prevsuite, node):
|
||||
if node.else_:
|
||||
self.doPlainWordSuite(prevsuite, node.else_)
|
||||
|
||||
def visitFor(self, node):
|
||||
self.doSuite(node, node.body)
|
||||
self.doElse(node.body, node)
|
||||
|
||||
def visitIf(self, node):
|
||||
# The first test has to be handled separately from the rest.
|
||||
# The first test is credited to the line with the "if", but the others
|
||||
# are credited to the line with the test for the elif.
|
||||
self.doSuite(node, node.tests[0][1])
|
||||
for t, n in node.tests[1:]:
|
||||
self.doSuite(t, n)
|
||||
self.doElse(node.tests[-1][1], node)
|
||||
|
||||
def visitWhile(self, node):
|
||||
self.doSuite(node, node.body)
|
||||
self.doElse(node.body, node)
|
||||
|
||||
def visitTryExcept(self, node):
|
||||
self.doSuite(node, node.body)
|
||||
for i in range(len(node.handlers)):
|
||||
a, b, h = node.handlers[i]
|
||||
if not a:
|
||||
# It's a plain "except:". Find the previous suite.
|
||||
if i > 0:
|
||||
prev = node.handlers[i-1][2]
|
||||
else:
|
||||
prev = node.body
|
||||
self.doPlainWordSuite(prev, h)
|
||||
else:
|
||||
self.doSuite(a, h)
|
||||
self.doElse(node.handlers[-1][2], node)
|
||||
|
||||
def visitTryFinally(self, node):
|
||||
self.doSuite(node, node.body)
|
||||
self.doPlainWordSuite(node.body, node.final)
|
||||
|
||||
def visitGlobal(self, node):
|
||||
# "global" statements don't execute like others (they don't call the
|
||||
# trace function), so don't record their line numbers.
|
||||
pass
|
||||
|
||||
the_coverage = None
|
||||
|
||||
class coverage:
|
||||
error = "coverage error"
|
||||
|
||||
# Name of the cache file (unless environment variable is set).
|
||||
cache_default = ".coverage"
|
||||
|
||||
# Environment variable naming the cache file.
|
||||
cache_env = "COVERAGE_FILE"
|
||||
|
||||
# A map from canonical Python source file name to a dictionary in
|
||||
# which there's an entry for each line number that has been
|
||||
# executed.
|
||||
cexecuted = {}
|
||||
|
||||
# Cache of results of calling the analysis2() method, so that you can
|
||||
# specify both -r and -a without doing double work.
|
||||
analysis_cache = {}
|
||||
|
||||
# Cache of results of calling the canonical_filename() method, to
|
||||
# avoid duplicating work.
|
||||
canonical_filename_cache = {}
|
||||
|
||||
def __init__(self):
|
||||
global the_coverage
|
||||
if the_coverage:
|
||||
raise self.error, "Only one coverage object allowed."
|
||||
self.usecache = 1
|
||||
self.cache = None
|
||||
self.exclude_re = ''
|
||||
|
||||
def help(self, error=None):
|
||||
if error:
|
||||
print error
|
||||
print
|
||||
print __doc__
|
||||
sys.exit(1)
|
||||
|
||||
def command_line(self):
|
||||
import getopt
|
||||
settings = {}
|
||||
optmap = {
|
||||
'-a': 'annotate',
|
||||
'-d:': 'directory=',
|
||||
'-e': 'erase',
|
||||
'-h': 'help',
|
||||
'-i': 'ignore-errors',
|
||||
'-m': 'show-missing',
|
||||
'-r': 'report',
|
||||
'-x': 'execute',
|
||||
}
|
||||
short_opts = string.join(map(lambda o: o[1:], optmap.keys()), '')
|
||||
long_opts = optmap.values()
|
||||
options, args = getopt.getopt(sys.argv[1:], short_opts,
|
||||
long_opts)
|
||||
for o, a in options:
|
||||
if optmap.has_key(o):
|
||||
settings[optmap[o]] = 1
|
||||
elif optmap.has_key(o + ':'):
|
||||
settings[optmap[o + ':']] = a
|
||||
elif o[2:] in long_opts:
|
||||
settings[o[2:]] = 1
|
||||
elif o[2:] + '=' in long_opts:
|
||||
settings[o[2:]] = a
|
||||
else:
|
||||
self.help("Unknown option: '%s'." % o)
|
||||
if settings.get('help'):
|
||||
self.help()
|
||||
for i in ['erase', 'execute']:
|
||||
for j in ['annotate', 'report']:
|
||||
if settings.get(i) and settings.get(j):
|
||||
self.help("You can't specify the '%s' and '%s' "
|
||||
"options at the same time." % (i, j))
|
||||
args_needed = (settings.get('execute')
|
||||
or settings.get('annotate')
|
||||
or settings.get('report'))
|
||||
action = settings.get('erase') or args_needed
|
||||
if not action:
|
||||
self.help("You must specify at least one of -e, -x, -r, or -a.")
|
||||
if not args_needed and args:
|
||||
self.help("Unexpected arguments %s." % args)
|
||||
|
||||
self.get_ready()
|
||||
self.exclude('#pragma[: ]+[nN][oO] [cC][oO][vV][eE][rR]')
|
||||
|
||||
if settings.get('erase'):
|
||||
self.erase()
|
||||
if settings.get('execute'):
|
||||
if not args:
|
||||
self.help("Nothing to do.")
|
||||
sys.argv = args
|
||||
self.start()
|
||||
import __main__
|
||||
sys.path[0] = os.path.dirname(sys.argv[0])
|
||||
execfile(sys.argv[0], __main__.__dict__)
|
||||
if not args:
|
||||
args = self.cexecuted.keys()
|
||||
ignore_errors = settings.get('ignore-errors')
|
||||
show_missing = settings.get('show-missing')
|
||||
directory = settings.get('directory=')
|
||||
if settings.get('report'):
|
||||
self.report(args, show_missing, ignore_errors)
|
||||
if settings.get('annotate'):
|
||||
self.annotate(args, directory, ignore_errors)
|
||||
|
||||
def use_cache(self, usecache):
|
||||
self.usecache = usecache
|
||||
|
||||
def get_ready(self):
|
||||
if self.usecache and not self.cache:
|
||||
self.cache = os.environ.get(self.cache_env, self.cache_default)
|
||||
self.restore()
|
||||
self.analysis_cache = {}
|
||||
|
||||
def start(self):
|
||||
self.get_ready()
|
||||
sys.settrace(t)
|
||||
|
||||
def stop(self):
|
||||
sys.settrace(None)
|
||||
|
||||
def erase(self):
|
||||
global c
|
||||
c = {}
|
||||
self.analysis_cache = {}
|
||||
self.cexecuted = {}
|
||||
if self.cache and os.path.exists(self.cache):
|
||||
os.remove(self.cache)
|
||||
self.exclude_re = ''
|
||||
|
||||
def exclude(self, re):
|
||||
if self.exclude_re:
|
||||
self.exclude_re += "|"
|
||||
self.exclude_re += "(" + re + ")"
|
||||
|
||||
# save(). Save coverage data to the coverage cache.
|
||||
|
||||
def save(self):
|
||||
if self.usecache and self.cache:
|
||||
self.canonicalize_filenames()
|
||||
cache = open(self.cache, 'wb')
|
||||
import marshal
|
||||
marshal.dump(self.cexecuted, cache)
|
||||
cache.close()
|
||||
|
||||
# restore(). Restore coverage data from the coverage cache (if it
|
||||
# exists).
|
||||
|
||||
def restore(self):
|
||||
global c
|
||||
c = {}
|
||||
self.cexecuted = {}
|
||||
assert self.usecache
|
||||
if not os.path.exists(self.cache):
|
||||
return
|
||||
try:
|
||||
cache = open(self.cache, 'rb')
|
||||
import marshal
|
||||
cexecuted = marshal.load(cache)
|
||||
cache.close()
|
||||
if isinstance(cexecuted, types.DictType):
|
||||
self.cexecuted = cexecuted
|
||||
except:
|
||||
pass
|
||||
|
||||
# canonical_filename(filename). Return a canonical filename for the
|
||||
# file (that is, an absolute path with no redundant components and
|
||||
# normalized case). See [GDR 2001-12-04b, 3.3].
|
||||
|
||||
def canonical_filename(self, filename):
|
||||
if not self.canonical_filename_cache.has_key(filename):
|
||||
f = filename
|
||||
if os.path.isabs(f) and not os.path.exists(f):
|
||||
f = os.path.basename(f)
|
||||
if not os.path.isabs(f):
|
||||
for path in [os.curdir] + sys.path:
|
||||
g = os.path.join(path, f)
|
||||
if os.path.exists(g):
|
||||
f = g
|
||||
break
|
||||
cf = os.path.normcase(os.path.abspath(f))
|
||||
self.canonical_filename_cache[filename] = cf
|
||||
return self.canonical_filename_cache[filename]
|
||||
|
||||
# canonicalize_filenames(). Copy results from "executed" to
|
||||
# "cexecuted", canonicalizing filenames on the way. Clear the
|
||||
# "executed" map.
|
||||
|
||||
def canonicalize_filenames(self):
|
||||
global c
|
||||
for filename, lineno in c.keys():
|
||||
f = self.canonical_filename(filename)
|
||||
if not self.cexecuted.has_key(f):
|
||||
self.cexecuted[f] = {}
|
||||
self.cexecuted[f][lineno] = 1
|
||||
c = {}
|
||||
|
||||
# morf_filename(morf). Return the filename for a module or file.
|
||||
|
||||
def morf_filename(self, morf):
|
||||
if isinstance(morf, types.ModuleType):
|
||||
if not hasattr(morf, '__file__'):
|
||||
raise self.error, "Module has no __file__ attribute."
|
||||
file = morf.__file__
|
||||
else:
|
||||
file = morf
|
||||
return self.canonical_filename(file)
|
||||
|
||||
# analyze_morf(morf). Analyze the module or filename passed as
|
||||
# the argument. If the source code can't be found, raise an error.
|
||||
# Otherwise, return a tuple of (1) the canonical filename of the
|
||||
# source code for the module, (2) a list of lines of statements
|
||||
# in the source code, and (3) a list of lines of excluded statements.
|
||||
|
||||
def analyze_morf(self, morf):
|
||||
if self.analysis_cache.has_key(morf):
|
||||
return self.analysis_cache[morf]
|
||||
filename = self.morf_filename(morf)
|
||||
ext = os.path.splitext(filename)[1]
|
||||
if ext == '.pyc':
|
||||
if not os.path.exists(filename[0:-1]):
|
||||
raise self.error, ("No source for compiled code '%s'."
|
||||
% filename)
|
||||
filename = filename[0:-1]
|
||||
elif ext != '.py':
|
||||
raise self.error, "File '%s' not Python source." % filename
|
||||
source = open(filename, 'r')
|
||||
lines, excluded_lines = self.find_executable_statements(
|
||||
source.read(), exclude=self.exclude_re
|
||||
)
|
||||
source.close()
|
||||
result = filename, lines, excluded_lines
|
||||
self.analysis_cache[morf] = result
|
||||
return result
|
||||
|
||||
def get_suite_spots(self, tree, spots):
|
||||
import symbol, token
|
||||
for i in range(1, len(tree)):
|
||||
if type(tree[i]) == type(()):
|
||||
if tree[i][0] == symbol.suite:
|
||||
# Found a suite, look back for the colon and keyword.
|
||||
lineno_colon = lineno_word = None
|
||||
for j in range(i-1, 0, -1):
|
||||
if tree[j][0] == token.COLON:
|
||||
lineno_colon = tree[j][2]
|
||||
elif tree[j][0] == token.NAME:
|
||||
if tree[j][1] == 'elif':
|
||||
# Find the line number of the first non-terminal
|
||||
# after the keyword.
|
||||
t = tree[j+1]
|
||||
while t and token.ISNONTERMINAL(t[0]):
|
||||
t = t[1]
|
||||
if t:
|
||||
lineno_word = t[2]
|
||||
else:
|
||||
lineno_word = tree[j][2]
|
||||
break
|
||||
elif tree[j][0] == symbol.except_clause:
|
||||
# "except" clauses look like:
|
||||
# ('except_clause', ('NAME', 'except', lineno), ...)
|
||||
if tree[j][1][0] == token.NAME:
|
||||
lineno_word = tree[j][1][2]
|
||||
break
|
||||
if lineno_colon and lineno_word:
|
||||
# Found colon and keyword, mark all the lines
|
||||
# between the two with the two line numbers.
|
||||
for l in range(lineno_word, lineno_colon+1):
|
||||
spots[l] = (lineno_word, lineno_colon)
|
||||
self.get_suite_spots(tree[i], spots)
|
||||
|
||||
def find_executable_statements(self, text, exclude=None):
|
||||
# Find lines which match an exclusion pattern.
|
||||
excluded = {}
|
||||
suite_spots = {}
|
||||
if exclude:
|
||||
reExclude = re.compile(exclude)
|
||||
lines = text.split('\n')
|
||||
for i in range(len(lines)):
|
||||
if reExclude.search(lines[i]):
|
||||
excluded[i+1] = 1
|
||||
|
||||
import parser
|
||||
tree = parser.suite(text+'\n\n').totuple(1)
|
||||
self.get_suite_spots(tree, suite_spots)
|
||||
|
||||
# Use the compiler module to parse the text and find the executable
|
||||
# statements. We add newlines to be impervious to final partial lines.
|
||||
statements = {}
|
||||
ast = compiler.parse(text+'\n\n')
|
||||
visitor = StatementFindingAstVisitor(statements, excluded, suite_spots)
|
||||
compiler.walk(ast, visitor, walker=visitor)
|
||||
|
||||
lines = statements.keys()
|
||||
lines.sort()
|
||||
excluded_lines = excluded.keys()
|
||||
excluded_lines.sort()
|
||||
return lines, excluded_lines
|
||||
|
||||
# format_lines(statements, lines). Format a list of line numbers
|
||||
# for printing by coalescing groups of lines as long as the lines
|
||||
# represent consecutive statements. This will coalesce even if
|
||||
# there are gaps between statements, so if statements =
|
||||
# [1,2,3,4,5,10,11,12,13,14] and lines = [1,2,5,10,11,13,14] then
|
||||
# format_lines will return "1-2, 5-11, 13-14".
|
||||
|
||||
def format_lines(self, statements, lines):
|
||||
pairs = []
|
||||
i = 0
|
||||
j = 0
|
||||
start = None
|
||||
pairs = []
|
||||
while i < len(statements) and j < len(lines):
|
||||
if statements[i] == lines[j]:
|
||||
if start == None:
|
||||
start = lines[j]
|
||||
end = lines[j]
|
||||
j = j + 1
|
||||
elif start:
|
||||
pairs.append((start, end))
|
||||
start = None
|
||||
i = i + 1
|
||||
if start:
|
||||
pairs.append((start, end))
|
||||
def stringify(pair):
|
||||
start, end = pair
|
||||
if start == end:
|
||||
return "%d" % start
|
||||
else:
|
||||
return "%d-%d" % (start, end)
|
||||
import string
|
||||
return string.join(map(stringify, pairs), ", ")
|
||||
|
||||
# Backward compatibility with version 1.
|
||||
def analysis(self, morf):
|
||||
f, s, _, m, mf = self.analysis2(morf)
|
||||
return f, s, m, mf
|
||||
|
||||
def analysis2(self, morf):
|
||||
filename, statements, excluded = self.analyze_morf(morf)
|
||||
self.canonicalize_filenames()
|
||||
if not self.cexecuted.has_key(filename):
|
||||
self.cexecuted[filename] = {}
|
||||
missing = []
|
||||
for line in statements:
|
||||
if not self.cexecuted[filename].has_key(line):
|
||||
missing.append(line)
|
||||
return (filename, statements, excluded, missing,
|
||||
self.format_lines(statements, missing))
|
||||
|
||||
def morf_name(self, morf):
|
||||
if isinstance(morf, types.ModuleType):
|
||||
return morf.__name__
|
||||
else:
|
||||
return os.path.splitext(os.path.basename(morf))[0]
|
||||
|
||||
def report(self, morfs, show_missing=1, ignore_errors=0):
|
||||
if not isinstance(morfs, types.ListType):
|
||||
morfs = [morfs]
|
||||
max_name = max([5,] + map(len, map(self.morf_name, morfs)))
|
||||
fmt_name = "%%- %ds " % max_name
|
||||
fmt_err = fmt_name + "%s: %s"
|
||||
header = fmt_name % "Name" + " Stmts Exec Cover"
|
||||
fmt_coverage = fmt_name + "% 6d % 6d % 5d%%"
|
||||
if show_missing:
|
||||
header = header + " Missing"
|
||||
fmt_coverage = fmt_coverage + " %s"
|
||||
print header
|
||||
print "-" * len(header)
|
||||
total_statements = 0
|
||||
total_executed = 0
|
||||
for morf in morfs:
|
||||
name = self.morf_name(morf)
|
||||
try:
|
||||
_, statements, _, missing, readable = self.analysis2(morf)
|
||||
n = len(statements)
|
||||
m = n - len(missing)
|
||||
if n > 0:
|
||||
pc = 100.0 * m / n
|
||||
else:
|
||||
pc = 100.0
|
||||
args = (name, n, m, pc)
|
||||
if show_missing:
|
||||
args = args + (readable,)
|
||||
print fmt_coverage % args
|
||||
total_statements = total_statements + n
|
||||
total_executed = total_executed + m
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except:
|
||||
if not ignore_errors:
|
||||
type, msg = sys.exc_info()[0:2]
|
||||
print fmt_err % (name, type, msg)
|
||||
if len(morfs) > 1:
|
||||
print "-" * len(header)
|
||||
if total_statements > 0:
|
||||
pc = 100.0 * total_executed / total_statements
|
||||
else:
|
||||
pc = 100.0
|
||||
args = ("TOTAL", total_statements, total_executed, pc)
|
||||
if show_missing:
|
||||
args = args + ("",)
|
||||
print fmt_coverage % args
|
||||
|
||||
# annotate(morfs, ignore_errors).
|
||||
|
||||
blank_re = re.compile("\\s*(#|$)")
|
||||
else_re = re.compile("\\s*else\\s*:\\s*(#|$)")
|
||||
|
||||
def annotate(self, morfs, directory=None, ignore_errors=0):
|
||||
for morf in morfs:
|
||||
try:
|
||||
filename, statements, excluded, missing, _ = self.analysis2(morf)
|
||||
self.annotate_file(filename, statements, excluded, missing, directory)
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except:
|
||||
if not ignore_errors:
|
||||
raise
|
||||
|
||||
def annotate_file(self, filename, statements, excluded, missing, directory=None):
|
||||
source = open(filename, 'r')
|
||||
if directory:
|
||||
dest_file = os.path.join(directory,
|
||||
os.path.basename(filename)
|
||||
+ ',cover')
|
||||
else:
|
||||
dest_file = filename + ',cover'
|
||||
dest = open(dest_file, 'w')
|
||||
lineno = 0
|
||||
i = 0
|
||||
j = 0
|
||||
covered = 1
|
||||
while 1:
|
||||
line = source.readline()
|
||||
if line == '':
|
||||
break
|
||||
lineno = lineno + 1
|
||||
while i < len(statements) and statements[i] < lineno:
|
||||
i = i + 1
|
||||
while j < len(missing) and missing[j] < lineno:
|
||||
j = j + 1
|
||||
if i < len(statements) and statements[i] == lineno:
|
||||
covered = j >= len(missing) or missing[j] > lineno
|
||||
if self.blank_re.match(line):
|
||||
dest.write(' ')
|
||||
elif self.else_re.match(line):
|
||||
# Special logic for lines containing only
|
||||
# 'else:'. See [GDR 2001-12-04b, 3.2].
|
||||
if i >= len(statements) and j >= len(missing):
|
||||
dest.write('! ')
|
||||
elif i >= len(statements) or j >= len(missing):
|
||||
dest.write('> ')
|
||||
elif statements[i] == missing[j]:
|
||||
dest.write('! ')
|
||||
else:
|
||||
dest.write('> ')
|
||||
elif lineno in excluded:
|
||||
dest.write('- ')
|
||||
elif covered:
|
||||
dest.write('> ')
|
||||
else:
|
||||
dest.write('! ')
|
||||
dest.write(line)
|
||||
source.close()
|
||||
dest.close()
|
||||
|
||||
# Singleton object.
|
||||
the_coverage = coverage()
|
||||
|
||||
# Module functions call methods in the singleton object.
|
||||
def use_cache(*args, **kw): return the_coverage.use_cache(*args, **kw)
|
||||
def start(*args, **kw): return the_coverage.start(*args, **kw)
|
||||
def stop(*args, **kw): return the_coverage.stop(*args, **kw)
|
||||
def erase(*args, **kw): return the_coverage.erase(*args, **kw)
|
||||
def exclude(*args, **kw): return the_coverage.exclude(*args, **kw)
|
||||
def analysis(*args, **kw): return the_coverage.analysis(*args, **kw)
|
||||
def analysis2(*args, **kw): return the_coverage.analysis2(*args, **kw)
|
||||
def report(*args, **kw): return the_coverage.report(*args, **kw)
|
||||
def annotate(*args, **kw): return the_coverage.annotate(*args, **kw)
|
||||
def annotate_file(*args, **kw): return the_coverage.annotate_file(*args, **kw)
|
||||
|
||||
# Save coverage data when Python exits. (The atexit module wasn't
|
||||
# introduced until Python 2.0, so use sys.exitfunc when it's not
|
||||
# available.)
|
||||
try:
|
||||
import atexit
|
||||
atexit.register(the_coverage.save)
|
||||
except ImportError:
|
||||
sys.exitfunc = the_coverage.save
|
||||
|
||||
# Command-line interface.
|
||||
if __name__ == '__main__':
|
||||
the_coverage.command_line()
|
||||
|
||||
|
||||
# A. REFERENCES
|
||||
#
|
||||
# [GDR 2001-12-04a] "Statement coverage for Python"; Gareth Rees;
|
||||
# Ravenbrook Limited; 2001-12-04;
|
||||
# <http://www.garethrees.org/2001/12/04/python-coverage/>.
|
||||
#
|
||||
# [GDR 2001-12-04b] "Statement coverage for Python: design and
|
||||
# analysis"; Gareth Rees; Ravenbrook Limited; 2001-12-04;
|
||||
# <http://www.garethrees.org/2001/12/04/python-coverage/design.html>.
|
||||
#
|
||||
# [van Rossum 2001-07-20a] "Python Reference Manual (releae 2.1.1)";
|
||||
# Guide van Rossum; 2001-07-20;
|
||||
# <http://www.python.org/doc/2.1.1/ref/ref.html>.
|
||||
#
|
||||
# [van Rossum 2001-07-20b] "Python Library Reference"; Guido van Rossum;
|
||||
# 2001-07-20; <http://www.python.org/doc/2.1.1/lib/lib.html>.
|
||||
#
|
||||
#
|
||||
# B. DOCUMENT HISTORY
|
||||
#
|
||||
# 2001-12-04 GDR Created.
|
||||
#
|
||||
# 2001-12-06 GDR Added command-line interface and source code
|
||||
# annotation.
|
||||
#
|
||||
# 2001-12-09 GDR Moved design and interface to separate documents.
|
||||
#
|
||||
# 2001-12-10 GDR Open cache file as binary on Windows. Allow
|
||||
# simultaneous -e and -x, or -a and -r.
|
||||
#
|
||||
# 2001-12-12 GDR Added command-line help. Cache analysis so that it
|
||||
# only needs to be done once when you specify -a and -r.
|
||||
#
|
||||
# 2001-12-13 GDR Improved speed while recording. Portable between
|
||||
# Python 1.5.2 and 2.1.1.
|
||||
#
|
||||
# 2002-01-03 GDR Module-level functions work correctly.
|
||||
#
|
||||
# 2002-01-07 GDR Update sys.path when running a file with the -x option,
|
||||
# so that it matches the value the program would get if it were run on
|
||||
# its own.
|
||||
#
|
||||
# 2004-12-12 NMB Significant code changes.
|
||||
# - Finding executable statements has been rewritten so that docstrings and
|
||||
# other quirks of Python execution aren't mistakenly identified as missing
|
||||
# lines.
|
||||
# - Lines can be excluded from consideration, even entire suites of lines.
|
||||
# - The filesystem cache of covered lines can be disabled programmatically.
|
||||
# - Modernized the code.
|
||||
#
|
||||
# 2004-12-14 NMB Minor tweaks. Return 'analysis' to its original behavior
|
||||
# and add 'analysis2'. Add a global for 'annotate', and factor it, adding
|
||||
# 'annotate_file'.
|
||||
#
|
||||
# 2004-12-31 NMB Allow for keyword arguments in the module global functions.
|
||||
#
|
||||
# C. COPYRIGHT AND LICENCE
|
||||
#
|
||||
# Copyright 2001 Gareth Rees. All rights reserved.
|
||||
# Copyright 2004 Ned Batchelder. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
# DAMAGE.
|
||||
#
|
||||
# $Id: coverage.py 5 2004-12-14 12:08:23Z ned $
|
||||
+197
-7
@@ -40,6 +40,29 @@ init:
|
||||
# clauses and statements.
|
||||
$ fade = Fade(.5, 0, .5) # Fade to black and back.
|
||||
$ dissolve = Dissolve(0.5)
|
||||
|
||||
$ wiperight = CropMove(1.0, "wiperight")
|
||||
$ wipeleft = CropMove(1.0, "wipeleft")
|
||||
$ wipeup = CropMove(1.0, "wipeup")
|
||||
$ wipedown = CropMove(1.0, "wipedown")
|
||||
|
||||
$ slideright = CropMove(1.0, "slideright")
|
||||
$ slideleft = CropMove(1.0, "slideleft")
|
||||
$ slideup = CropMove(1.0, "slideup")
|
||||
$ slidedown = CropMove(1.0, "slidedown")
|
||||
|
||||
$ slideawayright = CropMove(1.0, "slideawayright")
|
||||
$ slideawayleft = CropMove(1.0, "slideawayleft")
|
||||
$ slideawayup = CropMove(1.0, "slideawayup")
|
||||
$ slideawaydown = CropMove(1.0, "slideawaydown")
|
||||
|
||||
$ irisout = CropMove(1.0, "irisout")
|
||||
$ irisin = CropMove(1.0, "irisin")
|
||||
|
||||
# Select the transitions that are used when entering and exiting
|
||||
# the game menu.
|
||||
$ library.enter_transition = dissolve
|
||||
$ library.exit_transition = dissolve
|
||||
|
||||
# Now, we declare the images that are used in the program.
|
||||
|
||||
@@ -162,6 +185,10 @@ label choices:
|
||||
call writing from _call_writing_1
|
||||
jump choices
|
||||
|
||||
"What's new with Ren'Py?":
|
||||
call whatsnew from _call_whatsnew_1
|
||||
jump choices
|
||||
|
||||
# This choice has a condition associated with it. It is only
|
||||
# displayed if the condition is true (in this case, if we have
|
||||
# selected at least one other choice has been chosen.)
|
||||
@@ -448,7 +475,7 @@ label find_out_more:
|
||||
e "If you have questions, the best place to ask them is the Ren'Py
|
||||
forum of the Lemmasoft forums."
|
||||
|
||||
e "Just go to http://www.lemmasoft.net/forums/, and click on
|
||||
e "Just go to http://lemmasoft.renai.us/forums/, and click on
|
||||
Ren'Py."
|
||||
|
||||
e "We thank Blue Lemma for hosting our forum."
|
||||
@@ -736,10 +763,173 @@ init:
|
||||
plan[editing] = value
|
||||
editing = None
|
||||
|
||||
|
||||
|
||||
return plan
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
init:
|
||||
image movie = Movie()
|
||||
|
||||
python:
|
||||
style.create('odd_window', 'say_window')
|
||||
style.odd_window.left_margin = 50
|
||||
style.odd_window.right_margin = 150
|
||||
style.odd_window.bottom_margin = 25
|
||||
|
||||
eodd = Character('Eileen', color=(200, 255, 200, 255), window_style='odd_window')
|
||||
|
||||
|
||||
label whatsnew:
|
||||
|
||||
show washington
|
||||
show eileen happy
|
||||
|
||||
e "I can give you a demonstration of some of the new features in
|
||||
Ren'Py, but you'll have to tell me what version you want to
|
||||
start with."
|
||||
|
||||
menu:
|
||||
"I'd like to start with 4.5.":
|
||||
jump whatsnew45
|
||||
|
||||
"I'd like to start with 4.6.":
|
||||
jump whatsnew46
|
||||
|
||||
label whatsnew45:
|
||||
|
||||
show washington
|
||||
show eileen happy
|
||||
|
||||
e "While most of the improvements in Ren'Py 4.5 were behind the scenes,
|
||||
we can give you a demonstration of one of the new features."
|
||||
|
||||
e "There is now a new transition, CropMove, that can be used to
|
||||
provide a whole range of transition effects."
|
||||
|
||||
hide eileen with dissolve
|
||||
|
||||
e "I'll stand offscreen, so you can see some of its modes. I'll read
|
||||
out the mode name after each transiton."
|
||||
|
||||
scene whitehouse with wiperight
|
||||
|
||||
e "We first have wiperight..."
|
||||
|
||||
scene washington with wipeleft
|
||||
|
||||
e "...followed by wipeleft... "
|
||||
|
||||
scene whitehouse with wipeup
|
||||
|
||||
e "...wipeup..."
|
||||
|
||||
scene washington with wipedown
|
||||
|
||||
e "...and wipedown."
|
||||
|
||||
e "Next, the slides."
|
||||
|
||||
scene whitehouse with slideright
|
||||
|
||||
e "Slideright..."
|
||||
|
||||
scene washington with slideleft
|
||||
|
||||
e "...slideleft..."
|
||||
|
||||
scene whitehouse with slideup
|
||||
|
||||
e "...slideup..."
|
||||
|
||||
scene washington with slidedown
|
||||
|
||||
e "and slidedown."
|
||||
|
||||
e "We also have a couple of transitions that use a rectangular iris."
|
||||
|
||||
scene whitehouse with irisout
|
||||
|
||||
e "There's irisout..."
|
||||
|
||||
with None
|
||||
scene washington
|
||||
show eileen happy
|
||||
with irisin
|
||||
|
||||
e "... and irisin."
|
||||
|
||||
e "There are other transitions, such as various forms of
|
||||
slideaway. And if you can't find the transition for you, you
|
||||
can write a custom one."
|
||||
|
||||
e "It's enough to make you feel a bit dizzy."
|
||||
|
||||
e "Ren'Py 4.5 also includes the ability to show MPEG-1 movies as
|
||||
cutscenes or even backgrounds."
|
||||
|
||||
label ike:
|
||||
|
||||
if renpy.exists('Eisenhow1952.mpg'):
|
||||
|
||||
e "Since you downloaded the Eisenhower commercial, I can show
|
||||
it to you as a cutscene."
|
||||
|
||||
e "You can click to continue if it gets on your nerves too
|
||||
much."
|
||||
|
||||
$ renpy.movie_cutscene('Eisenhow1952.mpg', 63.0)
|
||||
|
||||
hide eileen
|
||||
show movie at Position(xpos=420, ypos=25, xanchor='left', yanchor='top')
|
||||
show eileen happy
|
||||
|
||||
$ renpy.movie_start_displayable('Eisenhow1952.mpg', (352, 240))
|
||||
|
||||
e "Ren'Py can even overlay rendered images on top of a movie,
|
||||
although that's more taxing for your CPU."
|
||||
|
||||
e "It's like I'm some sort of newscaster or something."
|
||||
|
||||
$ renpy.movie_stop()
|
||||
hide movie
|
||||
|
||||
else:
|
||||
|
||||
e "You haven't downloaded the Eisenhower commercial, so we
|
||||
can't demonstrate it."
|
||||
|
||||
label whatsnew46:
|
||||
|
||||
eodd "As of 4.6, we now support separate padding and margin for the
|
||||
left, right, top, and bottom sides of a window."
|
||||
|
||||
eodd "This means that a game can have oddly shaped windows without
|
||||
having to go beyond the style system."
|
||||
|
||||
e "We also introduced a new layer system, and the ability to have
|
||||
transitions affect only one layer."
|
||||
|
||||
e "Because of this we can do things like slide away a window..."
|
||||
|
||||
$ renpy.transition(slideawayup, 'transient')
|
||||
$ renpy.pause(1.5)
|
||||
$ renpy.transition(slidedown, 'transient')
|
||||
|
||||
e "... and slide it back in again."
|
||||
|
||||
e "Also new in this release is the ability to specify transitions
|
||||
that occur when you enter and exit the game menu."
|
||||
|
||||
e "Right click to see them, if you want."
|
||||
|
||||
e "A few more obscure features involving things like overlays and
|
||||
activated widgets round out the 4.6 release."
|
||||
|
||||
label whatsnewend:
|
||||
|
||||
e "Anyway, now that you've heard about some of the new features, is there anything
|
||||
else I can help you with?"
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+11
-2
@@ -43,12 +43,18 @@ def copy_tree(source, dest, should_copy=lambda fn : True, license=""):
|
||||
if "/CVS" in dirpath:
|
||||
continue
|
||||
|
||||
if "/.svn" in dirpath:
|
||||
continue
|
||||
|
||||
reldir = dirpath[len(source):]
|
||||
dstrel = dest + "/" + reldir
|
||||
|
||||
for i in dirnames:
|
||||
if i == "CVS":
|
||||
continue
|
||||
|
||||
if i == ".svn":
|
||||
continue
|
||||
|
||||
os.mkdir(dstrel + "/" + i)
|
||||
|
||||
@@ -99,13 +105,16 @@ def main():
|
||||
copy_tree("doc", target + "/doc",
|
||||
should_copy = lambda fn : fn in doc_files)
|
||||
|
||||
# Copy the game
|
||||
# Copy the game
|
||||
copy_tree(gamedir, target + "/game",
|
||||
should_copy = lambda fn : not fn.startswith(".") and not fn.endswith("~"))
|
||||
should_copy = lambda fn : not fn.startswith(".") and not fn.endswith("~") and not fn.endswith(".mpg"))
|
||||
|
||||
copy_tree("common", target + "/common",
|
||||
should_copy = lambda fn : not fn.startswith(".") and not fn.endswith("~"))
|
||||
|
||||
copy_tree("extras", target + "/extras",
|
||||
should_copy = lambda fn : not fn.startswith(".") and not fn.endswith("~"))
|
||||
|
||||
def cp(x, license=""):
|
||||
copy_file(x, target + "/" + x)
|
||||
|
||||
|
||||
@@ -84,6 +84,12 @@ def function(m):
|
||||
renpy.store.renpy = renpy.exports
|
||||
|
||||
func = eval(name, store)
|
||||
|
||||
if isinstance(func, renpy.curry.Curry):
|
||||
if func.callable == renpy.curry.Curry:
|
||||
func = func.args[0]
|
||||
else:
|
||||
func = func.callable
|
||||
|
||||
doc = func.__doc__
|
||||
|
||||
|
||||
+275
-124
@@ -540,12 +540,20 @@ menu what_to_do:
|
||||
<p>
|
||||
Without the ability to display images to the user, a visual
|
||||
novel would be a text adventure. Ren'Py controls image display
|
||||
by using a <def>scene list</def>, a list of things to be
|
||||
displayed to the user. Every time an interaction starts (that
|
||||
is, a line of dialogue or a menu is displayed), the things in
|
||||
the scene list are drawn to the screen, with the first being in
|
||||
by using a <def>layers</def>, each comprising a list of things to be
|
||||
displayed to the user. By default, there are three layers. The
|
||||
master layer is manipulated using the various image display
|
||||
statement defined in this section. The transient layer is used to
|
||||
display ui widgets, like windows containing dialogue and
|
||||
menus. Finally, an overlay layer exists to allow things to be
|
||||
overlaid on top of the screen.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Every time an interaction starts (for example, a line of dialogue or a menu is displayed), the things in
|
||||
the each of the layers is drawn to the screen, with the first being in
|
||||
the back and the last being in the front. A number of statements
|
||||
manipulate the scene list. Before we can explain them, however,
|
||||
manipulate the contents of the master layer. Before we can explain them, however,
|
||||
we should first define a few terms.
|
||||
</p>
|
||||
|
||||
@@ -607,16 +615,16 @@ init:
|
||||
<p>
|
||||
|
||||
The next display statement is the show statement, which takes an
|
||||
image specifier and displays it on the screen. If an image with
|
||||
the same tag as the image given in the spec already exists on
|
||||
the scene list, it is replaced with the newly displayed
|
||||
image specifier and displays it on the master layer. If an image with
|
||||
the same tag as the image given in the spec already exists in
|
||||
the master layer, it is replaced with the newly displayed
|
||||
Displayable. Otherwise, the new one is added to the end of the
|
||||
scene list (that is, closest to the user).
|
||||
master layer (that is, closest to the user).
|
||||
|
||||
</p><p>
|
||||
|
||||
If an at list is present, the image is transformed with the at
|
||||
list before being added to the display list.
|
||||
list before being added to the master layer.
|
||||
|
||||
</p><p>
|
||||
|
||||
@@ -630,7 +638,7 @@ init:
|
||||
<rule>scene_statement -> "scene" ( image_spec )?</rule>
|
||||
|
||||
<p>
|
||||
The scene statement first clears the scene_list. If the optional
|
||||
The scene statement first clears the master layer. If the optional
|
||||
image_spec is present, it is shown as if it was shown with the
|
||||
show statement. The best use for the image_spec on a scene
|
||||
command is show a background for the scene.
|
||||
@@ -665,8 +673,8 @@ e "But sometimes, I can get upset for no good reason."
|
||||
<rule>hide_statement -> "hide" image_spec</rule>
|
||||
|
||||
<p>
|
||||
The hide statement is used to remove an image from the scene
|
||||
list. The image spec is parsed for a tag, and any image matching
|
||||
The hide statement is used to remove an image from the master
|
||||
layer. The image spec is parsed for a tag, and any image matching
|
||||
that tag is remove from the scene. The at list is ignored (but
|
||||
should be valid or omitted), and transitions associated with the
|
||||
image spec are run.
|
||||
@@ -814,8 +822,8 @@ show eileen happy at left
|
||||
<!-- func renpy.ParameterizedText -->
|
||||
|
||||
<p>
|
||||
This is used to impelement the text image. The user may also want
|
||||
to instatiate their own ParameterizedText object (in an init
|
||||
This is used to implement the text image. The user may also want
|
||||
to instantiate their own ParameterizedText object (in an init
|
||||
block) if they want to have more than one bit of text on the
|
||||
screen at once, or if they want to change the style (and therefore
|
||||
the position) of that text.
|
||||
@@ -914,22 +922,22 @@ show ball at Move((0.0, 0.0), (1.0, 1.0), 10.0,
|
||||
<h3>Transitions</h3>
|
||||
|
||||
<p>
|
||||
By default, Ren'Py displays each scene list by replacing the old
|
||||
scene list with a new one. This is appropriate in general (such as
|
||||
for emotion changes), but it may be boring for large scene
|
||||
changes, such as a change in scene or a character entering or
|
||||
By default, Ren'Py displays each scene by replacing the old
|
||||
scene with a new one. This is appropriate in general (such as
|
||||
for emotion changes), but it may be boring for large
|
||||
changes, such as a change in location or a character entering or
|
||||
leaving the scene. Ren'Py supports transitions that control how
|
||||
changes to the scene lists are exposed to the user.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Transitions occur between the last scene list that was shown to
|
||||
the user, and the current scene list that has been updated using
|
||||
Transitions occur between the last scene that was shown to
|
||||
the user, and the current scene that has been updated using
|
||||
the scene, show, or hide statements. A transition takes both lists
|
||||
as input, and is responsible for displaying the transition between
|
||||
them to the user. Each transition runs for a given amount of time,
|
||||
but may be dismissed early by the user. Once a transition is
|
||||
shown, the scene list is considered shown for the purposes of
|
||||
shown, the scene is considered shown for the purposes of
|
||||
future transitions.
|
||||
</p>
|
||||
|
||||
@@ -1068,57 +1076,23 @@ e "How are you doing?" with dissolve
|
||||
</dl>
|
||||
|
||||
<p>
|
||||
Functions that return things that are useful as arguments to with
|
||||
statements or clauses are:
|
||||
The following are functions that return things useful as
|
||||
transitions. The user should not supply the new_widget or old_widget
|
||||
parameters, as these are supplied by Ren'Py when a transition
|
||||
begins.
|
||||
</p>
|
||||
|
||||
<function name="Fade" sig="(in_time, hold_time, out_time, color=(0, 0, 0))">
|
||||
|
||||
<p>
|
||||
This returns an object that can be used as an argument to a with
|
||||
statement to fade the old scene into a solid color, waits for a
|
||||
given amount of time, and then fades from the solid color into
|
||||
the new scene.
|
||||
</p>
|
||||
<!-- func Fade -->
|
||||
|
||||
<p>
|
||||
<param>in_time</param> - The amount of time that will be spent
|
||||
fading from the old scene to the solid color. A float, given as
|
||||
seconds.
|
||||
</p>
|
||||
<!-- func Dissolve -->
|
||||
|
||||
<p>
|
||||
<param>hold_time</param> - The amount of time that will be spent
|
||||
displaying the solid color. A float, given as seconds.
|
||||
</p>
|
||||
<!-- func CropMove -->
|
||||
|
||||
<p>
|
||||
<param>out_time</param> - The amount of time that will be spent
|
||||
fading from the solid color to the new scene. A float, given as
|
||||
seconds.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<param>color</param> - The solid color that will be faded
|
||||
to. This is an RGB triple, where each element is in the range 0
|
||||
to 255. This defaults to black.
|
||||
</p>
|
||||
|
||||
</function>
|
||||
|
||||
<function name="Dissolve" sig="(delay)">
|
||||
<p>
|
||||
This dissolves from the old scene to the new scene, by
|
||||
overlaying the new scene on top of the old scene and varying its
|
||||
alpha from 0 to 255.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<param>delay</param> - The amount of time the dissolve will
|
||||
take.
|
||||
</p>
|
||||
|
||||
</function>
|
||||
<p>
|
||||
Some transitions can also be applied to specific layers, using the
|
||||
renpy.transition function (documented below). Only transitions that
|
||||
are not completely opaque can be used in this way.
|
||||
</p>
|
||||
|
||||
<h3>Control Statements</h3>
|
||||
|
||||
@@ -1740,6 +1714,8 @@ e "Pleased to meet you, %(name)s."
|
||||
|
||||
<!-- func renpy.transition -->
|
||||
|
||||
<!-- func renpy.restart_interaction -->
|
||||
|
||||
<!-- func renpy.full_restart -->
|
||||
|
||||
<!-- func renpy.quit -->
|
||||
@@ -1748,6 +1724,8 @@ e "Pleased to meet you, %(name)s."
|
||||
|
||||
<!-- func renpy.windows -->
|
||||
|
||||
<!-- func renpy.exists -->
|
||||
|
||||
<!-- func renpy.clear_game_runtime -->
|
||||
|
||||
<!-- func renpy.get_game_runtime -->
|
||||
@@ -1778,10 +1756,14 @@ e "Pleased to meet you, %(name)s."
|
||||
|
||||
<p>
|
||||
Overlays are set up by adding to the config.overlay_functions list
|
||||
a python function which, when called, returns a list of
|
||||
Displayables that are added to the overlay. These functions are
|
||||
a python function which, when called, uses the ui functions to add
|
||||
widgets to the screen. By default, such widgets are added to the
|
||||
'overlay' layer, but a call to ui.layer() can change the layer
|
||||
to any of the layers listed in library.overlay_layers. These functions are
|
||||
called for each interaction, which allows the overlay to change to
|
||||
reflect the status of game variables.
|
||||
reflect the status of game variables. If a variable affecting the
|
||||
overlay changes during an interaction, renpy.restart_interaction()
|
||||
should be called to regenerate the overlay.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -1799,11 +1781,9 @@ init:
|
||||
python hide:
|
||||
def date_overlay():
|
||||
if date:
|
||||
return [ Image(date + ".png",
|
||||
xpos=1.0, xanchor="right",
|
||||
ypos=0.0, yanchor="top") ]
|
||||
else:
|
||||
return [ ]
|
||||
ui.image(date + ".png",
|
||||
xpos=1.0, xanchor="right",
|
||||
ypos=0.0, yanchor="top") ]
|
||||
|
||||
config.overlay_functions.append(date_overlay)
|
||||
</example>
|
||||
@@ -1817,7 +1797,7 @@ init:
|
||||
</p>
|
||||
|
||||
|
||||
<h3>Sound and Music Functions</h3>
|
||||
<h3>Multimedia: Sound, Music, and Movies</h3>
|
||||
|
||||
<h4>Sound</h4>
|
||||
|
||||
@@ -1895,6 +1875,79 @@ e "Just kidding."
|
||||
of old music when a new track is started.
|
||||
</p>
|
||||
|
||||
<h4>Movies</h4>
|
||||
|
||||
<p>
|
||||
As of version 4.5, Ren'Py is capable of playing MPEG-1 movies. This
|
||||
allows you to include video as part of your game. There are two ways
|
||||
that this can be done. Displaying video in fullscreen mode allows
|
||||
video to be scaled up to very large sizes, while displaying video in a
|
||||
displayable allows it to be blended with images and text.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Displaying a video fullscreen allows it to take advantage of hardware
|
||||
decode and scaling support. The downside of fullscreen mode is that
|
||||
nothing else can be displayed while the movie is being displayed.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The easiest way to display a movie fullscreen is to display it using
|
||||
the renpy.cutscene function. This function displays a movie for a
|
||||
specified length of time. When that time has elapsed, or when the user
|
||||
clicks to dismiss the movie, the movie ends and the function returns.
|
||||
</p>
|
||||
|
||||
<!-- func renpy.movie_cutscene -->
|
||||
|
||||
<p>
|
||||
Displaying a movie in a displayable is more difficult, but it allows
|
||||
the movie to be mixed with rendered text and graphics. A downside of
|
||||
this method is that the screen needs to be re-rendered with each
|
||||
frame, a process that may be unacceptibly slow when the screen is
|
||||
large. We don't recommend this when the game window is larger that
|
||||
640x480.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To display a movie in a displayable, we must start the movie and show
|
||||
a Movie displayable on the screen. The movie is then displayed on the
|
||||
screen along with any other widget. When we no longer want to show it,
|
||||
we need to stop the decoding of the movie and hide the displayable.
|
||||
</p>
|
||||
|
||||
<!-- func Movie -->
|
||||
|
||||
<!-- func renpy.movie_start_displayable -->
|
||||
|
||||
<!-- func renpy.movie_stop -->
|
||||
|
||||
<example>
|
||||
init:
|
||||
image movie = Movie
|
||||
|
||||
$ renpy.movie_start_displayable("bg.mpg", (640, 480), -1)
|
||||
scene movie
|
||||
show eileen happy
|
||||
|
||||
e "I'm standing in front of a movie."
|
||||
|
||||
e "Isn't that keen?"
|
||||
|
||||
hide movie
|
||||
$ renpy.movie_stop()
|
||||
</example>
|
||||
|
||||
<p>
|
||||
Whenever a movie is playing, it is expected to provide all sound that
|
||||
the user hears. Ren'Py disables sound hardware for the duration of the
|
||||
movie.
|
||||
</p>
|
||||
|
||||
<example>
|
||||
$ renpy.cutscene("On_Your_Mark.mpg", 394)
|
||||
</example>
|
||||
|
||||
<h3>Configuration Variables</h3>
|
||||
|
||||
<p>
|
||||
@@ -2072,6 +2125,31 @@ load scripts, as scripts will be loaded before it can be set.
|
||||
screen.
|
||||
</var>
|
||||
|
||||
<var name="config.layers" value="[ 'master', 'transient', 'overlay' ]">
|
||||
This variable gives a list of all of the layers that Ren'Py knows
|
||||
about, in the order that they will be displayed to the screen. (The
|
||||
lowest layer is the first entry in the list.) Ren'Py uses the layers
|
||||
"master", "transient", and "overlay" internally, so they should
|
||||
always be in this list.
|
||||
</var>
|
||||
|
||||
<var name="config.transient_layers" value="[ 'transient' ]" >
|
||||
This variable gives a list of all of the transient layers. Transient
|
||||
layers are layers that are cleared after each interaction. "transient"
|
||||
should always be in this list.
|
||||
</var>
|
||||
|
||||
<var name="config.overlay_layers" value="[ 'overlay' ]" >
|
||||
This is a list of all of the overlay layers. Overlay layers are
|
||||
cleared before the overlay functions are called. "overlay" should
|
||||
always be in this list.
|
||||
</var>
|
||||
|
||||
<var name="config.overlay_during_wait" value="True" >
|
||||
True if we want overlays to be shown during wait statements, or False
|
||||
if we'd prefer that they be hidden during the wait statements.
|
||||
</var>
|
||||
|
||||
<var name="library.file_page_length" value="10">
|
||||
This is the number of save slots that are shown in the picker
|
||||
that's used by the load and save portions of the game menu. This
|
||||
@@ -2100,14 +2178,14 @@ the file picker.
|
||||
thumbnail is shown to the user.
|
||||
</var>
|
||||
|
||||
<var name="library.main_menu" value='[ ( "Start Game", "start" ), ("Continue Game", "_continue"), ("Quit Game", "_quit") ]'>
|
||||
<var name="library.main_menu" value='[ ( "Start Game", "start" ), ("Continue Game", "_continue"), ("Preferences", "_preferences"), ("Quit Game", "_quit") ]'>
|
||||
This is used to give the main menu that is shown to the user
|
||||
when the game first starts. It is a list of tuples, where the
|
||||
first element of each tuple is the title of the menu button, and
|
||||
the second element is a label that we jump to when that button
|
||||
is selected. Two useful labels to use here are "_continue",
|
||||
which brings up the game menu in load mode, and "_quit", which
|
||||
terminates Ren'Py.
|
||||
is selected. Some useful labels to use here are "_continue",
|
||||
which brings up the game menu in load mode, "_preferences", which brings up the preferences screen,
|
||||
and "_quit", which terminates Ren'Py.
|
||||
</var>
|
||||
|
||||
<var name="library.has_music" value="True">
|
||||
@@ -2145,6 +2223,16 @@ the file picker.
|
||||
If True, the library will display a skip indicator when skipping
|
||||
through the script.
|
||||
</var>
|
||||
|
||||
<var name="library.enter_transition" value="None">
|
||||
If not None, this variable should give a transition that will be
|
||||
used when entering the game menu.
|
||||
</var>
|
||||
|
||||
<var name="library.exit_transition" value="None">
|
||||
If not None, this variable should give a transition that will be
|
||||
performed when exiting the game menu.
|
||||
</var>
|
||||
|
||||
|
||||
</dl>
|
||||
@@ -2298,15 +2386,68 @@ indent to quoted dialogue.
|
||||
128))
|
||||
</prop>
|
||||
|
||||
<prop name="left_margin">
|
||||
The amount of transparent space left to the left of this window. If a
|
||||
floating point number, it is scaled to the available width.
|
||||
</prop>
|
||||
|
||||
<prop name="right_margin">
|
||||
The amount of transparent space left to the right of this window. If a
|
||||
floating point number, it is scaled to the available width.
|
||||
</prop>
|
||||
|
||||
<prop name="top_margin">
|
||||
The amount of transparent space left to the top of this window. If a
|
||||
floating point number, it is scaled to the available height.
|
||||
</prop>
|
||||
|
||||
<prop name="bottom_margin">
|
||||
The amount of transparent space left to the bottom of this window. If a
|
||||
floating point number, it is scaled to the available height.
|
||||
</prop>
|
||||
|
||||
<prop name="xmargin">
|
||||
The amount of transparent space that is left around this window in
|
||||
the x direction, in pixels.
|
||||
This is a convenient (and backwards compatible) way of setting
|
||||
left_margin and right_margin to the same value.
|
||||
</prop>
|
||||
|
||||
<prop name="ymargin">
|
||||
This is a convenient (and backwards compatible) way of setting
|
||||
top_margin and bottom_margin to the same value.
|
||||
</prop>
|
||||
|
||||
<prop name="left_padding">
|
||||
The amount of space left between the edge of the border and the
|
||||
left side of the contents of the window. If a float, it is scaled to the
|
||||
width of the window.
|
||||
</prop>
|
||||
|
||||
<prop name="right_padding">
|
||||
The amount of space left between the edge of the border and the
|
||||
right side of the contents of the window. If a float, it is scaled to the
|
||||
width of the window.
|
||||
</prop>
|
||||
|
||||
<prop name="top_padding">
|
||||
The amount of space left between the edge of the border and the
|
||||
top side of the contents of the window. If a float, it is scaled to the
|
||||
height of the window.
|
||||
</prop>
|
||||
|
||||
<prop name="bottom_padding">
|
||||
The amount of space left between the edge of the border and the
|
||||
bottom side of the contents of the window. If a float, it is scaled to the
|
||||
height of the window.
|
||||
</prop>
|
||||
|
||||
<prop name="xpadding">
|
||||
The amount of space left between the edge of the background of the
|
||||
window, and the edge of the content contained within this window,
|
||||
in the x direction, in pixels.
|
||||
A convenient (and backwards compatible) way of setting
|
||||
left_padding and right_padding to the same value.
|
||||
</prop>
|
||||
|
||||
<prop name="ypadding">
|
||||
A convenient (and backwards compatible) way of setting
|
||||
top_padding and bottom_padding to the same value.
|
||||
</prop>
|
||||
|
||||
<prop name="xfill">
|
||||
@@ -2314,32 +2455,23 @@ indent to quoted dialogue.
|
||||
x direction. If False, it will shrink to fit its contents.
|
||||
</prop>
|
||||
|
||||
<prop name="xminimum">
|
||||
The minimum size of this window in the x direction, including
|
||||
margins and padding. If the window would be smaller than this, it
|
||||
is grown to be at least this size.
|
||||
</prop>
|
||||
|
||||
<prop name="ymargin">
|
||||
The amount of transparent space that is left around this window in
|
||||
the y direction, in pixels.
|
||||
</prop>
|
||||
|
||||
<prop name="ypadding">
|
||||
The amount of space left between the edge of the background of the
|
||||
window, and the edge of the content contained within this window,
|
||||
in the y direction, in pixels.
|
||||
</prop>
|
||||
|
||||
<prop name="yfill">
|
||||
If True, the window will expand to fill all available space in the
|
||||
y direction. If False, it will shrink to fit its contents.
|
||||
</prop>
|
||||
|
||||
<prop name="xminimum">
|
||||
The minimum size of this window in the x direction, including
|
||||
margins and padding. If the window would be smaller than this, it
|
||||
is grown to be at least this size. If a floating point number, it is
|
||||
scaled to the available width.
|
||||
</prop>
|
||||
|
||||
<prop name="yminimum">
|
||||
The minimum size of this window in the y direction, including
|
||||
margins and padding. If the window would be smaller than this, it
|
||||
is grown to be at least this size.
|
||||
is grown to be at least this size. If a floating point number, it is
|
||||
scaled to the available height.
|
||||
</prop>
|
||||
|
||||
|
||||
@@ -2452,10 +2584,11 @@ The size of the left gutter of a clickable bar, in pixels.
|
||||
|
||||
<p>
|
||||
In Ren'Py, buttons and their contents support the idea of hovering:
|
||||
using different sets of properties when the mouse is over or not
|
||||
over the widget. If the mouse is over the widget, then that widget
|
||||
using different sets of properties for different states of the
|
||||
widget. If the widget has been selected, it is considered to be
|
||||
activated. Otherwise, if the mouse is over the widget, then that widget
|
||||
is hovered, else it is idle. On these widgets, Ren'Py will look up
|
||||
a property prefixed with "hover_" or "idle_" (as appropriate) in a
|
||||
a property prefixed with "activate_", "hover_", "idle_", (as appropriate) in a
|
||||
style, before looking up an unprefixed property. If neither form
|
||||
is found on a style, the process is repeated again on the parent
|
||||
style. (The search pattern for background on a hovered button
|
||||
@@ -2463,6 +2596,11 @@ The size of the left gutter of a clickable bar, in pixels.
|
||||
default.hover_background, default.background.)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The only time an activated widget will be seen by the user is if a
|
||||
transition occurs immediately after the widget has been activated.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
As an example, take assigning a background to buttons that changes
|
||||
when the button is hovered over.
|
||||
@@ -2677,10 +2815,10 @@ unilaterally making something different from everyone else.
|
||||
|
||||
<p>
|
||||
Anyway, in Ren'Py keysyms are strings. The first kind of keysym is of
|
||||
the form 'mouse_#', for a number between 1 and 5. These keysyms are
|
||||
generated by mouse button presses or turns of the mouse wheel. For
|
||||
example, "mouse_1" is generally the left mouse button, while "mouse_4"
|
||||
is a turn of the the mouse wheel to the top.
|
||||
the form 'mouseup_#' or 'mousedown_#', for a number between 1 and 5. These keysyms are
|
||||
generated by mouse button presses, releases, or turns of the mouse wheel. For
|
||||
example, "mousedown_1" is generally a press of the left mouse button,
|
||||
"mouseup_1" is a release of that button, and "mousedown_4" is a turn of the the mouse wheel to the top.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -2700,34 +2838,35 @@ This type of keysym looks like "K_BACKSPACE", "K_RETURN", and "K_TAB".
|
||||
<p>
|
||||
To change a binding, update the appropriate list in
|
||||
config.keymap. The following code adds the 't' key to the list of
|
||||
keys that dismiss a say statement.
|
||||
keys that dismiss a say statement, and removes the space key from
|
||||
that list.
|
||||
</p>
|
||||
|
||||
<example>
|
||||
init:
|
||||
$ config.keymap['dismiss'].append('t')
|
||||
$ config.keymap['dismiss'].remove('K_SPACE')
|
||||
</example>
|
||||
|
||||
<p>
|
||||
The default keymap is contained inside the python code implementing
|
||||
Ren'Py, and as of version 4.3 is as follows:
|
||||
Ren'Py, and as of version 4.5 is as follows:
|
||||
</p>
|
||||
|
||||
<example>
|
||||
keymap = dict(
|
||||
|
||||
# Bindings present almost everywhere, unless explicitly
|
||||
# disabled.
|
||||
rollback = [ 'K_PAGEUP', 'mouse_4' ],
|
||||
rollback = [ 'K_PAGEUP', 'mousedown_4' ],
|
||||
screenshot = [ 's' ],
|
||||
toggle_fullscreen = [ 'f' ],
|
||||
toggle_music = [ 'm' ],
|
||||
game_menu = [ 'K_ESCAPE', 'mouse_3' ],
|
||||
hide_windows = [ 'mouse_2' ],
|
||||
game_menu = [ 'K_ESCAPE', 'mousedown_3' ],
|
||||
hide_windows = [ 'mouseup_2' ],
|
||||
|
||||
# Say.
|
||||
rollforward = [ 'mouse_5', 'K_PAGEDOWN' ],
|
||||
dismiss = [ 'mouse_1', 'K_RETURN', 'K_SPACE', 'K_KP_ENTER' ],
|
||||
rollforward = [ 'mousedown_5', 'K_PAGEDOWN' ],
|
||||
dismiss = [ 'mouseup_1', 'K_RETURN', 'K_SPACE', 'K_KP_ENTER' ],
|
||||
|
||||
# Keymouse.
|
||||
keymouse_left = [ 'K_LEFT' ],
|
||||
@@ -2736,23 +2875,23 @@ keymap = dict(
|
||||
keymouse_down = [ 'K_DOWN' ],
|
||||
|
||||
# Menu.
|
||||
menu_mouseselect = [ 'mouse_1' ],
|
||||
menu_mouseselect = [ 'mouseup_1' ],
|
||||
menu_keyselect = ['K_RETURN', 'K_KP_ENTER' ],
|
||||
menu_keyup = [ 'K_UP' ],
|
||||
menu_keydown = [ 'K_DOWN' ],
|
||||
|
||||
# Button.
|
||||
button_select = [ 'mouse_1', 'K_RETURN', 'K_KP_ENTER' ],
|
||||
button_select = [ 'mouseup_1', 'K_RETURN', 'K_KP_ENTER' ],
|
||||
|
||||
# Input.
|
||||
input_backspace = [ 'K_BACKSPACE' ],
|
||||
input_enter = [ 'K_RETURN', 'K_KP_ENTER' ],
|
||||
|
||||
# Imagemap.
|
||||
imagemap_select = [ 'K_RETURN', 'K_KP_ENTER', 'mouse_1' ],
|
||||
imagemap_select = [ 'K_RETURN', 'K_KP_ENTER', 'mouseup_1' ],
|
||||
|
||||
# Bar.
|
||||
bar_click = [ 'mouse_1' ],
|
||||
bar_click = [ 'mouseup_1' ],
|
||||
|
||||
# These keys control skipping.
|
||||
skip = [ 'K_LCTRL', 'K_RCTRL' ],
|
||||
@@ -2790,7 +2929,7 @@ $ ui.textbutton("B", clicked=ui.returns("B"))
|
||||
$ ui.textbutton("C", clicked=ui.returns("C"))
|
||||
|
||||
$ ui.close()
|
||||
$ choice = renpy.interact(suppress_overlay=True)
|
||||
$ choice = ui.interact(suppress_overlay=True)
|
||||
</example>
|
||||
|
||||
<p>
|
||||
@@ -2835,6 +2974,12 @@ suffer from this problem, and so should probably be used in preference
|
||||
to lambda.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
By default, widgets are added to the 'transient' layer. This can be
|
||||
changed by a call to ui.layer(), followed by a matching call to
|
||||
ui.close() when done adding widgets to the new layer.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The following are the functions available in the ui module, which is
|
||||
automatically present in the game namespace.
|
||||
@@ -2854,6 +2999,8 @@ automatically present in the game namespace.
|
||||
|
||||
<!-- func ui.hbox -->
|
||||
|
||||
<!-- func ui.grid -->
|
||||
|
||||
<!-- func ui.fixed -->
|
||||
|
||||
<h4>No-Child Widgets and Psuedo-Widgets</h4>
|
||||
@@ -2874,12 +3021,16 @@ automatically present in the game namespace.
|
||||
|
||||
<!-- func ui.saybehavior -->
|
||||
|
||||
<!-- func ui.pausebehavior -->
|
||||
|
||||
<!-- func ui.keymousebehavior -->
|
||||
|
||||
<h4>Functions</h4>
|
||||
|
||||
<!-- func ui.interact -->
|
||||
|
||||
<!-- func ui.layer -->
|
||||
|
||||
<!-- func ui.close -->
|
||||
|
||||
<function name="ui.returns" sig="(value)">
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../extras/fullscreen.rpy
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../extras/kanamode.rpy
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../script.rpy
|
||||
@@ -0,0 +1,102 @@
|
||||
init -10:
|
||||
|
||||
$ config.searchpath.append('moonlight')
|
||||
|
||||
# Set up the size of the screen, and the window title.
|
||||
$ config.screen_width = 800
|
||||
$ config.screen_height = 600
|
||||
$ config.window_title = "A Ren'Py Extras Game"
|
||||
|
||||
# These are positions that can be used inside at clauses. We set
|
||||
# them up here so that they can be used throughout the program.
|
||||
$ left = Position(xpos=0.0, xanchor='left')
|
||||
$ right = Position(xpos=1.0, xanchor='right')
|
||||
$ center = Position()
|
||||
|
||||
# Likewise, we set up some transitions that we can use in with
|
||||
# clauses and statements.
|
||||
$ fade = Fade(.5, 0, .5) # Fade to black and back.
|
||||
$ dissolve = Dissolve(0.5)
|
||||
|
||||
$ wiperight = CropMove(1.0, "wiperight")
|
||||
$ wipeleft = CropMove(1.0, "wipeleft")
|
||||
$ wipeup = CropMove(1.0, "wipeup")
|
||||
$ wipedown = CropMove(1.0, "wipedown")
|
||||
|
||||
$ slideright = CropMove(1.0, "slideright")
|
||||
$ slideleft = CropMove(1.0, "slideleft")
|
||||
$ slideup = CropMove(1.0, "slideup")
|
||||
$ slidedown = CropMove(1.0, "slidedown")
|
||||
|
||||
$ slideawayright = CropMove(1.0, "slideawayright")
|
||||
$ slideawayleft = CropMove(1.0, "slideawayleft")
|
||||
$ slideawayup = CropMove(1.0, "slideawayup")
|
||||
$ slideawaydown = CropMove(1.0, "slideawaydown")
|
||||
|
||||
$ irisout = CropMove(1.0, "irisout")
|
||||
$ irisin = CropMove(1.0, "irisin")
|
||||
|
||||
# Now, we declare the images that are used in the program.
|
||||
|
||||
# Images.
|
||||
image black = Solid((0, 0, 0, 255))
|
||||
image white = Solid((255, 255, 255, 255))
|
||||
image yellow = Solid((255, 255, 200, 255))
|
||||
|
||||
# Opening Sequence.
|
||||
image bigbeach1 = Image("bigbeach1.jpg")
|
||||
image presents = Image("presents.png")
|
||||
|
||||
# Backgrounds.
|
||||
image beach1 = Image("beach1b.jpg")
|
||||
image beach1 mary = Image("beach1c.jpg")
|
||||
image beach1 title = Image("beach1a.jpg")
|
||||
|
||||
image beach2 = Image("beach2.jpg")
|
||||
image beach3 = Image("beach3.jpg")
|
||||
|
||||
image dawn1 = Image("dawn1.jpg")
|
||||
image dawn2 = Image("dawn2.jpg")
|
||||
|
||||
image library = Image("library.jpg")
|
||||
|
||||
# Ending 1.
|
||||
image transfer = Image("transfer.png")
|
||||
image moonpic = Image("moonpic.jpg")
|
||||
image nogirlpic = Image("nogirlpic.jpg")
|
||||
|
||||
|
||||
# Ending 3.
|
||||
image littlemary = Image("littlemary.jpg")
|
||||
|
||||
# Ending 4.
|
||||
image hospital1 = Image("hospital1.jpg")
|
||||
image hospital2 = Image("hospital2.jpg")
|
||||
image hospital3 = Image("hospital3.jpg")
|
||||
image heaven = Image("heaven.jpg")
|
||||
|
||||
# Endings Common
|
||||
image good_ending = Image("ending.jpg")
|
||||
image bad_ending = Image("badending.jpg")
|
||||
|
||||
# Mary.
|
||||
image mary dark confused smiling = Image("mary_dark_confused_smiling.png")
|
||||
image mary dark confused wistful = Image("mary_dark_confused_wistful.png")
|
||||
image mary dark crying = Image("mary_dark_crying.png")
|
||||
image mary dark laughing = Image("mary_dark_laughing.png")
|
||||
image mary dark sad = Image("mary_dark_sad.png")
|
||||
image mary dark smiling = Image("mary_dark_smiling.png")
|
||||
image mary dark vhappy = Image("mary_dark_vhappy.png")
|
||||
image mary dark wistful = Image("mary_dark_wistful.png")
|
||||
|
||||
image mary dawn confused smiling = Image("mary_dawn_confused_smiling.png")
|
||||
image mary dawn confused wistful = Image("mary_dawn_confused_wistful.png")
|
||||
image mary dawn crying = Image("mary_dawn_crying.png")
|
||||
image mary dawn laughing = Image("mary_dawn_laughing.png")
|
||||
image mary dawn sad = Image("mary_dawn_sad.png")
|
||||
image mary dawn smiling = Image("mary_dawn_smiling.png")
|
||||
image mary dawn vhappy = Image("mary_dawn_vhappy.png")
|
||||
image mary dawn wistful = Image("mary_dawn_wistful.png")
|
||||
|
||||
label start:
|
||||
jump example
|
||||
@@ -0,0 +1,21 @@
|
||||
# This file contains a minimal set of style changes needed to have
|
||||
# Ren'Py work with a game that's 640x480 in size.
|
||||
|
||||
init 1:
|
||||
# Change the screen width.
|
||||
$ config.screen_width = 640
|
||||
$ config.screen_height = 480
|
||||
|
||||
# Font sizes.
|
||||
$ style.default.size = 20
|
||||
$ style.button_text.size = 20
|
||||
$ style.file_picker_text.size = 14
|
||||
|
||||
# Perhaps change the fudge factor on windows, if line spacing
|
||||
# looks weird.
|
||||
if renpy.windows():
|
||||
$ style.default.line_height_fudge = -4
|
||||
|
||||
$ style.file_picker_entry.xminimum = 320
|
||||
$ library.thumbnail_width = 60
|
||||
$ library.thumbnail_height = 45
|
||||
@@ -0,0 +1,63 @@
|
||||
# In this file, we have an example of replacing the default menu with
|
||||
# a custom one. This new menu consists of a series of centered
|
||||
# buttons, with dialogue appearing in a narration window.
|
||||
|
||||
# You can try this one out by dropping it right into the game
|
||||
# directory.
|
||||
|
||||
# The code that implements button menus.
|
||||
init:
|
||||
python:
|
||||
style.create('menu_button', 'button')
|
||||
style.create('menu_button_text', 'button_text')
|
||||
|
||||
def menu(menuitems):
|
||||
|
||||
narration = None
|
||||
|
||||
ui.keymousebehavior()
|
||||
|
||||
ui.window(style='menu_window')
|
||||
ui.vbox(xanchor='center', xpos=0.5)
|
||||
|
||||
for label, value in menuitems:
|
||||
if value is None:
|
||||
narration = label
|
||||
continue
|
||||
|
||||
ui.textbutton(label,
|
||||
style="menu_button",
|
||||
text_style="menu_button_text",
|
||||
clicked=ui.returns(value))
|
||||
|
||||
ui.close()
|
||||
|
||||
if narration:
|
||||
narrator(narration, interact=False)
|
||||
|
||||
rv = ui.interact()
|
||||
renpy.checkpoint()
|
||||
return rv
|
||||
|
||||
# Styles to make button menus look good.
|
||||
init 1:
|
||||
python hide:
|
||||
style.menu_window.background = None
|
||||
style.menu_window.yminimum = 0
|
||||
style.menu_window.ypos = 0.40
|
||||
style.menu_window.yanchor = 'center'
|
||||
style.menu_window.xfill = True
|
||||
|
||||
style.menu_button.background = Solid((0, 0, 255, 128))
|
||||
style.menu_button.xfill = True
|
||||
style.menu_button.top_padding = 5
|
||||
style.menu_button.bottom_margin = 5
|
||||
|
||||
style.menu_button_text.xpos = 0.5
|
||||
style.menu_button_text.xanchor = 'center'
|
||||
|
||||
style.menu_button_text.hover_color = (255, 255, 0, 255)
|
||||
style.menu_button_text.idle_color = (255, 255, 255, 255)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# This file contains code to automatically switch the game into
|
||||
# fullscreen mode.
|
||||
|
||||
# Please note: Fullscreen mode is broken on some platforms (mostly
|
||||
# virtual windows machines like vmware and virtual PC). So having
|
||||
# this code automatically execute could make your game hard to use
|
||||
# on those platforms.
|
||||
#
|
||||
# The way we work around this is to tell users of these platforms
|
||||
# to press 'f' to toggle fullscreen mode. If the game's running in
|
||||
# windowed mode, the problems don't seem to appear.
|
||||
#
|
||||
# You should probably put words to that effect in some sort of
|
||||
# README file.
|
||||
|
||||
# The first time this code runs, the game switches into fullscreen.
|
||||
# After that, it respects the user's preference.
|
||||
|
||||
init:
|
||||
python:
|
||||
if not persistent.set_fullscreen:
|
||||
persistent.set_fullscreen = True
|
||||
_preferences.fullscreen = True
|
||||
@@ -0,0 +1,193 @@
|
||||
# This is an implementation of Kana mode, a mode in which multiple
|
||||
# lines of dialogue are shown at once, in a fullscreen window. A
|
||||
# single line of dialogue is placed onto the screen for each mouse
|
||||
# click. Calling the clear function clears the screen, ensuring that
|
||||
# the next line will appear at the top of the sceen.
|
||||
#
|
||||
# An example of using Kana mode follows the code that implements it.
|
||||
|
||||
|
||||
# This init block contains all of the code needed to implement
|
||||
# Kana mode. You probably don't want to change this... unless you
|
||||
# do. But all of the customization is found below.
|
||||
init -100:
|
||||
python:
|
||||
|
||||
# This is a list of KanaCharacter, line of dialogue tuples.
|
||||
kana_display_list = [ ]
|
||||
|
||||
# Spacings used.
|
||||
kana_vspacing = 0
|
||||
kana_hspacing = 0
|
||||
|
||||
def clear(arg=None):
|
||||
global kana_display_list
|
||||
kana_display_list = [ ]
|
||||
|
||||
def kana_show():
|
||||
|
||||
ui.window(style='say_window')
|
||||
ui.vbox(kana_vspacing)
|
||||
|
||||
for char, what in kana_display_list:
|
||||
char.show(what)
|
||||
|
||||
ui.close()
|
||||
|
||||
ui.saybehavior()
|
||||
ui.interact()
|
||||
renpy.checkpoint()
|
||||
|
||||
# Characters and the narrator should be instances of this
|
||||
# object.
|
||||
class KanaCharacter(object):
|
||||
|
||||
def __init__(self, who,
|
||||
what_prefix='"',
|
||||
what_suffix='"',
|
||||
who_style='say_label',
|
||||
what_style='say_dialogue',
|
||||
**properties):
|
||||
|
||||
self.who = who
|
||||
self.what_prefix = what_prefix
|
||||
self.what_suffix = what_suffix
|
||||
self.who_style = who_style
|
||||
self.what_style = what_style
|
||||
self.properties = properties
|
||||
|
||||
def __call__(self, what):
|
||||
kana_display_list.append((self, what))
|
||||
kana_show()
|
||||
|
||||
def show(self, what):
|
||||
ui.hbox(kana_hspacing)
|
||||
ui.text(self.who, style=self.who_style,
|
||||
**self.properties)
|
||||
ui.text(self.what_prefix + what + self.what_suffix,
|
||||
style=self.what_style)
|
||||
ui.close()
|
||||
|
||||
|
||||
# This section updates the styles that are used by the Kana mode stuff
|
||||
# so that they work well with Kana mode. The user may want to change
|
||||
# these so that they look more appropriate.
|
||||
init:
|
||||
|
||||
# The space between lines of dialogue.
|
||||
$ kana_vspacing = 10
|
||||
$ kana_hspacing = 10
|
||||
|
||||
$ style.say_window.background = Solid((0, 0, 0, 96))
|
||||
$ style.say_window.xfill = True
|
||||
$ style.say_window.yfill = True
|
||||
$ style.say_window.xmargin = 0
|
||||
$ style.say_window.ymargin = 0
|
||||
$ style.say_window.xpadding = 20
|
||||
$ style.say_window.ypadding = 20
|
||||
|
||||
$ style.say_label.minwidth = 100
|
||||
$ style.say_label.textalign = 1.0
|
||||
|
||||
# This just makes it look nicer.
|
||||
$ style.say_dialogue.rest_indent = 9
|
||||
|
||||
# Adjust the menu to match.
|
||||
$ style.menu_window.background = Solid((0, 0, 0, 96))
|
||||
$ style.menu_window.xfill = True
|
||||
$ style.menu_window.yfill = True
|
||||
$ style.menu_window.xmargin = 0
|
||||
$ style.menu_window.ymargin = 0
|
||||
$ style.menu_window.xpadding = 20
|
||||
$ style.menu_window.ypadding = 20
|
||||
|
||||
$ style.menu.xpos = 110
|
||||
|
||||
# And now, the example. This example can't be simply run, but instead
|
||||
# should serve as a guide to how to get Kana mode working in your own
|
||||
# game.
|
||||
|
||||
# In the init block, declare the characters as KanaCharacters. We also
|
||||
# want to declare the narrator as a special KanaCharacter.
|
||||
init:
|
||||
$ p = KanaCharacter("")
|
||||
$ g = KanaCharacter("Girl:", color=(255, 128, 128, 255))
|
||||
$ narrator = KanaCharacter("",
|
||||
what_prefix='',
|
||||
what_suffix='',
|
||||
what_style='say_thought')
|
||||
|
||||
|
||||
# Now, the actual script of the example. (An excerpt from Moonlight
|
||||
# Walks.) Notice how we place calls to clear in places where the
|
||||
# we want the screen to be cleared.
|
||||
|
||||
# This text is here to serve as an example, and shouldn't be used
|
||||
# in your game.
|
||||
label example:
|
||||
|
||||
show beach2
|
||||
show mary dark wistful
|
||||
|
||||
$ clear()
|
||||
|
||||
p "What can you tell me about your parents?"
|
||||
|
||||
g "Papa and Mama both came across the ocean as settlers when they
|
||||
were just children."
|
||||
|
||||
g "Papa fought in the war. When it was over, he married Mama, and
|
||||
they used his pension to move here from the mainland, and to
|
||||
build us a house."
|
||||
|
||||
g "Together, they farmed the land, and eventually they had
|
||||
children."
|
||||
|
||||
g "I had an older sister and a younger sister. I was the middle
|
||||
child."
|
||||
|
||||
$ clear()
|
||||
|
||||
show mary dark sad
|
||||
|
||||
"She paused for a second to collect her thoughts before
|
||||
continuing. This part was taking a strain on her."
|
||||
|
||||
g "When I was ten, an epidemic hit the island."
|
||||
|
||||
g "We came down with it, and so did all of the other families."
|
||||
|
||||
g "My family was too sick to move, but our neighbors, the Millers,
|
||||
sent some of their boys to get help from the mainland."
|
||||
|
||||
"I remember thinking that Miller was the last name of my aunt and
|
||||
uncle, and wondering if they could be related to me."
|
||||
|
||||
g "I don't know what happened to them, but I never heard from them
|
||||
again."
|
||||
|
||||
show mary dark crying
|
||||
|
||||
g "It lasted a week, and then it was over. My sisters... my
|
||||
parents... they all..."
|
||||
|
||||
g "Now I'm the only one left."
|
||||
|
||||
$ clear()
|
||||
|
||||
p "I'm sorry."
|
||||
|
||||
"I didn't know what else to say to a girl that had lost her
|
||||
family, and was obviously broken up about it."
|
||||
|
||||
show mary dark sad
|
||||
|
||||
"She nodded in response, wiped her tears, and we once again
|
||||
started walking in silence."
|
||||
|
||||
menu menu_1:
|
||||
"Show me the example again.":
|
||||
jump example
|
||||
|
||||
"I'm done. Let me go.":
|
||||
return
|
||||
@@ -0,0 +1,87 @@
|
||||
# This file replaces most of the game menu navigation with overlay
|
||||
# buttons that jump directly to various parts of the game menu.
|
||||
# Right clicking hides and shows the buttons, rather than calling
|
||||
# up the game menu directly.
|
||||
|
||||
init:
|
||||
python hide:
|
||||
|
||||
# overlay_menu is an object storing information about the
|
||||
# overlay menu state.
|
||||
store.overlay_menu = object()
|
||||
overlay_menu.shown = False
|
||||
|
||||
# overlay_menu is also a new layer, containing the overlay
|
||||
# menu.
|
||||
config.layers.append("overlay_menu")
|
||||
config.overlay_layers.append("overlay_menu")
|
||||
|
||||
|
||||
# This function actually draws the overlay menu.
|
||||
def overlay_menu_func():
|
||||
if overlay_menu.shown:
|
||||
|
||||
ui.layer("overlay_menu")
|
||||
|
||||
ui.vbox(xpos=1.0, xanchor="right", ypos=0.75, yanchor="bottom")
|
||||
|
||||
def button(label, target):
|
||||
ui.textbutton(label, clicked=renpy.curried_call_in_new_context(target))
|
||||
|
||||
button("Load Game", "_game_menu_load")
|
||||
button("Save Game", "_game_menu_save")
|
||||
button("Preferences", "_game_menu_preferences")
|
||||
|
||||
ui.close()
|
||||
ui.close()
|
||||
|
||||
|
||||
config.overlay_functions.append(overlay_menu_func)
|
||||
|
||||
|
||||
# This function toggles the visibility of the overlay menu.
|
||||
def overlay_menu_toggle():
|
||||
shown = not overlay_menu.shown
|
||||
overlay_menu.shown = shown
|
||||
|
||||
# How long should the transitions take?
|
||||
trans_delay = 0.5
|
||||
|
||||
# These transitions assume that the menu is placed on the
|
||||
# right side of the screen. This indicator gives the
|
||||
# fraction of the screen that participates in transitions.
|
||||
|
||||
trans_frac = 0.75
|
||||
|
||||
if shown:
|
||||
|
||||
trans = CropMove(trans_delay,
|
||||
"custom",
|
||||
startcrop=(trans_frac, 0.0, 0.0, 1.0),
|
||||
startpos=(1.0, 0.0),
|
||||
endcrop=(trans_frac, 0.0, 1.0-trans_frac, 1.0),
|
||||
endpos=(trans_frac, 0.0),
|
||||
topnew=True)
|
||||
|
||||
renpy.transition(trans, 'overlay_menu')
|
||||
|
||||
else:
|
||||
|
||||
trans = CropMove(trans_delay,
|
||||
"custom",
|
||||
endcrop=(trans_frac, 0.0, 0.0, 1.0),
|
||||
endpos=(1.0, 0.0),
|
||||
startcrop=(trans_frac, 0.0, 1.0-trans_frac, 1.0),
|
||||
startpos=(trans_frac, 0.0),
|
||||
topnew=False)
|
||||
|
||||
renpy.transition(trans, 'overlay_menu')
|
||||
|
||||
|
||||
renpy.restart_interaction()
|
||||
|
||||
|
||||
# Add a new underlay that handles the overlay menu toggle.
|
||||
config.underlay.append(renpy.Keymap(game_menu = overlay_menu_toggle))
|
||||
|
||||
|
||||
+6
-6
@@ -2,9 +2,9 @@
|
||||
# order.
|
||||
|
||||
# Some version numbers and things.
|
||||
version = "Ren'Py 4.4.2"
|
||||
version = "Ren'Py 4.6"
|
||||
script_version = 5
|
||||
savegame_suffix = "-3.save"
|
||||
savegame_suffix = "-6.save"
|
||||
|
||||
|
||||
# Can be first, because has no dependencies, and may be imported
|
||||
@@ -19,21 +19,21 @@ import renpy.curry
|
||||
import renpy.execution
|
||||
import renpy.loader
|
||||
import renpy.loadsave
|
||||
import renpy.music
|
||||
import renpy.parser
|
||||
import renpy.python # object
|
||||
import renpy.script
|
||||
import renpy.style
|
||||
import renpy.sound
|
||||
|
||||
import renpy.display
|
||||
import renpy.display
|
||||
import renpy.display.render # Most display stuff depends on this.
|
||||
import renpy.display.core # object
|
||||
import renpy.display.surface
|
||||
import renpy.display.audio
|
||||
import renpy.display.text # core
|
||||
import renpy.display.layout # core
|
||||
import renpy.display.behavior # layout
|
||||
import renpy.display.transition # core
|
||||
import renpy.display.image # core, behavior
|
||||
import renpy.display.video
|
||||
|
||||
import renpy.ui
|
||||
|
||||
|
||||
+1
-10
@@ -403,16 +403,7 @@ class With(Node):
|
||||
def execute(self):
|
||||
trans = renpy.python.py_eval(self.expr)
|
||||
|
||||
# Code copied into exports.with
|
||||
|
||||
if not trans:
|
||||
renpy.game.interface.with_none()
|
||||
else:
|
||||
if renpy.game.preferences.transitions:
|
||||
renpy.game.interface.set_transition(trans)
|
||||
renpy.game.interface.interact(show_mouse=False,
|
||||
trans_pause=True,
|
||||
suppress_overlay=True)
|
||||
renpy.exports.with(trans)
|
||||
|
||||
return self.next
|
||||
|
||||
|
||||
+24
-9
@@ -98,21 +98,36 @@ fade_music = 0.0
|
||||
# Should the at list be sticky?
|
||||
sticky_positions = False
|
||||
|
||||
# A list of all of the layers that we know about.
|
||||
layers = [ 'master', 'transient', 'overlay' ]
|
||||
|
||||
# A list of layers that should be cleared when we replace
|
||||
# transients.
|
||||
transient_layers = [ 'transient' ]
|
||||
|
||||
# A list of layers that should be cleared when we recompute
|
||||
# overlays.
|
||||
overlay_layers = [ 'overlay' ]
|
||||
|
||||
# True if we want to show overlays during wait statements, or
|
||||
# false otherwise.
|
||||
overlay_during_wait = True
|
||||
|
||||
# The keymap that is used to change keypresses and mouse events.
|
||||
keymap = dict(
|
||||
|
||||
# Bindings present almost everywhere, unless explicitly
|
||||
# disabled.
|
||||
rollback = [ 'K_PAGEUP', 'mouse_4' ],
|
||||
rollback = [ 'K_PAGEUP', 'mousedown_4' ],
|
||||
screenshot = [ 's' ],
|
||||
toggle_fullscreen = [ 'f' ],
|
||||
toggle_music = [ 'm' ],
|
||||
game_menu = [ 'K_ESCAPE', 'mouse_3' ],
|
||||
hide_windows = [ 'mouse_2' ],
|
||||
game_menu = [ 'K_ESCAPE', 'mouseup_3' ],
|
||||
hide_windows = [ 'mouseup_2' ],
|
||||
|
||||
# Say.
|
||||
rollforward = [ 'mouse_5', 'K_PAGEDOWN' ],
|
||||
dismiss = [ 'mouse_1', 'K_RETURN', 'K_SPACE', 'K_KP_ENTER' ],
|
||||
rollforward = [ 'mousedown_5', 'K_PAGEDOWN' ],
|
||||
dismiss = [ 'mouseup_1', 'K_RETURN', 'K_SPACE', 'K_KP_ENTER' ],
|
||||
|
||||
# Keymouse.
|
||||
keymouse_left = [ 'K_LEFT' ],
|
||||
@@ -121,23 +136,23 @@ keymap = dict(
|
||||
keymouse_down = [ 'K_DOWN' ],
|
||||
|
||||
# Menu.
|
||||
menu_mouseselect = [ 'mouse_1' ],
|
||||
menu_mouseselect = [ 'mouseup_1' ],
|
||||
menu_keyselect = ['K_RETURN', 'K_KP_ENTER' ],
|
||||
menu_keyup = [ 'K_UP' ],
|
||||
menu_keydown = [ 'K_DOWN' ],
|
||||
|
||||
# Button.
|
||||
button_select = [ 'mouse_1', 'K_RETURN', 'K_KP_ENTER' ],
|
||||
button_select = [ 'mouseup_1', 'K_RETURN', 'K_KP_ENTER' ],
|
||||
|
||||
# Input.
|
||||
input_backspace = [ 'K_BACKSPACE' ],
|
||||
input_enter = [ 'K_RETURN', 'K_KP_ENTER' ],
|
||||
|
||||
# Imagemap.
|
||||
imagemap_select = [ 'K_RETURN', 'K_KP_ENTER', 'mouse_1' ],
|
||||
imagemap_select = [ 'K_RETURN', 'K_KP_ENTER', 'mouseup_1' ],
|
||||
|
||||
# Bar.
|
||||
bar_click = [ 'mouse_1' ],
|
||||
bar_click = [ 'mouseup_1' ],
|
||||
|
||||
|
||||
# These keys control skipping.
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@ class Curry(object):
|
||||
callable with the stored arguments and the additional arguments
|
||||
supplied to the call.
|
||||
"""
|
||||
|
||||
__doc__ = property(fget=lambda self : self.callable.__doc__)
|
||||
|
||||
# __doc__ = property(fget=lambda self : self.callable.__doc__)
|
||||
|
||||
def __init__(self, callable, *args, **kwargs):
|
||||
self.callable = callable
|
||||
|
||||
@@ -0,0 +1,319 @@
|
||||
# This module contains code that handles the playing of sound and
|
||||
# music files.
|
||||
|
||||
import pygame
|
||||
import renpy
|
||||
import sys # to detect windows.
|
||||
|
||||
# The Windows Volume Management Strategy (tm).
|
||||
|
||||
# We keep a master music volume, which is the volume we use directly
|
||||
# when playing music as mp3, ogg, etc. When we start up, we compute
|
||||
# a midi music scaling factor. This midi music scaling factor is
|
||||
# computed from the current master music volume such that when we are not
|
||||
# fading, if we pygame.mixer.music.set_volume() to the mmv * mmsv, we get
|
||||
# read the same value from midiOutGetVolume() as we did before we tried
|
||||
# doing that.
|
||||
|
||||
|
||||
mixer_enabled = True
|
||||
|
||||
playing_midi = True
|
||||
fading = False
|
||||
master_music_volume = 1.0
|
||||
|
||||
|
||||
windows_magic = False
|
||||
|
||||
if hasattr(sys, 'winver'):
|
||||
|
||||
midi_msf = 0.0
|
||||
|
||||
last_raw_volume = -1
|
||||
|
||||
def read_raw_volume():
|
||||
res = c_uint()
|
||||
|
||||
for i in range(0, winmm.midiOutGetNumDevs()):
|
||||
rv = winmm.midiOutGetVolume(i, byref(res))
|
||||
|
||||
if not rv:
|
||||
return res.value
|
||||
else:
|
||||
print "Couldn't read raw midi volume."
|
||||
return -1
|
||||
|
||||
|
||||
|
||||
try:
|
||||
from ctypes import windll, c_uint, byref
|
||||
winmm = windll.winmm
|
||||
|
||||
|
||||
def compute_midi_msf():
|
||||
"""
|
||||
Computes the Midi MSF. Returns True if successful, False if otherwise.
|
||||
"""
|
||||
|
||||
# Don't update the MSF when fading is going on, or when not
|
||||
# playing a midi. (Except before playing any music whatsoever.)
|
||||
if fading or not playing_midi:
|
||||
return False
|
||||
|
||||
global last_raw_volume
|
||||
|
||||
raw_vol = read_raw_volume()
|
||||
|
||||
if raw_vol < 0:
|
||||
return False
|
||||
|
||||
# The case in which the volume hasn't changed recently.
|
||||
if raw_vol == last_raw_volume:
|
||||
return True
|
||||
|
||||
last_raw_volume = raw_vol
|
||||
|
||||
# print "raw_vol", raw_vol
|
||||
|
||||
# The fraction that the midi mixer is at.
|
||||
mixfrac = 1.0 * ( raw_vol & 0xffff ) / 0xffff
|
||||
|
||||
global midi_msf
|
||||
midi_msf = mixfrac / master_music_volume
|
||||
|
||||
# print "Midi msf is now:", midi_msf
|
||||
|
||||
return True
|
||||
|
||||
# This should get called after the music starts playing.
|
||||
def set_music_volume(vol):
|
||||
|
||||
global master_music_volume
|
||||
master_music_volume = vol
|
||||
|
||||
if playing_midi:
|
||||
|
||||
vol *= midi_msf
|
||||
if vol > 1.0:
|
||||
vol = 1.0
|
||||
|
||||
pygame.mixer.music.set_volume(vol)
|
||||
|
||||
global last_raw_volume
|
||||
last_raw_volume = read_raw_volume()
|
||||
|
||||
# Figure out the default msf, and set it up.
|
||||
windows_magic = compute_midi_msf()
|
||||
playing_midi = False
|
||||
|
||||
except Exception, e:
|
||||
print "Exception when trying to init music:", str(e)
|
||||
print "Falling back to Unix mode."
|
||||
|
||||
if not windows_magic:
|
||||
|
||||
def compute_midi_msf():
|
||||
return
|
||||
|
||||
def set_music_volume(vol):
|
||||
global master_music_volume
|
||||
master_music_volume = vol
|
||||
|
||||
pygame.mixer.music.set_volume(vol)
|
||||
|
||||
playing_midi = False
|
||||
|
||||
# This detects if the filename is a midi, and sets playing_midi
|
||||
# appropriately.
|
||||
def detect_midi(fn):
|
||||
|
||||
fn = fn.lower()
|
||||
|
||||
global playing_midi
|
||||
playing_midi = fn.endswith(".mid") or fn.endswith(".midi")
|
||||
|
||||
|
||||
# Information about the currently playing track.
|
||||
current_music = None
|
||||
|
||||
def music_delay(offset):
|
||||
"""
|
||||
Returns the time left until the current music has been playing for
|
||||
offset seconds. If music is not playing, return None. May return
|
||||
a negative time.
|
||||
"""
|
||||
|
||||
mo = pygame.mixer.music.get_pos()
|
||||
if mo < 0:
|
||||
return None
|
||||
|
||||
mo /= 1000.0
|
||||
|
||||
return offset - mo
|
||||
|
||||
|
||||
|
||||
def music_start(filename, loops=-1, startpos=0.0):
|
||||
"""
|
||||
This starts music playing. If a music track is already playing,
|
||||
stops that track in favor of this one.
|
||||
|
||||
@param filename: The file that the music will be played from. This
|
||||
is relative to the game directory, and must be a real file (so it
|
||||
cannot be stored in an archive.)
|
||||
|
||||
@param loops: The number of times the music will loop after it
|
||||
finishes playing. If negative, the music will loop indefinitely.
|
||||
Please note that even once the song has finished, rollback or load
|
||||
may cause it to start playing again. So it may not be safe to have
|
||||
this set to a non-negative value.
|
||||
|
||||
@param startpos: The number of seconds into the music to start playing.
|
||||
"""
|
||||
|
||||
music_stop()
|
||||
renpy.game.context().scene_lists.music = (filename, loops, startpos)
|
||||
restore_music()
|
||||
|
||||
|
||||
def music_stop():
|
||||
"""
|
||||
Stops the currently playing music track.
|
||||
"""
|
||||
|
||||
renpy.game.context().scene_lists.music = None
|
||||
restore_music()
|
||||
|
||||
def restore_music():
|
||||
"""
|
||||
This makes sure that the current music matches the music found in
|
||||
the context.
|
||||
"""
|
||||
|
||||
global current_music
|
||||
global fading
|
||||
|
||||
compute_midi_msf()
|
||||
set_music_volume(1.0)
|
||||
|
||||
new_music = renpy.game.context().scene_lists.music
|
||||
|
||||
if not renpy.game.preferences.music:
|
||||
new_music = None
|
||||
|
||||
if current_music == new_music:
|
||||
return
|
||||
|
||||
if not mixer_enabled:
|
||||
return
|
||||
|
||||
# Usually, ignore errors.
|
||||
try:
|
||||
if current_music != new_music and current_music:
|
||||
current_music = None
|
||||
pygame.mixer.music.fadeout(int(renpy.config.fade_music * 1000))
|
||||
fading = True
|
||||
else:
|
||||
if not pygame.mixer.music.get_busy():
|
||||
fn, loops, startpos = new_music
|
||||
|
||||
fading = False
|
||||
detect_midi(fn)
|
||||
|
||||
pygame.mixer.music.load(renpy.game.basepath + "/" + fn)
|
||||
pygame.mixer.music.play(loops, startpos)
|
||||
|
||||
set_music_volume(master_music_volume)
|
||||
|
||||
current_music = new_music
|
||||
|
||||
except pygame.error, e:
|
||||
if renpy.config.debug_sound:
|
||||
raise
|
||||
else:
|
||||
print "Error while trying to play music:", str(e)
|
||||
# Plays sounds.
|
||||
|
||||
def play(fn, loops=0):
|
||||
"""
|
||||
This plays the given sound. The sound must be in a wav file,
|
||||
and expected to have a sample rate 44100hz (changable with
|
||||
config.sound_sample_rate), 16 bit, stereo. These expectations may
|
||||
be violated, but that may lead to conversion delays.
|
||||
|
||||
Once a sound has been started, there's no way to stop it.
|
||||
|
||||
@param fn: The name of the file that the sound is read from. This
|
||||
file may be contained in a game directory or an archive.
|
||||
|
||||
@param loops: The number of extra times the sound will be
|
||||
played. (The default, 0, will play the sound once.)
|
||||
"""
|
||||
|
||||
if not fn:
|
||||
return
|
||||
|
||||
if not renpy.game.preferences.sound:
|
||||
return
|
||||
|
||||
if not mixer_enabled:
|
||||
return
|
||||
|
||||
try:
|
||||
sound = pygame.mixer.Sound(renpy.loader.load(fn))
|
||||
sound.play()
|
||||
except:
|
||||
if renpy.config.debug_sound:
|
||||
raise
|
||||
|
||||
def pre_init():
|
||||
try:
|
||||
bufsize = 4096
|
||||
|
||||
import os
|
||||
|
||||
if 'RENPY_SOUND_BUFSIZE' in os.environ:
|
||||
bufsize = int(os.environ('RENPY_SOUND_BUFSIZE'))
|
||||
|
||||
pygame.mixer.pre_init(renpy.config.sound_sample_rate, -16, 2, bufsize)
|
||||
except:
|
||||
try:
|
||||
pygame.mixer.pre_init()
|
||||
except:
|
||||
if renpy.config.debug_sound:
|
||||
raise
|
||||
|
||||
def disable_mixer():
|
||||
"""
|
||||
This function is called by the video code to disable the
|
||||
pygame mixer.
|
||||
"""
|
||||
|
||||
global mixer_enabled
|
||||
|
||||
if mixer_enabled:
|
||||
try:
|
||||
pygame.mixer.quit()
|
||||
except:
|
||||
if renpy.config.debug_sound:
|
||||
raise
|
||||
|
||||
mixer_enabled = False
|
||||
|
||||
def enable_mixer():
|
||||
"""
|
||||
This function is called by the video code to enable the
|
||||
pygame mixer.
|
||||
"""
|
||||
|
||||
global mixer_enabled
|
||||
|
||||
if not mixer_enabled:
|
||||
try:
|
||||
pygame.mixer.init()
|
||||
except:
|
||||
if renpy.config.debug_sound:
|
||||
raise
|
||||
|
||||
mixer_enabled = True
|
||||
|
||||
+92
-57
@@ -2,6 +2,7 @@
|
||||
|
||||
|
||||
import renpy
|
||||
from renpy.display.render import render
|
||||
|
||||
# import renpy.display.core as core
|
||||
# import renpy.display.layout as layout
|
||||
@@ -23,7 +24,13 @@ def map_event(ev, name):
|
||||
keys = renpy.config.keymap[name]
|
||||
|
||||
if ev.type == MOUSEBUTTONDOWN:
|
||||
if ( "mouse_" + str(ev.button) ) in keys:
|
||||
if ( "mousedown_" + str(ev.button) ) in keys:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
if ev.type == MOUSEBUTTONUP:
|
||||
if ( "mouseup_" + str(ev.button) ) in keys:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -79,7 +86,7 @@ def skipping(ev):
|
||||
|
||||
return
|
||||
|
||||
class Keymap(renpy.display.layout.Container):
|
||||
class Keymap(renpy.display.layout.Null):
|
||||
"""
|
||||
This is a behavior that maps keys to functions that are called when
|
||||
the key is pressed. The keys are specified by giving the appropriate
|
||||
@@ -87,6 +94,7 @@ class Keymap(renpy.display.layout.Container):
|
||||
"""
|
||||
|
||||
def __init__(self, **keymap):
|
||||
super(Keymap, self).__init__(style='default')
|
||||
self.keymap = keymap
|
||||
|
||||
def event(self, ev, x, y):
|
||||
@@ -96,8 +104,8 @@ class Keymap(renpy.display.layout.Container):
|
||||
action()
|
||||
raise renpy.display.core.IgnoreEvent()
|
||||
|
||||
def render(self, width, height, st):
|
||||
return None
|
||||
# def render(self, width, height, st):
|
||||
# return None
|
||||
|
||||
class KeymouseBehavior(renpy.display.layout.Null):
|
||||
"""
|
||||
@@ -128,6 +136,26 @@ class KeymouseBehavior(renpy.display.layout.Null):
|
||||
|
||||
return None
|
||||
|
||||
class PauseBehavior(renpy.display.layout.Null):
|
||||
"""
|
||||
This is a class implementing the Pause behavior, which is to
|
||||
return a value after a certain amount of time has elapsed.
|
||||
"""
|
||||
|
||||
def __init__(self, delay, result=False):
|
||||
super(PauseBehavior, self).__init__()
|
||||
|
||||
self.delay = delay
|
||||
self.result = result
|
||||
|
||||
|
||||
def event(self, ev, x, y):
|
||||
|
||||
if ev.type == renpy.display.core.DISPLAYTIME and \
|
||||
self.delay and ev.duration > self.delay:
|
||||
return self.result
|
||||
|
||||
|
||||
class SayBehavior(renpy.display.layout.Null):
|
||||
"""
|
||||
This is a class that implements the say behavior,
|
||||
@@ -136,18 +164,12 @@ class SayBehavior(renpy.display.layout.Null):
|
||||
mouse button.
|
||||
"""
|
||||
|
||||
def __init__(self, delay=None):
|
||||
def __init__(self):
|
||||
super(SayBehavior, self).__init__()
|
||||
|
||||
self.delay = delay
|
||||
|
||||
|
||||
def event(self, ev, x, y):
|
||||
|
||||
if ev.type == renpy.display.core.DISPLAYTIME and \
|
||||
self.delay and ev.duration > self.delay:
|
||||
return False
|
||||
|
||||
if ev.type == renpy.display.core.DISPLAYTIME and \
|
||||
renpy.config.allow_skipping and renpy.config.skipping and \
|
||||
ev.duration > renpy.config.skip_delay / 1000.0:
|
||||
@@ -169,7 +191,7 @@ class SayBehavior(renpy.display.layout.Null):
|
||||
|
||||
class Menu(renpy.display.layout.VBox):
|
||||
|
||||
def __init__(self, menuitems, **properties):
|
||||
def __init__(self, menuitems, style='menu', **properties):
|
||||
"""
|
||||
@param menuitems: A list of menuitem tuples. The first element
|
||||
of each tuple is the string that should be displayed to the
|
||||
@@ -178,21 +200,27 @@ class Menu(renpy.display.layout.VBox):
|
||||
caption.
|
||||
"""
|
||||
|
||||
super(Menu, self).__init__(**properties)
|
||||
super(Menu, self).__init__(style=style, **properties)
|
||||
|
||||
self.selected = None
|
||||
self.results = [ ]
|
||||
|
||||
self.caption_style = renpy.style.Style('menu_caption', { })
|
||||
self.selected_style = renpy.style.Style('menu_choice', { })
|
||||
self.unselected_style = renpy.style.Style('menu_choice', { })
|
||||
# self.caption_style = renpy.style.Style('menu_caption', { })
|
||||
# self.selected_style = renpy.style.Style('menu_choice', { })
|
||||
# self.unselected_style = renpy.style.Style('menu_choice', { })
|
||||
|
||||
self.selected_style.set_prefix('hover_')
|
||||
self.unselected_style.set_prefix('idle_')
|
||||
# self.selected_style.set_prefix('hover_')
|
||||
# self.unselected_style.set_prefix('idle_')
|
||||
|
||||
for i, (caption, result) in enumerate(menuitems):
|
||||
self.add(renpy.display.text.Text(caption))
|
||||
|
||||
if result is not None:
|
||||
style = 'menu_choice'
|
||||
else:
|
||||
style = 'menu_caption'
|
||||
|
||||
self.add(renpy.display.text.Text(caption, style=style))
|
||||
|
||||
if self.selected is None and result is not None:
|
||||
self.selected = i
|
||||
|
||||
@@ -210,14 +238,13 @@ class Menu(renpy.display.layout.VBox):
|
||||
|
||||
# Captions should stay the default text color.
|
||||
if result is None:
|
||||
child.set_style(self.caption_style)
|
||||
continue
|
||||
|
||||
# Actual choices change color if they are selected or not.
|
||||
if i == self.selected:
|
||||
child.set_style(self.selected_style)
|
||||
child.set_style_prefix('hover_')
|
||||
else:
|
||||
child.set_style(self.unselected_style)
|
||||
child.set_style_prefix('idle_')
|
||||
|
||||
|
||||
def event(self, ev, x, y):
|
||||
@@ -229,25 +256,17 @@ class Menu(renpy.display.layout.VBox):
|
||||
# print x, y
|
||||
|
||||
old_selected = self.selected
|
||||
mouse_select = False
|
||||
|
||||
# Change selection based on mouse position.
|
||||
if ev.type == MOUSEMOTION:
|
||||
if ev.type == MOUSEMOTION or map_event(ev, "menu_mouseselect"):
|
||||
target = self.child_at_point(x, y)
|
||||
if target is None:
|
||||
return None
|
||||
|
||||
if self.results[target] is not None:
|
||||
self.selected = target
|
||||
|
||||
# Make selection based on mouse click position.
|
||||
if map_event(ev, "menu_mouseselect"):
|
||||
target = self.child_at_point(x, y)
|
||||
if target is None:
|
||||
return None
|
||||
|
||||
if self.results[target] is not None:
|
||||
renpy.sound.play(self.selected_style.activate_sound)
|
||||
return self.results[target]
|
||||
mouse_select = True
|
||||
|
||||
# Change selection based on keypress.
|
||||
if map_event(ev, "menu_keydown"):
|
||||
@@ -271,20 +290,23 @@ class Menu(renpy.display.layout.VBox):
|
||||
self.selected = selected
|
||||
break
|
||||
|
||||
# Make selection based on keypress.
|
||||
if map_event(ev, "menu_keyselect"):
|
||||
renpy.sound.play(self.selected_style.activate_sound)
|
||||
return self.results[self.selected]
|
||||
|
||||
# If the selected item changed, update the display.
|
||||
if self.selected != old_selected:
|
||||
|
||||
self.children[self.selected].set_style(self.selected_style)
|
||||
self.children[old_selected].set_style(self.unselected_style)
|
||||
self.children[self.selected].set_style_prefix("hover_")
|
||||
self.children[old_selected].set_style_prefix("idle_")
|
||||
|
||||
renpy.sound.play(self.selected_style.hover_sound)
|
||||
renpy.display.audio.play(self.style.hover_sound)
|
||||
# renpy.display.render.redraw(self, 0)
|
||||
|
||||
renpy.game.interface.redraw(0)
|
||||
# Make selection based on keypress or mouse click.
|
||||
if map_event(ev, "menu_keyselect") or \
|
||||
(mouse_select and map_event(ev, "menu_mouseselect")):
|
||||
|
||||
self.children[self.selected].set_style_prefix("activate_")
|
||||
|
||||
renpy.display.audio.play(self.style.activate_sound)
|
||||
return self.results[self.selected]
|
||||
|
||||
return None
|
||||
|
||||
@@ -297,13 +319,17 @@ class Button(renpy.display.layout.Window):
|
||||
super(Button, self).__init__(child, style=style, **properties)
|
||||
self.style.set_prefix('idle_')
|
||||
|
||||
self.activated = False
|
||||
|
||||
self.old_hover = False
|
||||
self.clicked = clicked
|
||||
self.hovered = hovered
|
||||
|
||||
def render(self, width, height, st):
|
||||
|
||||
if self.old_hover:
|
||||
if self.activated:
|
||||
self.set_style_prefix('activate_')
|
||||
elif self.old_hover:
|
||||
self.set_style_prefix('hover_')
|
||||
else:
|
||||
self.set_style_prefix('idle_')
|
||||
@@ -322,10 +348,13 @@ class Button(renpy.display.layout.Window):
|
||||
# else:
|
||||
# self.style.set_prefix('idle_')
|
||||
|
||||
renpy.game.interface.redraw(0)
|
||||
renpy.display.render.redraw(self, 0)
|
||||
|
||||
def event(self, ev, x, y):
|
||||
|
||||
# We deactivate on an event.
|
||||
self.activated = False
|
||||
|
||||
inside = False
|
||||
|
||||
width, height = self.window_size
|
||||
@@ -343,12 +372,15 @@ class Button(renpy.display.layout.Window):
|
||||
if self.hovered:
|
||||
self.hovered()
|
||||
|
||||
renpy.sound.play(self.style.hover_sound)
|
||||
renpy.display.audio.play(self.style.hover_sound)
|
||||
|
||||
|
||||
if map_event(ev, "button_select"):
|
||||
if inside and self.clicked:
|
||||
renpy.sound.play(self.style.activate_sound)
|
||||
renpy.display.audio.play(self.style.activate_sound)
|
||||
|
||||
self.activated = True
|
||||
renpy.display.render.redraw(self, 0)
|
||||
|
||||
rv = self.clicked()
|
||||
|
||||
@@ -356,6 +388,7 @@ class Button(renpy.display.layout.Window):
|
||||
return rv
|
||||
else:
|
||||
raise renpy.display.core.IgnoreEvent()
|
||||
|
||||
|
||||
|
||||
return super(Button, self).event(ev, x, y)
|
||||
@@ -363,10 +396,10 @@ class Button(renpy.display.layout.Window):
|
||||
# Reimplementation of the TextButton widget as a Button and a Text
|
||||
# widget.
|
||||
def TextButton(text, style='button', text_style='button_text',
|
||||
clicked=None):
|
||||
clicked=None, **properties):
|
||||
|
||||
text = renpy.display.text.Text(text, style=text_style)
|
||||
return Button(text, style=style, clicked=clicked)
|
||||
return Button(text, style=style, clicked=clicked, **properties)
|
||||
|
||||
|
||||
|
||||
@@ -410,7 +443,7 @@ class Input(renpy.display.text.Text):
|
||||
self.content = self.content[:-1]
|
||||
|
||||
self.set_text(self.content + "_")
|
||||
renpy.game.interface.redraw(0)
|
||||
renpy.display.render.redraw(self, 0)
|
||||
|
||||
|
||||
elif map_event(ev, "input_enter"):
|
||||
@@ -426,7 +459,9 @@ class Input(renpy.display.text.Text):
|
||||
self.content += ev.unicode
|
||||
|
||||
self.set_text(self.content + "_")
|
||||
renpy.game.interface.redraw(0)
|
||||
renpy.display.render.redraw(self, 0)
|
||||
|
||||
raise renpy.display.core.IgnoreEvent()
|
||||
|
||||
|
||||
class Bar(renpy.display.core.Displayable):
|
||||
@@ -461,7 +496,7 @@ class Bar(renpy.display.core.Displayable):
|
||||
if not (0 <= x < self.width and 0 <= y <= self.height):
|
||||
return
|
||||
|
||||
print x, y
|
||||
# print x, y
|
||||
|
||||
lgutter = self.style.left_gutter
|
||||
rgutter = self.style.right_gutter
|
||||
@@ -508,10 +543,10 @@ class Bar(renpy.display.core.Displayable):
|
||||
left_width = barwidth * self.value // self.range
|
||||
right_width = barwidth - left_width
|
||||
|
||||
rv = renpy.display.surface.Surface(width, height)
|
||||
rv = renpy.display.render.Render(width, height)
|
||||
|
||||
lsurf = self.style.left_bar.render(left_width, height, st)
|
||||
rsurf = self.style.right_bar.render(right_width, height, st)
|
||||
lsurf = render(self.style.left_bar, left_width, height, st)
|
||||
rsurf = render(self.style.right_bar, right_width, height, st)
|
||||
|
||||
rv.blit(lsurf, (lgutter, 0))
|
||||
rv.blit(rsurf, (lgutter + left_width, 0))
|
||||
@@ -538,16 +573,16 @@ class Conditional(renpy.display.layout.Container):
|
||||
|
||||
def render(self, width, height, st):
|
||||
if self.state:
|
||||
return self.child.render(width, height, st)
|
||||
return render(self.child, width, height, st)
|
||||
else:
|
||||
return self.null.render(width, height, st)
|
||||
return render(self.null, width, height, st)
|
||||
|
||||
def event(self, ev, x, y):
|
||||
|
||||
state = eval(self.condition, renpy.game.store)
|
||||
|
||||
if state != self.state:
|
||||
renpy.game.interface.redraw(0)
|
||||
renpy.display.render.redraw(self, 0)
|
||||
|
||||
self.state = state
|
||||
|
||||
|
||||
+272
-203
@@ -2,6 +2,7 @@
|
||||
# window.
|
||||
|
||||
import renpy
|
||||
from renpy.display.render import render
|
||||
|
||||
import pygame
|
||||
from pygame.constants import *
|
||||
@@ -34,6 +35,7 @@ class Displayable(renpy.object.Object):
|
||||
|
||||
def __init__(self):
|
||||
self.style = None
|
||||
self.style_prefix = None
|
||||
|
||||
def set_style_prefix(self, prefix):
|
||||
"""
|
||||
@@ -41,9 +43,15 @@ class Displayable(renpy.object.Object):
|
||||
widgets, if any.
|
||||
"""
|
||||
|
||||
if prefix == self.style_prefix:
|
||||
return
|
||||
|
||||
if self.style:
|
||||
self.style.set_prefix(prefix)
|
||||
|
||||
self.style_prefix = prefix
|
||||
renpy.display.render.redraw(self, 0)
|
||||
|
||||
def parameterize(self, name, parameters):
|
||||
"""
|
||||
Called to parameterize this. By default, we don't take any
|
||||
@@ -62,10 +70,7 @@ class Displayable(renpy.object.Object):
|
||||
and height parameters, which give the largest width and height
|
||||
that this drawable can be drawn to without overflowing some
|
||||
bounding box. It's also given two times. It returns a Surface
|
||||
that is the current image of this drawable. If the image will
|
||||
be need to be redrawn at some time in the future, call
|
||||
game.interface.redraw with the time with the delay until the
|
||||
next redraw.
|
||||
that is the current image of this drawable.
|
||||
|
||||
@param shown_time: The time since we first started showing this
|
||||
drawable, a float in seconds.
|
||||
@@ -171,8 +176,7 @@ class Displayable(renpy.object.Object):
|
||||
dest.blit(surf, (xoff, yoff))
|
||||
|
||||
return xoff, yoff
|
||||
|
||||
|
||||
|
||||
|
||||
class SceneLists(object):
|
||||
"""
|
||||
@@ -194,19 +198,26 @@ class SceneLists(object):
|
||||
"""
|
||||
|
||||
def __init__(self, oldsl=None):
|
||||
|
||||
|
||||
self.layers = { }
|
||||
|
||||
if oldsl:
|
||||
self.master = oldsl.master[:]
|
||||
|
||||
for i in renpy.config.layers:
|
||||
self.layers[i] = oldsl.layers[i][:]
|
||||
|
||||
self.replace_transient()
|
||||
self.overlay = oldsl.overlay[:]
|
||||
|
||||
self.music = oldsl.music
|
||||
self.sticky_positions = oldsl.sticky_positions.copy()
|
||||
|
||||
self.movie = oldsl.movie
|
||||
|
||||
else:
|
||||
self.master = [ ]
|
||||
self.transient = [ ]
|
||||
self.overlay = [ ]
|
||||
for i in renpy.config.layers:
|
||||
self.layers[i] = [ ]
|
||||
|
||||
self.music = None
|
||||
self.movie = None
|
||||
self.sticky_positions = { }
|
||||
|
||||
def rollback_copy(self):
|
||||
@@ -215,11 +226,16 @@ class SceneLists(object):
|
||||
which is a 2-level copy.
|
||||
"""
|
||||
|
||||
rv = SceneLists()
|
||||
rv.master = self.master[:]
|
||||
rv.transient = self.transient[:]
|
||||
rv = SceneLists(self)
|
||||
|
||||
rv.music = self.music
|
||||
for i in renpy.config.overlay_layers:
|
||||
rv.layers[i] = [ ]
|
||||
|
||||
# rv.master = self.master[:]
|
||||
# rv.transient = self.transient[:]
|
||||
|
||||
# rv.music = self.music
|
||||
# rv.movie = self.movie
|
||||
|
||||
return rv
|
||||
|
||||
@@ -232,27 +248,28 @@ class SceneLists(object):
|
||||
elements.
|
||||
"""
|
||||
|
||||
self.transient = [ ]
|
||||
for i in renpy.config.transient_layers:
|
||||
self.layers[i] = [ ]
|
||||
|
||||
def add(self, listname, thing, key=None):
|
||||
def add(self, layer, thing, key=None):
|
||||
"""
|
||||
This is called to add something to a display list. Listname is
|
||||
the name of the displaylist that we need to add the thing to,
|
||||
This is called to add something to a layer. Layer is
|
||||
the name of the layer that we need to add the thing to,
|
||||
one of 'master' or 'transient'. Key is an optional key.
|
||||
|
||||
If key is provided, and there exists something in the selected
|
||||
displaylist with the given key, that entry from the displaylist
|
||||
layer with the given key, that entry from the layer
|
||||
is replaced with the supplied displayable, preserving the
|
||||
time it was first displayed.
|
||||
|
||||
Otherwise, the displayable is added to the end of the list, and
|
||||
Otherwise, the displayable is added to the end of the layer, and
|
||||
the time it was first displayed is set to the current time.
|
||||
"""
|
||||
|
||||
l = getattr(self, listname, None)
|
||||
if layer not in self.layers:
|
||||
raise Exception("Trying to add something to non-existent layer '%s'." % layer)
|
||||
|
||||
if l is None:
|
||||
raise Exception("Trying to add something to non-existent display list '%s'." % listname)
|
||||
l = self.layers[layer]
|
||||
|
||||
if key is not None:
|
||||
for index, (k, t, d) in enumerate(l):
|
||||
@@ -267,71 +284,33 @@ class SceneLists(object):
|
||||
|
||||
l.append((key, time.time(), thing))
|
||||
|
||||
def remove(self, listname, thing):
|
||||
def remove(self, layer, thing):
|
||||
"""
|
||||
This is either a key or a displayable. This iterates through the
|
||||
named display list, searching for entries matching the thing.
|
||||
named layer, searching for entries matching the thing.
|
||||
When they are found, they are removed from the displaylist.
|
||||
|
||||
It's not an error to remove something that isn't in the list in
|
||||
It's not an error to remove something that isn't in the layer in
|
||||
the first place.
|
||||
"""
|
||||
|
||||
l = getattr(self, listname)
|
||||
if layer not in self.layers:
|
||||
raise Exception("Trying to remove something from non-existent layer '%s'." % layer)
|
||||
|
||||
l = self.layers[layer]
|
||||
l = [ (k, t, d) for k, t, d in l if k != thing if d is not thing ]
|
||||
|
||||
setattr(self, listname, l)
|
||||
self.layers[layer] = l
|
||||
|
||||
def clear(self, listname):
|
||||
def clear(self, layer):
|
||||
"""
|
||||
Resets the given listname to either the empty list, or to the
|
||||
contents of the empty scenelist if reset_to_empty is True.
|
||||
|
||||
@param: The name of the list to clear. One of the strings
|
||||
'master' or 'transient'.
|
||||
Clears the named layer, making it empty.
|
||||
"""
|
||||
|
||||
l = getattr(self, listname, None)
|
||||
|
||||
if l is None:
|
||||
raise Exception("Trying to clear non-existent display list '%s'." % listname)
|
||||
if layer not in self.layers is None:
|
||||
raise Exception("Trying to clear non-existent layer '%s'." % layer)
|
||||
|
||||
nl = [ ]
|
||||
|
||||
setattr(self, listname, nl)
|
||||
|
||||
def render_scene_list(sl, width, height):
|
||||
"""
|
||||
This renders the scene list sl, and returns a rendered
|
||||
renpy Surface containing the rendered scene list.
|
||||
|
||||
@returns: The surface, and the offset of each member of the
|
||||
list relative to the surface.
|
||||
"""
|
||||
|
||||
t = time.time()
|
||||
|
||||
surftree = renpy.display.surface.Surface(width, height)
|
||||
offsets = [ ]
|
||||
|
||||
if renpy.config.background:
|
||||
surftree.fill(renpy.config.background)
|
||||
|
||||
for key, base_start_time, d in sl:
|
||||
|
||||
surf = d.render(width, height, t - base_start_time)
|
||||
|
||||
if not surf:
|
||||
offsets.append((0, 0))
|
||||
continue
|
||||
|
||||
# Place the surface.
|
||||
offset = d.place(surftree, 0, 0, width, height, surf)
|
||||
|
||||
offsets.append(offset)
|
||||
|
||||
return surftree, offsets
|
||||
self.layers[layer] = [ ]
|
||||
|
||||
|
||||
class Display(object):
|
||||
@@ -353,12 +332,18 @@ class Display(object):
|
||||
|
||||
@ivar mouse_location: The mouse location the last time it was
|
||||
drawn, or None if it wasn't drawn the last time around.
|
||||
|
||||
@ivar full_redraw: Force a full redraw.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self):
|
||||
|
||||
renpy.sound.init()
|
||||
|
||||
# Ensure that we kill off the movie when changing screen res.
|
||||
renpy.display.video.movie_stop(clear=False)
|
||||
|
||||
renpy.display.audio.pre_init()
|
||||
pygame.init()
|
||||
|
||||
self.fullscreen = renpy.game.preferences.fullscreen
|
||||
@@ -398,8 +383,11 @@ class Display(object):
|
||||
pygame.mouse.set_visible(True)
|
||||
|
||||
self.mouse_location = None
|
||||
self.suppress_mouse = False
|
||||
|
||||
def draw_mouse(self):
|
||||
self.full_redraw = True
|
||||
|
||||
def draw_mouse(self, show_mouse=True):
|
||||
"""
|
||||
This draws the mouse to the screen, if necessary. It uses the
|
||||
buffer to minimize the amount of the screen that needs to be
|
||||
@@ -409,34 +397,35 @@ class Display(object):
|
||||
if not self.mouse:
|
||||
return
|
||||
|
||||
if self.suppress_mouse:
|
||||
return
|
||||
|
||||
mw, mh = self.mouse.get_size()
|
||||
pos = pygame.mouse.get_pos()
|
||||
|
||||
if not pygame.mouse.get_focused():
|
||||
pos = None
|
||||
|
||||
flip = False
|
||||
if not show_mouse:
|
||||
pos = None
|
||||
|
||||
updates = [ ]
|
||||
|
||||
if self.mouse_location and self.mouse_location != pos:
|
||||
ox, oy = self.mouse_location
|
||||
self.window.blit(self.buffer, (ox, oy), (ox, oy, mw, mh))
|
||||
flip = True
|
||||
updates.append((ox, oy, mw, mh))
|
||||
|
||||
if pos and (pos != self.mouse_location):
|
||||
self.window.blit(self.mouse, pos)
|
||||
flip = True
|
||||
updates.append(pos + (mw, mh))
|
||||
|
||||
self.mouse_location = pos
|
||||
|
||||
|
||||
if flip:
|
||||
pygame.display.flip()
|
||||
|
||||
|
||||
pygame.display.update(updates)
|
||||
|
||||
|
||||
def show(self, transient):
|
||||
def show(self, root_widget, suppress_blit):
|
||||
"""
|
||||
Draws the current transient screen list to the screen.
|
||||
|
||||
@@ -444,23 +433,34 @@ class Display(object):
|
||||
relative to the screen.
|
||||
"""
|
||||
|
||||
surftree, rv = render_scene_list(transient,
|
||||
renpy.config.screen_width,
|
||||
renpy.config.screen_height)
|
||||
surftree = renpy.display.render.render_screen(
|
||||
root_widget,
|
||||
renpy.config.screen_width,
|
||||
renpy.config.screen_height,
|
||||
0)
|
||||
|
||||
if not suppress_blit:
|
||||
|
||||
if self.mouse:
|
||||
self.draw_mouse(False)
|
||||
|
||||
damage = renpy.display.render.screen_blit(surftree, self.full_redraw)
|
||||
self.full_redraw = False
|
||||
|
||||
if self.mouse:
|
||||
if damage:
|
||||
self.buffer.blit(self.window, damage, damage)
|
||||
self.draw_mouse(True)
|
||||
|
||||
if damage:
|
||||
pygame.display.update(damage)
|
||||
|
||||
else:
|
||||
self.full_redraw = True
|
||||
|
||||
self.suppress_mouse = suppress_blit
|
||||
|
||||
# self.window.set_clip((0, 0, 800, 300))
|
||||
|
||||
surftree.blit_to(self.window, 0, 0)
|
||||
|
||||
if self.mouse:
|
||||
self.buffer.blit(self.window, (0, 0))
|
||||
# We don't need to undraw the mouse.
|
||||
self.mouse_location = None
|
||||
|
||||
pygame.display.flip()
|
||||
|
||||
return rv
|
||||
|
||||
def save_screenshot(self, filename):
|
||||
"""
|
||||
Saves a full-size screenshot in the given filename.
|
||||
@@ -491,38 +491,40 @@ class Interface(object):
|
||||
|
||||
@ivar display: The display that we used to display the screen.
|
||||
|
||||
@ivar needs_redraw: True if we need a redraw now.
|
||||
|
||||
@ivar profile_time: The time of the last profiling.
|
||||
|
||||
@ivar screenshot: A screenshot, or None if no screenshot has been
|
||||
taken.
|
||||
|
||||
@ivar old_scene: The last thing that was displayed to the screen, not
|
||||
counting overlays and things like that.
|
||||
counting underlays.
|
||||
|
||||
@ivar transition: If not None, the transition to be applied for the
|
||||
next interaction.
|
||||
@ivar transition: A map from layer name to the transition that will
|
||||
be applied the next time interact restarts.
|
||||
|
||||
@ivar supress_transition: If True, then the next transition will not
|
||||
@ivar suppress_transition: If True, then the next transition will not
|
||||
happen.
|
||||
|
||||
@ivar quick_quit: If true, a click on the delete button will
|
||||
cause an immediate quit.
|
||||
|
||||
@ivar force_redraw: If True, a redraw is forced.
|
||||
|
||||
@ivar restart_interaction: If True, the current interaction will
|
||||
be restarted.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.display = Display()
|
||||
self.needs_redraw = False
|
||||
self.profile_time = time.time()
|
||||
self.screenshot = None
|
||||
self.redraw_time = 0.0
|
||||
self.old_scene = [ ]
|
||||
self.transition = None
|
||||
self.supress_transition = False
|
||||
self.old_scene = None
|
||||
self.transition = { }
|
||||
self.suppress_transition = False
|
||||
self.quick_quit = False
|
||||
self.force_redraw = False
|
||||
self.restart_interaction = False
|
||||
|
||||
def take_screenshot(self, scale):
|
||||
"""
|
||||
@@ -549,37 +551,32 @@ class Interface(object):
|
||||
|
||||
self.screenshot = None
|
||||
|
||||
|
||||
def redraw(self, delay=0.0):
|
||||
"""
|
||||
Called to indicate that the screen has changed in some way, and
|
||||
needs to be redrawn.
|
||||
|
||||
@param delay: The time from now at which the redraw needs to
|
||||
be performed, in fractional seconds.
|
||||
"""
|
||||
|
||||
if delay == 0.0:
|
||||
self.needs_redraw = True
|
||||
else:
|
||||
self.redraw_time = min(self.redraw_time, time.time() + delay)
|
||||
|
||||
def with_none(self):
|
||||
"""
|
||||
Implements the with None command, which sets the scene we will
|
||||
be transitioning from.
|
||||
"""
|
||||
|
||||
|
||||
scene_lists = renpy.game.context().scene_lists
|
||||
self.old_scene = scene_lists.master[:]
|
||||
|
||||
def set_transition(self, transition):
|
||||
# We don't want the overlay to be clear here.
|
||||
|
||||
if not renpy.config.overlay_during_wait:
|
||||
for i in renpy.config.overlay_layers:
|
||||
scene_lists.clear(i)
|
||||
|
||||
self.old_scene = self.compute_scene(scene_lists)
|
||||
|
||||
|
||||
# self.old_scene.append_scene_list(scene_lists.master)
|
||||
|
||||
def set_transition(self, transition, layer=None):
|
||||
"""
|
||||
Sets the transition that will be performed as part of the next
|
||||
interaction.
|
||||
"""
|
||||
|
||||
self.transition = transition
|
||||
self.transition[layer] = transition
|
||||
|
||||
def event_wait(self):
|
||||
"""
|
||||
@@ -589,20 +586,61 @@ class Interface(object):
|
||||
|
||||
return pygame.event.wait()
|
||||
|
||||
def interact(self, transient=None, show_mouse=True,
|
||||
|
||||
def compute_scene(self, scene_lists):
|
||||
"""
|
||||
This converts scene lists into a dictionary mapping layer
|
||||
name to a Fixed containing that layer. None is mapped
|
||||
to a fixed containing all of the layers.
|
||||
"""
|
||||
|
||||
rv = { }
|
||||
|
||||
root = renpy.display.layout.Fixed()
|
||||
|
||||
for layer in renpy.config.layers:
|
||||
f = renpy.display.layout.Fixed()
|
||||
f.append_scene_list(scene_lists.layers[layer])
|
||||
|
||||
rv[layer] = f
|
||||
root.add(f)
|
||||
|
||||
rv[None] = root
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
def interact(self, **kwargs):
|
||||
"""
|
||||
This handles an interaction, restarting it if necessary. All of the
|
||||
keyword arguments are passed off to interact_core.
|
||||
"""
|
||||
|
||||
repeat = True
|
||||
|
||||
while repeat:
|
||||
repeat, rv = self.interact_core(**kwargs)
|
||||
|
||||
# Clean out transient stuff at the end of an interaction.
|
||||
scene_lists = renpy.game.context().scene_lists
|
||||
scene_lists.replace_transient()
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
def interact_core(self,
|
||||
show_mouse=True,
|
||||
trans_pause=False,
|
||||
suppress_overlay=False,
|
||||
suppress_underlay=False,
|
||||
):
|
||||
|
||||
"""
|
||||
This handles one cycle of displaying an image to the user,
|
||||
and then responding to user input.
|
||||
|
||||
@param transient: If given, a replacement list of transient
|
||||
things to show.
|
||||
|
||||
@param show_mouse: Should the mouse be shown during this
|
||||
interaction? Only advisory.
|
||||
interaction? Only advisory, and usually doesn't work.
|
||||
|
||||
@param trans_pause: If given, we must have a transition. Should we
|
||||
add a pause behavior during the transition?
|
||||
@@ -611,13 +649,19 @@ class Interface(object):
|
||||
@param suppress_underlay: This suppresses the display of the underlay.
|
||||
"""
|
||||
|
||||
self.suppress_transition |= renpy.config.skipping
|
||||
|
||||
## Safety condition, prevents deadlocks.
|
||||
if trans_pause:
|
||||
if not self.transition:
|
||||
return None
|
||||
if self.supress_transition:
|
||||
return None
|
||||
return False, None
|
||||
if self.suppress_transition:
|
||||
return False, None
|
||||
|
||||
# We just restarted.
|
||||
self.restart_interaction = False
|
||||
|
||||
# frames = 0
|
||||
|
||||
## Expensive things we want to do before we pick the start_time.
|
||||
|
||||
@@ -642,64 +686,76 @@ class Interface(object):
|
||||
start_time = time.time()
|
||||
scene_lists = renpy.game.context().scene_lists
|
||||
|
||||
# Compute the overlay, by calling the overlay functions.
|
||||
overlay = [ ]
|
||||
|
||||
# Figure out what the overlay layer should look like.
|
||||
for i in renpy.config.overlay_layers:
|
||||
scene_lists.clear(i)
|
||||
|
||||
renpy.ui.layer("overlay")
|
||||
|
||||
if not suppress_overlay:
|
||||
for i in renpy.config.overlay_functions:
|
||||
i()
|
||||
|
||||
overlaid = i()
|
||||
|
||||
if overlaid:
|
||||
for j in overlaid:
|
||||
overlay.append((None, 0, j))
|
||||
renpy.ui.close()
|
||||
|
||||
# Figure out the underlay layer.
|
||||
if not suppress_underlay:
|
||||
underlay = [ ( None, 0, i) for i in renpy.config.underlay ]
|
||||
else:
|
||||
underlay = [ ]
|
||||
|
||||
# Set up the transient scene list.
|
||||
if transient:
|
||||
transient = scene_lists.transient + [ (None, start_time, i) for i in transient ]
|
||||
else:
|
||||
transient = scene_lists.transient
|
||||
# The root widget of everything that is displayed on the screen.
|
||||
root_widget = renpy.display.layout.Fixed()
|
||||
root_widget.append_scene_list(underlay)
|
||||
|
||||
|
||||
# Figure out the display list.
|
||||
current_scene = scene_lists.master + transient + overlay
|
||||
# Figure out the scene. (All of the layers, and the root.)
|
||||
scene = self.compute_scene(scene_lists)
|
||||
|
||||
if self.transition and not self.supress_transition:
|
||||
trans = self.transition(self.old_scene, current_scene)
|
||||
# The root widget of all of the layers.
|
||||
layers_root = renpy.display.layout.Fixed()
|
||||
|
||||
transition_scene = [ (None, start_time, trans) ]
|
||||
# Add layers (perhaps with transitions) to the layers root.
|
||||
for layer in renpy.config.layers:
|
||||
if layer in self.transition and self.old_scene and not self.suppress_transition:
|
||||
|
||||
trans = self.transition[layer](old_widget=self.old_scene[layer],
|
||||
new_widget=scene[layer])
|
||||
layers_root.add(trans, start_time)
|
||||
else:
|
||||
layers_root.add(scene[layer], start_time)
|
||||
|
||||
# Add layers_root to root_widget, perhaps through a transition.
|
||||
if None in self.transition and self.old_scene and not self.suppress_transition:
|
||||
trans = self.transition[None](old_widget=self.old_scene[None],
|
||||
new_widget=layers_root)
|
||||
|
||||
root_widget.add(trans, start_time)
|
||||
|
||||
if trans_pause:
|
||||
sb = renpy.display.behavior.SayBehavior(delay=trans.delay)
|
||||
transition_scene.append((None, start_time, sb))
|
||||
sb = renpy.display.behavior.SayBehavior()
|
||||
root_widget.add(sb, start_time)
|
||||
|
||||
pb = renpy.display.behavior.PauseBehavior(trans.delay)
|
||||
root_widget.add(pb, start_time)
|
||||
|
||||
else:
|
||||
transition_scene = current_scene
|
||||
root_widget.add(layers_root, start_time)
|
||||
|
||||
self.transition = None
|
||||
self.supress_transition = False
|
||||
|
||||
# The list of things to be displayed.
|
||||
display_list = underlay + transition_scene
|
||||
# Now, update various things regarding scenes and transitions,
|
||||
# so we are ready for a new interaction or a restart.
|
||||
self.old_scene = scene
|
||||
self.transition = { }
|
||||
self.suppress_transition = False
|
||||
|
||||
|
||||
# This list of things recieving events.
|
||||
event_list = display_list[:]
|
||||
# Okay, from here on we now have a single root widget (root_widget),
|
||||
# which we will try to show to the user.
|
||||
|
||||
# Compute the reversed list, which is in the right order
|
||||
# for handling events on.
|
||||
event_list.reverse()
|
||||
|
||||
# Redraw the screen during every interaction.
|
||||
self.needs_redraw = True
|
||||
|
||||
# This list of offsets will be filled in before we need
|
||||
# it. It's kept in reverse order of the transient list
|
||||
# (The same order as rev_transient.)
|
||||
offsets = [ ]
|
||||
# Redraw the screen.
|
||||
renpy.display.render.process_redraws()
|
||||
needs_redraw = True
|
||||
|
||||
# Post an event that moves us to the current mouse position.
|
||||
pygame.event.post(pygame.event.Event(MOUSEMOTION,
|
||||
@@ -713,18 +769,29 @@ class Interface(object):
|
||||
|
||||
while rv is None:
|
||||
|
||||
# Check for a change in fullscreen preference.
|
||||
if self.display.fullscreen != renpy.game.preferences.fullscreen:
|
||||
self.display = Display()
|
||||
self.needs_redraw = True
|
||||
needs_redraw = True
|
||||
|
||||
if self.needs_redraw:
|
||||
self.needs_redraw = False
|
||||
# Check for a forced redraw.
|
||||
if self.force_redraw:
|
||||
needs_redraw = True
|
||||
self.force_redraw = False
|
||||
|
||||
# Redraw the screen.
|
||||
if needs_redraw:
|
||||
needs_redraw = False
|
||||
|
||||
# If we have a movie, start showing it.
|
||||
suppress_blit = renpy.display.video.interact()
|
||||
|
||||
# Draw the screen.
|
||||
draw_start = time.time()
|
||||
self.redraw_time = draw_start + 365.25 * 86400.0
|
||||
|
||||
offsets = self.display.show(display_list)
|
||||
offsets.reverse()
|
||||
self.display.show(root_widget, suppress_blit)
|
||||
|
||||
# frames = frames + 1
|
||||
|
||||
# If profiling is enabled, report the profile time.
|
||||
if renpy.config.profile:
|
||||
@@ -735,11 +802,13 @@ class Interface(object):
|
||||
# Draw the mouse, if it needs drawing.
|
||||
if show_mouse:
|
||||
self.display.draw_mouse()
|
||||
|
||||
|
||||
# Determine if we need a redraw.
|
||||
needs_redraw = renpy.display.render.process_redraws()
|
||||
|
||||
# If we need to redraw again, do it if we don't have an
|
||||
# event going on.
|
||||
if self.needs_redraw and not pygame.event.peek():
|
||||
if needs_redraw and not pygame.event.peek():
|
||||
continue
|
||||
|
||||
# While we have nothing to do, preload images.
|
||||
@@ -762,7 +831,10 @@ class Interface(object):
|
||||
duration=(time.time() - start_time))
|
||||
|
||||
# Update the playing music, if necessary.
|
||||
renpy.music.restore()
|
||||
|
||||
# This needs to be here so that we eventually start a
|
||||
# new song at the end of a fadeout.
|
||||
renpy.display.audio.restore_music()
|
||||
|
||||
# Handle skipping.
|
||||
renpy.display.behavior.skipping(ev)
|
||||
@@ -783,39 +855,36 @@ class Interface(object):
|
||||
if len(evs):
|
||||
ev = evs[-1]
|
||||
|
||||
|
||||
# x, y = getattr(ev, 'pos', (0, 0))
|
||||
|
||||
x, y = pygame.mouse.get_pos()
|
||||
|
||||
for (k, t, d), (xo, yo) in zip(event_list, offsets):
|
||||
rv = d.event(ev, x - xo, y - yo)
|
||||
rv = root_widget.event(ev, x, y)
|
||||
|
||||
if rv is not None:
|
||||
break
|
||||
if rv is not None:
|
||||
break
|
||||
|
||||
except IgnoreEvent:
|
||||
pass
|
||||
|
||||
if time.time() > self.redraw_time:
|
||||
self.needs_redraw = True
|
||||
# Check again after handling the event.
|
||||
needs_redraw |= renpy.display.render.process_redraws()
|
||||
|
||||
if self.restart_interaction:
|
||||
return True, None
|
||||
|
||||
# But wait, there's more! The finally block runs some cleanup
|
||||
# after this.
|
||||
return rv
|
||||
return False, rv
|
||||
|
||||
finally:
|
||||
|
||||
# pygame.time.set_timer(KEYREPEATEVENT, 0)
|
||||
pygame.time.set_timer(DISPLAYTIME, 0)
|
||||
scene_lists.replace_transient()
|
||||
|
||||
# Clear up the transitions.
|
||||
self.old_scene = current_scene
|
||||
self.transition = None
|
||||
self.supress_transition = False
|
||||
# Restart the old interaction, which also causes a
|
||||
# redraw if needed.
|
||||
self.restart_interaction = True
|
||||
|
||||
# Redraw the old scene, if any.
|
||||
self.redraw(0)
|
||||
|
||||
# print "It took", frames, "frames."
|
||||
|
||||
|
||||
+40
-13
@@ -1,4 +1,6 @@
|
||||
import renpy
|
||||
from renpy.display.render import render
|
||||
|
||||
import pygame
|
||||
from pygame.constants import *
|
||||
|
||||
@@ -50,8 +52,12 @@ class ImageCache(object):
|
||||
return rv
|
||||
|
||||
im = pygame.image.load(renpy.loader.load(fn), fn)
|
||||
im = im.convert_alpha()
|
||||
|
||||
if im.get_flags() & SRCALPHA:
|
||||
im = im.convert_alpha()
|
||||
else:
|
||||
im = im.convert()
|
||||
|
||||
return im
|
||||
|
||||
|
||||
@@ -67,7 +73,7 @@ class ImageCache(object):
|
||||
|
||||
# iw, ih = im.get_size()
|
||||
|
||||
# surf = renpy.display.surface.Surface(iw, ih)
|
||||
# surf = renpy.display.render.Render(iw, ih)
|
||||
# surf.blit(im, (0, 0))
|
||||
|
||||
im = self.really_load_image(fn)
|
||||
@@ -176,6 +182,8 @@ class Image(renpy.display.core.Displayable):
|
||||
other images will be aligned with the upper-left corner of the
|
||||
image.
|
||||
"""
|
||||
|
||||
super(Image, self).__init__()
|
||||
|
||||
self.filename = filename
|
||||
self.style = renpy.style.Style(style, properties)
|
||||
@@ -183,7 +191,7 @@ class Image(renpy.display.core.Displayable):
|
||||
def render(self, w, h, st):
|
||||
im = cache.load_image(self.filename)
|
||||
w, h = im.get_size()
|
||||
rv = renpy.display.surface.Surface(w, h)
|
||||
rv = renpy.display.render.Render(w, h)
|
||||
rv.blit(im, (0, 0))
|
||||
return rv
|
||||
|
||||
@@ -200,6 +208,9 @@ class UncachedImage(renpy.display.core.Displayable):
|
||||
|
||||
def __init__(self, file, hint=None, scale=None, style='image_placement',
|
||||
**properties):
|
||||
|
||||
super(UncachedImage, self).__init__()
|
||||
|
||||
self.surf = pygame.image.load(file, hint)
|
||||
|
||||
if scale:
|
||||
@@ -212,7 +223,7 @@ class UncachedImage(renpy.display.core.Displayable):
|
||||
|
||||
def render(self, w, h, st):
|
||||
sw, sh = self.surf.get_size()
|
||||
rv = renpy.display.surface.Surface(sw, sh)
|
||||
rv = renpy.display.render.Render(sw, sh)
|
||||
rv.blit(self.surf, (0, 0))
|
||||
|
||||
return rv
|
||||
@@ -236,6 +247,8 @@ class ImageReference(renpy.display.core.Displayable):
|
||||
nosave = [ 'target' ]
|
||||
|
||||
def __init__(self, name):
|
||||
super(ImageReference, self).__init__()
|
||||
|
||||
self.name = name
|
||||
|
||||
def find_target(self):
|
||||
@@ -280,7 +293,7 @@ class ImageReference(renpy.display.core.Displayable):
|
||||
if not hasattr(self, 'target'):
|
||||
self.find_target()
|
||||
|
||||
return self.target.render(width, height, st)
|
||||
return render(self.target, width, height, st)
|
||||
|
||||
def get_placement(self):
|
||||
if not hasattr(self, 'target'):
|
||||
@@ -301,11 +314,12 @@ class Solid(renpy.display.core.Displayable):
|
||||
@param color: An RGBA tuple, giving the color that the display will be filled with.
|
||||
"""
|
||||
|
||||
super(Solid, self).__init__()
|
||||
self.color = color
|
||||
|
||||
def render(self, width, height, st):
|
||||
|
||||
rv = renpy.display.surface.Surface(width, height)
|
||||
rv = renpy.display.render.Render(width, height)
|
||||
rv.fill(self.color)
|
||||
|
||||
return rv
|
||||
@@ -339,6 +353,8 @@ class Frame(renpy.display.core.Displayable):
|
||||
at. We detect this and avoid scaling if possible.
|
||||
"""
|
||||
|
||||
super(Frame, self).__init__()
|
||||
|
||||
self.filename = filename
|
||||
self.xborder = xborder
|
||||
self.yborder = yborder
|
||||
@@ -351,7 +367,7 @@ class Frame(renpy.display.core.Displayable):
|
||||
if self.cache.get_size() == (width, height):
|
||||
return self.cache
|
||||
|
||||
dest = renpy.display.surface.Surface(width, height)
|
||||
dest = renpy.display.render.Render(width, height)
|
||||
dw, dh = width, height
|
||||
|
||||
source = cache.load_image(self.filename)
|
||||
@@ -451,6 +467,8 @@ class Animation(renpy.display.core.Displayable):
|
||||
animation will restart after the final delay time.
|
||||
"""
|
||||
|
||||
super(Animation, self).__init__()
|
||||
|
||||
self.images = [ ]
|
||||
self.delays = [ ]
|
||||
|
||||
@@ -470,8 +488,15 @@ class Animation(renpy.display.core.Displayable):
|
||||
|
||||
for image, delay in zip(self.images, self.delays):
|
||||
if t < delay:
|
||||
renpy.game.interface.redraw(delay - t)
|
||||
return cache.load_image(image)
|
||||
renpy.display.render.redraw(self, delay - t)
|
||||
|
||||
im = cache.load_image(image)
|
||||
width, height = im.get_size()
|
||||
rv = renpy.display.render.Render(width, height)
|
||||
rv.blit(im, (0, 0))
|
||||
|
||||
return rv
|
||||
|
||||
else:
|
||||
t = t - delay
|
||||
|
||||
@@ -491,6 +516,8 @@ class ImageMap(renpy.display.core.Displayable):
|
||||
def __init__(self, ground, selected, hotspots, unselected=None,
|
||||
style='imagemap', **properties):
|
||||
|
||||
super(ImageMap, self).__init__()
|
||||
|
||||
self.ground = ground
|
||||
self.selected = selected
|
||||
self.hotspots = hotspots
|
||||
@@ -520,7 +547,7 @@ class ImageMap(renpy.display.core.Displayable):
|
||||
unselected = cache.load_image(self.unselected)
|
||||
|
||||
width, height = ground.get_size()
|
||||
rv = renpy.display.surface.Surface(width, height)
|
||||
rv = renpy.display.render.Render(width, height)
|
||||
rv.blit(ground, (0, 0))
|
||||
|
||||
for i, hotspot in enumerate(self.hotspots):
|
||||
@@ -551,17 +578,17 @@ class ImageMap(renpy.display.core.Displayable):
|
||||
|
||||
if old_active != active:
|
||||
self.active = active
|
||||
renpy.game.interface.redraw(0)
|
||||
renpy.display.render.redraw(self, 0)
|
||||
|
||||
if active is not None:
|
||||
renpy.sound.play(self.style.hover_sound)
|
||||
renpy.display.audio.play(self.style.hover_sound)
|
||||
|
||||
|
||||
if active is None:
|
||||
return None
|
||||
|
||||
if renpy.display.behavior.map_event(ev, "imagemap_select"):
|
||||
renpy.sound.play(self.style.activate_sound)
|
||||
renpy.display.audio.play(self.style.activate_sound)
|
||||
return result
|
||||
|
||||
return None
|
||||
|
||||
+198
-61
@@ -5,6 +5,19 @@ import pygame
|
||||
from pygame.constants import *
|
||||
|
||||
import renpy
|
||||
from renpy.display.render import render
|
||||
import time
|
||||
|
||||
def scale(num, base):
|
||||
"""
|
||||
If num is a float, multiplies it by base and returns that. Otherwise,
|
||||
returns num unchanged.
|
||||
"""
|
||||
|
||||
if isinstance(num, float):
|
||||
return num * base
|
||||
else:
|
||||
return num
|
||||
|
||||
class Null(renpy.display.core.Displayable):
|
||||
"""
|
||||
@@ -20,8 +33,11 @@ class Null(renpy.display.core.Displayable):
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
def get_placement(self):
|
||||
return self.style
|
||||
|
||||
def render(self, width, height, st):
|
||||
return renpy.display.surface.Surface(self.width, self.height)
|
||||
return renpy.display.render.Render(self.width, self.height)
|
||||
|
||||
|
||||
class Container(renpy.display.core.Displayable):
|
||||
@@ -73,7 +89,7 @@ class Container(renpy.display.core.Displayable):
|
||||
|
||||
def render(self, width, height, st):
|
||||
|
||||
rv = self.child.render(width, height, st)
|
||||
rv = render(self.child, width, height, st)
|
||||
self.offsets = [ (0, 0) ]
|
||||
self.sizes = [ rv.get_size() ]
|
||||
|
||||
@@ -83,7 +99,10 @@ class Container(renpy.display.core.Displayable):
|
||||
return self.child.get_placement()
|
||||
|
||||
def event(self, ev, x, y):
|
||||
for i, (xo, yo) in zip(self.children, self.offsets):
|
||||
children_offsets = zip(self.children, self.offsets)
|
||||
children_offsets.reverse()
|
||||
|
||||
for i, (xo, yo) in children_offsets:
|
||||
rv = i.event(ev, x - xo, y - yo)
|
||||
if rv is not None:
|
||||
return rv
|
||||
@@ -116,6 +135,67 @@ class Container(renpy.display.core.Displayable):
|
||||
for i in self.children:
|
||||
i.predict(callback)
|
||||
|
||||
class Fixed(Container):
|
||||
"""
|
||||
A container that lays out each of its children at fixed
|
||||
coordinates determined by the position style of the child. Each
|
||||
widget is given the whole area of this widget, and then placed
|
||||
within that area based on its position style.
|
||||
|
||||
The result of this layout is the size of the entire area allocated
|
||||
to it. So it's probably only viable for laying out a root window.
|
||||
|
||||
Fixed is used by the display core to render scene lists, and to
|
||||
pass them off to transitions.
|
||||
"""
|
||||
|
||||
def __init__(self, style='default', **properties):
|
||||
super(Fixed, self).__init__()
|
||||
self.style = renpy.style.Style(style, properties)
|
||||
self.times = [ ]
|
||||
|
||||
def add(self, widget, time=None):
|
||||
super(Fixed, self).add(widget)
|
||||
self.times.append(time)
|
||||
|
||||
def append_scene_list(self, l):
|
||||
for tag, time, d in l:
|
||||
self.add(d, time)
|
||||
|
||||
def get_widget_time_list(self):
|
||||
return zip(self.children, self.times)
|
||||
|
||||
def get_placement(self):
|
||||
return self.style
|
||||
|
||||
def render(self, width, height, st):
|
||||
|
||||
self.offsets = [ ]
|
||||
self.sizes = [ ]
|
||||
|
||||
rv = renpy.display.render.Render(width, height)
|
||||
|
||||
t = time.time()
|
||||
|
||||
for child, start in zip(self.children, self.times):
|
||||
|
||||
if start:
|
||||
newst = t - start
|
||||
else:
|
||||
newst = st
|
||||
|
||||
surf = render(child, width, height, newst)
|
||||
|
||||
if surf:
|
||||
self.sizes.append(surf.get_size())
|
||||
offset = child.place(rv, 0, 0, width, height, surf)
|
||||
self.offsets.append(offset)
|
||||
else:
|
||||
self.sizes.append((0, 0))
|
||||
self.offsets.append((0, 0))
|
||||
|
||||
return rv
|
||||
|
||||
class Position(Container):
|
||||
"""
|
||||
Controls the placement of a displayable on the screen, using
|
||||
@@ -141,7 +221,7 @@ class Position(Container):
|
||||
|
||||
def render(self, width, height, st):
|
||||
|
||||
surf = self.child.render(width, height, st)
|
||||
surf = render(self.child, width, height, st)
|
||||
cw, ch = surf.get_size()
|
||||
|
||||
self.offsets = [ (0, 0) ]
|
||||
@@ -152,7 +232,76 @@ class Position(Container):
|
||||
def get_placement(self):
|
||||
return self.style
|
||||
|
||||
class Grid(Container):
|
||||
"""
|
||||
A grid is a widget that evenly allocates space to its children.
|
||||
The child widgets should not be greedy, but should instead be
|
||||
widgets that only use part of the space available to them.
|
||||
"""
|
||||
|
||||
def __init__(self, cols, rows, padding=0,
|
||||
style='default', **properties):
|
||||
"""
|
||||
@param cols: The number of columns in this widget.
|
||||
|
||||
@params rows: The number of rows in this widget.
|
||||
"""
|
||||
|
||||
super(Grid, self).__init__()
|
||||
|
||||
self.style = renpy.style.Style(style, properties)
|
||||
|
||||
self.cols = cols
|
||||
self.rows = rows
|
||||
|
||||
self.padding = padding
|
||||
|
||||
def render(self, width, height, st):
|
||||
|
||||
# For convenience and speed.
|
||||
padding = self.padding
|
||||
cols = self.cols
|
||||
rows = self.rows
|
||||
|
||||
if len(self.children) != cols * rows:
|
||||
raise Exception("Grid not completely full.")
|
||||
|
||||
renders = [ render(i, width, height, st) for i in self.children ]
|
||||
self.sizes = [ i.get_size() for i in renders ]
|
||||
|
||||
cwidth = 0
|
||||
cheight = 0
|
||||
|
||||
for w, h in self.sizes:
|
||||
cwidth = max(cwidth, w)
|
||||
cheight = max(cheight, h)
|
||||
|
||||
if self.style.xfill:
|
||||
cwidth = (width - (cols - 1) * padding) / cols
|
||||
|
||||
if self.style.yfill:
|
||||
cheight = (height - (rows - 1) * padding) / rows
|
||||
|
||||
width = cwidth * cols + padding * (cols - 1)
|
||||
height = cheight * rows + padding * (rows - 1)
|
||||
|
||||
rv = renpy.display.render.Render(width, height)
|
||||
|
||||
self.offsets = [ ]
|
||||
|
||||
for y in range(0, rows):
|
||||
for x in range(0, cols):
|
||||
|
||||
child = self.children[ x + y * cols ]
|
||||
surf = renders[x + y * cols]
|
||||
|
||||
xpos = x * (cwidth + padding)
|
||||
ypos = y * (cheight + padding)
|
||||
|
||||
offset = child.place(rv, xpos, ypos, cwidth, cheight, surf)
|
||||
self.offsets.append(offset)
|
||||
|
||||
return rv
|
||||
|
||||
class HBox(Container):
|
||||
"""
|
||||
@@ -192,7 +341,7 @@ class HBox(Container):
|
||||
for i in self.children:
|
||||
|
||||
xoffsets.append(xo)
|
||||
surf = i.render(remwidth, height, st)
|
||||
surf = render(i, remwidth, height, st)
|
||||
|
||||
sw, sh = surf.get_size()
|
||||
|
||||
@@ -209,7 +358,7 @@ class HBox(Container):
|
||||
|
||||
width = xo - self.padding
|
||||
|
||||
rv = renpy.display.surface.Surface(width, myheight)
|
||||
rv = renpy.display.render.Render(width, myheight)
|
||||
|
||||
for surf, child, xo in zip(surfaces, self.children, xoffsets):
|
||||
sw, sh = surf.get_size()
|
||||
@@ -259,7 +408,7 @@ class VBox(Container):
|
||||
|
||||
yoffsets.append(yo)
|
||||
|
||||
surf = i.render(width, remheight, st)
|
||||
surf = render(i, width, remheight, st)
|
||||
|
||||
sw, sh = surf.get_size()
|
||||
|
||||
@@ -276,7 +425,7 @@ class VBox(Container):
|
||||
|
||||
height = yo - self.padding
|
||||
|
||||
rv = renpy.display.surface.Surface(mywidth, height)
|
||||
rv = renpy.display.render.Render(mywidth, height)
|
||||
|
||||
for surf, child, yo in zip(surfaces, self.children, yoffsets):
|
||||
|
||||
@@ -288,39 +437,6 @@ class VBox(Container):
|
||||
|
||||
return rv
|
||||
|
||||
class Fixed(Container):
|
||||
"""
|
||||
A container that lays out each of its children at fixed
|
||||
coordinates determined by the position style of the child. Each
|
||||
widget is given the whole area of this widget, and then placed
|
||||
within that area based on its position style.
|
||||
|
||||
The result of this layout is the size of the entire area allocated
|
||||
to it. So it's probably only viable for laying out a root window.
|
||||
"""
|
||||
|
||||
def __init__(self, style='default', **properties):
|
||||
super(Fixed, self).__init__()
|
||||
self.style = renpy.style.Style(style, properties)
|
||||
|
||||
def get_placement(self):
|
||||
return self.style
|
||||
|
||||
def render(self, width, height, st):
|
||||
|
||||
self.offsets = [ ]
|
||||
self.sizes = [ ]
|
||||
|
||||
rv = renpy.display.surface.Surface(width, height)
|
||||
|
||||
for child in self.children:
|
||||
surf = child.render(width, height, st)
|
||||
self.sizes.append(surf.get_size())
|
||||
|
||||
offset = child.place(rv, 0, 0, width, height, surf)
|
||||
self.offsets.append(offset)
|
||||
|
||||
return rv
|
||||
|
||||
class Window(Container):
|
||||
"""
|
||||
@@ -353,43 +469,64 @@ class Window(Container):
|
||||
# save typing and screen space.
|
||||
style = self.style
|
||||
|
||||
xminimum = scale(style.xminimum, width)
|
||||
yminimum = scale(style.yminimum, height)
|
||||
|
||||
left_margin = scale(style.left_margin, width)
|
||||
left_padding = scale(style.left_padding, width)
|
||||
|
||||
right_margin = scale(style.right_margin, width)
|
||||
right_padding = scale(style.right_padding, width)
|
||||
|
||||
top_margin = scale(style.top_margin, height)
|
||||
top_padding = scale(style.top_padding, height)
|
||||
|
||||
bottom_margin = scale(style.bottom_margin, height)
|
||||
bottom_padding = scale(style.bottom_padding, height)
|
||||
|
||||
# c for combined.
|
||||
cxmargin = left_margin + right_margin
|
||||
cymargin = top_margin + bottom_margin
|
||||
|
||||
cxpadding = left_padding + right_padding
|
||||
cypadding = top_padding + bottom_padding
|
||||
|
||||
# Render the child.
|
||||
surf = self.child.render(width - 2 * style.xmargin - 2 * style.xpadding,
|
||||
height - 2 * style.ymargin - 2 * style.ypadding,
|
||||
st)
|
||||
surf = render(self.child,
|
||||
width - cxmargin - cxpadding,
|
||||
height - cymargin - cypadding,
|
||||
st)
|
||||
|
||||
sw, sh = surf.get_size()
|
||||
|
||||
# If we don't fill, shrink our size to fit.
|
||||
|
||||
if not style.xfill:
|
||||
width = max(2 * style.xmargin + 2 * style.xpadding + sw, style.xminimum)
|
||||
width = max(cxmargin + cxpadding + sw, xminimum)
|
||||
|
||||
if not style.yfill:
|
||||
height = max(2 * style.ymargin + 2 * style.ypadding + sh, style.yminimum)
|
||||
height = max(cymargin + cypadding + sh, yminimum)
|
||||
|
||||
rv = renpy.display.surface.Surface(width, height)
|
||||
rv = renpy.display.render.Render(width, height)
|
||||
|
||||
# Draw the background. The background should render at exactly the
|
||||
# requested size. (That is, be a Frame or a Solid).
|
||||
if style.background:
|
||||
bw = width - 2 * style.xmargin
|
||||
bh = height - 2 * style.ymargin
|
||||
bw = width - cxmargin
|
||||
bh = height - cymargin
|
||||
|
||||
back = style.background.render(bw, bh, st)
|
||||
back = render(style.background, bw, bh, st)
|
||||
|
||||
rv.blit(back,
|
||||
(style.xmargin, style.ymargin))
|
||||
(left_margin, top_margin))
|
||||
# (0, 0, bw, bh))
|
||||
|
||||
offsets = self.child.place(rv,
|
||||
style.xmargin + style.xpadding,
|
||||
style.ymargin + style.ypadding,
|
||||
width - 2 * (style.xmargin + style.xpadding),
|
||||
height - 2 * (style.ymargin + style.ypadding),
|
||||
left_margin + left_padding,
|
||||
top_margin + top_padding,
|
||||
width - cxmargin - cxpadding,
|
||||
height - cymargin - cypadding,
|
||||
surf)
|
||||
|
||||
|
||||
self.offsets = [ offsets ]
|
||||
self.sizes = [ (sw, sh) ]
|
||||
@@ -435,7 +572,7 @@ class Pan(Container):
|
||||
|
||||
def render(self, width, height, st):
|
||||
|
||||
surf = self.child.render(width, height, st)
|
||||
surf = render(self.child, width, height, st)
|
||||
self.sizes = [ surf.get_size() ]
|
||||
|
||||
x0, y0 = self.startpos
|
||||
@@ -455,7 +592,7 @@ class Pan(Container):
|
||||
|
||||
self.offsets = [ (-xo, -yo) ]
|
||||
|
||||
rv = renpy.display.surface.Surface(width, height)
|
||||
rv = renpy.display.render.Render(width, height)
|
||||
|
||||
# print surf
|
||||
|
||||
@@ -465,7 +602,7 @@ class Pan(Container):
|
||||
# rv.blit(surf, (-xo, -yo))
|
||||
|
||||
if st < self.time:
|
||||
renpy.game.interface.redraw(0)
|
||||
renpy.display.render.redraw(self, 0)
|
||||
|
||||
return rv
|
||||
|
||||
@@ -520,13 +657,13 @@ class Move(Container):
|
||||
|
||||
def render(self, width, height, st):
|
||||
self.st = st
|
||||
rv = self.child.render(width, height, st)
|
||||
rv = render(self.child, width, height, st)
|
||||
|
||||
self.sizes = [ rv.get_size() ]
|
||||
self.offsets = [ (0, 0) ]
|
||||
|
||||
if st < self.time:
|
||||
renpy.game.interface.redraw(0)
|
||||
renpy.display.render.redraw(self, 0)
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
@@ -0,0 +1,464 @@
|
||||
# Rules for renders: Every widget creates its own Render, even if all
|
||||
# it does is blit that render somewhere else.
|
||||
|
||||
import time
|
||||
import renpy
|
||||
import pygame
|
||||
from pygame.constants import *
|
||||
|
||||
# We only cache a single solid... but that should be enough to handle
|
||||
# some important cases, like button and window backgrounds.
|
||||
class SolidCache(object):
|
||||
|
||||
def __init__(self):
|
||||
self.size = None
|
||||
self.color = None
|
||||
self.cached = None
|
||||
|
||||
def create(self, size, color):
|
||||
if size == self.size and color == self.color:
|
||||
return self.cached
|
||||
|
||||
self.size = size
|
||||
self.color = color
|
||||
|
||||
if color[3] == 255:
|
||||
surf = pygame.Surface(size, 0, renpy.game.interface.display.window)
|
||||
else:
|
||||
surf = pygame.Surface(size, 0,
|
||||
renpy.game.interface.display.sample_surface)
|
||||
|
||||
surf.fill(color)
|
||||
|
||||
self.cached = surf
|
||||
return surf
|
||||
|
||||
solid_cache = SolidCache()
|
||||
|
||||
## One thing to realize when considering the safety of this is that
|
||||
## if any widget producing a render is redrawn, all instances of that
|
||||
## render are killed, and so the entire thing is redrawn.
|
||||
|
||||
# Renders of a given widget.
|
||||
widget_renders = { }
|
||||
|
||||
# Renders that have been used during the current rendering pass.
|
||||
new_renders = { }
|
||||
|
||||
# Renders that were used on the old rendering pass.
|
||||
old_renders = { }
|
||||
|
||||
# The set of surfaces that are mutable (that is, can change their
|
||||
# contents.)
|
||||
mutable_surfaces = { }
|
||||
|
||||
def render(widget, width, height, st):
|
||||
"""
|
||||
Renders a widget on the screen.
|
||||
"""
|
||||
|
||||
if (widget, width, height) in old_renders:
|
||||
rv = old_renders[widget, width, height]
|
||||
|
||||
assert widget in rv.widgets
|
||||
|
||||
rv.add_to_new_renders()
|
||||
|
||||
return rv
|
||||
|
||||
rv = widget.render(width, height, st)
|
||||
|
||||
rv.req_width = width
|
||||
rv.req_height = height
|
||||
|
||||
rv.widgets.append(widget)
|
||||
new_renders[widget, width, height] = rv
|
||||
widget_renders.setdefault(widget, []).append(rv)
|
||||
|
||||
return rv
|
||||
|
||||
# A list of (when, widget) for redraws.
|
||||
redraw_queue = [ ]
|
||||
|
||||
def process_redraws():
|
||||
"""
|
||||
Processes pending redraws. Returns True if a redraw is needed.
|
||||
"""
|
||||
|
||||
global redraw_queue
|
||||
redraw_queue.sort()
|
||||
|
||||
i = 0
|
||||
now = time.time()
|
||||
|
||||
for when, widget in redraw_queue:
|
||||
|
||||
if when > now:
|
||||
break
|
||||
|
||||
i += 1
|
||||
|
||||
if widget in widget_renders:
|
||||
for r in widget_renders[widget]:
|
||||
r.remove_from_old_renders()
|
||||
|
||||
if i == 0:
|
||||
return False
|
||||
|
||||
redraw_queue = redraw_queue[i:]
|
||||
return True
|
||||
|
||||
def redraw(widget, when):
|
||||
"""
|
||||
Call this to queue the redraw of the supplied widget in the
|
||||
supplied number of seconds.
|
||||
"""
|
||||
|
||||
redraw_queue.append((when + time.time(), widget))
|
||||
|
||||
def render_screen(widget, width, height, st):
|
||||
|
||||
global widget_renders
|
||||
global redraw_queue
|
||||
global old_renders
|
||||
global new_renders
|
||||
global mutable_surfaces
|
||||
|
||||
new_renders = { }
|
||||
widget_renders = { }
|
||||
redraw_queue = [ ]
|
||||
mutable_surfaces = { }
|
||||
|
||||
rv = render(widget, width, height, st)
|
||||
|
||||
old_renders = new_renders
|
||||
|
||||
return rv
|
||||
|
||||
old_blits = [ ]
|
||||
|
||||
|
||||
def compute_clip(source):
|
||||
"""
|
||||
This computes and returns the clipping rectangle of the source render.
|
||||
"""
|
||||
|
||||
global old_blits
|
||||
|
||||
new_blits = [ ]
|
||||
source.clip_to(pygame.display.get_surface(), 0, 0, new_blits)
|
||||
|
||||
bl0 = old_blits[:]
|
||||
bl1 = new_blits[:]
|
||||
|
||||
# Backup blits.
|
||||
old_blits = new_blits
|
||||
|
||||
# Changes between the two lists.
|
||||
changes = [ ]
|
||||
|
||||
|
||||
# Set of things in bl1.
|
||||
bl1set = { }
|
||||
for i in bl1:
|
||||
bl1set[i] = True
|
||||
|
||||
# indices.
|
||||
i0 = 0
|
||||
i1 = 0
|
||||
|
||||
while True:
|
||||
# If we're done with either of the lists, break.
|
||||
if i0 >= len(bl0) or i1 >= len(bl1):
|
||||
break
|
||||
|
||||
# blits
|
||||
b0 = bl0[i0]
|
||||
b1 = bl1[i1]
|
||||
|
||||
# If the two are the same.
|
||||
if b0 == b1:
|
||||
|
||||
# Only add if the surface is mutable.
|
||||
if b0[0] in mutable_surfaces:
|
||||
changes.append(b0)
|
||||
|
||||
i0 += 1
|
||||
i1 += 1
|
||||
continue
|
||||
|
||||
# If the surface is only in bl0.
|
||||
if b0 not in bl1set:
|
||||
changes.append(b0)
|
||||
i0 += 1
|
||||
|
||||
# The surface is only in bl1.
|
||||
else:
|
||||
changes.append(b1)
|
||||
i1 += 1
|
||||
|
||||
changes.extend(bl0[i0:])
|
||||
changes.extend(bl1[i1:])
|
||||
|
||||
if not changes:
|
||||
return None
|
||||
|
||||
surf, x0, y0, w, h = changes[0]
|
||||
x1 = x0 + w
|
||||
y1 = y0 + h
|
||||
|
||||
for surf, x, y, w, h in changes:
|
||||
x0 = min(x0, x)
|
||||
y0 = min(y0, y)
|
||||
|
||||
x1 = max(x1, x + w)
|
||||
y1 = max(y1, y + h)
|
||||
|
||||
return x0, y0, x1 - x0, y1 - y0
|
||||
|
||||
|
||||
def screen_blit(source, full=False):
|
||||
"""
|
||||
Blits the given render to the screen. Computes the difference
|
||||
between the current blit list and old_blits.
|
||||
"""
|
||||
|
||||
screen = pygame.display.get_surface()
|
||||
|
||||
if full:
|
||||
source.blit_to(screen, 0, 0)
|
||||
return (0, 0) + screen.get_size()
|
||||
|
||||
cliprect = compute_clip(source)
|
||||
|
||||
if not cliprect:
|
||||
return None
|
||||
|
||||
screen = pygame.display.get_surface()
|
||||
screen.set_clip(cliprect)
|
||||
|
||||
source.blit_to(screen, 0, 0)
|
||||
|
||||
screen.set_clip()
|
||||
|
||||
return cliprect
|
||||
|
||||
|
||||
|
||||
def mutable_surface(surf):
|
||||
"""
|
||||
Called to indicate that a pygame surface is mutable.
|
||||
"""
|
||||
|
||||
mutable_surfaces[id(surf)] = True
|
||||
|
||||
class Render(object):
|
||||
"""
|
||||
A render represents a static picture of a single widget (perhaps
|
||||
including the images of all of the children of that widget). It
|
||||
is able to draw that widget to the screen, and contains
|
||||
information about when it becomes invalid (and therefore the
|
||||
widget can be withdrawn).
|
||||
"""
|
||||
|
||||
def __init__(self, width, height):
|
||||
"""
|
||||
Creates a new render corresponding to the given widget with
|
||||
the specified width and height.
|
||||
|
||||
@param widget: If this render corresponds directly to a
|
||||
widget, then this is the widget it corresponds to.
|
||||
"""
|
||||
|
||||
self.widgets = [ ]
|
||||
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
self.req_width = width
|
||||
self.req_height = height
|
||||
|
||||
self.parents = [ ]
|
||||
|
||||
self.blittables = [ ]
|
||||
self.children = [ ]
|
||||
|
||||
# A pygame surface holding this Render, if one exists.
|
||||
self.surface = None
|
||||
self.surface_alpha = False
|
||||
|
||||
self.subsurfaces = { }
|
||||
|
||||
def add_to_new_renders(self):
|
||||
|
||||
for w in self.widgets:
|
||||
widget_renders.setdefault(w, []).append(self)
|
||||
new_renders[w, self.req_width, self.req_height] = self
|
||||
|
||||
for i in self.children:
|
||||
i.add_to_new_renders()
|
||||
|
||||
def remove_from_old_renders(self):
|
||||
|
||||
for w in self.widgets:
|
||||
# We need to check, since the same widget can appear twice.
|
||||
if (w, self.req_width, self.req_height) in old_renders:
|
||||
del old_renders[w, self.req_width, self.req_height]
|
||||
|
||||
self.widgets = [ ]
|
||||
|
||||
for p in self.parents:
|
||||
p.remove_from_old_renders()
|
||||
|
||||
self.parents = [ ]
|
||||
|
||||
def blit(self, source, (x, y)):
|
||||
"""
|
||||
Adds the source to the list of things that need to be blitted
|
||||
to the screen. The source should be either a pygame.Surface,
|
||||
or a Render.
|
||||
"""
|
||||
|
||||
if isinstance(source, Render):
|
||||
source.parents.append(self)
|
||||
self.children.append(source)
|
||||
|
||||
self.blittables.append((x, y, source))
|
||||
|
||||
|
||||
def blit_to(self, dest, x, y):
|
||||
"""
|
||||
This blits the children of this Render to dest, which must be
|
||||
a pygame.Surface. The x and y parameters are the location of
|
||||
the upper-left hand corner of this surface, relative to the
|
||||
destination surface.
|
||||
"""
|
||||
|
||||
for xo, yo, source in self.blittables:
|
||||
|
||||
if isinstance(source, pygame.Surface):
|
||||
dest.blit(source, (x + xo, y + yo))
|
||||
else:
|
||||
source.blit_to(dest, x + xo, y + yo)
|
||||
|
||||
def clip_to(self, dest, x, y, blits):
|
||||
"""
|
||||
This fills in blits with (id(surf), x, y, w, h) tuples.
|
||||
"""
|
||||
|
||||
for xo, yo, source in self.blittables:
|
||||
|
||||
if isinstance(source, pygame.Surface):
|
||||
blits.append((id(source), x + xo, y + yo) + source.get_size())
|
||||
else:
|
||||
source.clip_to(dest, x + xo, y + yo, blits)
|
||||
|
||||
def fill(self, color):
|
||||
"""
|
||||
Fake a pygame.Surface.fill()
|
||||
"""
|
||||
|
||||
surf = solid_cache.create((self.width, self.height), color)
|
||||
self.blit(surf, (0,0))
|
||||
|
||||
def get_size(self):
|
||||
"""
|
||||
Returns the size of this Render, a mostly ficticious value
|
||||
that's taken from the inputs to the constructor. (As in, we
|
||||
don't clip to this size.)
|
||||
"""
|
||||
|
||||
return self.width, self.height
|
||||
|
||||
def pygame_surface(self, alpha=True):
|
||||
"""
|
||||
Returns a pygame surface constructed from this Render. This
|
||||
may return a cached surface, if one already has been rendered
|
||||
(so you probably shouldn't change the output of this much).
|
||||
"""
|
||||
|
||||
if self.surface and self.surface_alpha == alpha:
|
||||
return self.surface
|
||||
|
||||
if alpha:
|
||||
sample = renpy.game.interface.display.sample_surface
|
||||
else:
|
||||
sample = renpy.game.interface.display.window
|
||||
|
||||
rv = pygame.Surface((self.width, self.height), 0, sample)
|
||||
|
||||
self.blit_to(rv, 0, 0)
|
||||
|
||||
self.surface = rv
|
||||
self.surface_alpha = alpha
|
||||
|
||||
return rv
|
||||
|
||||
def subsurface(self, pos):
|
||||
"""
|
||||
Returns a subsurface of this render.
|
||||
"""
|
||||
|
||||
if pos in self.subsurfaces:
|
||||
return self.subsurfaces[pos]
|
||||
|
||||
x, y, width, height = pos
|
||||
|
||||
if x > self.width or y > self.height:
|
||||
return Render(0, 0)
|
||||
|
||||
width = min(self.width - x, width)
|
||||
height = min(self.height - y, height)
|
||||
|
||||
rv = Render(width, height)
|
||||
|
||||
for xo, yo, source in self.blittables:
|
||||
|
||||
# ulx, uly -- the coordinates of the upper-left hand corner of
|
||||
# the image, relative to the subsurface.
|
||||
|
||||
ulx = xo - x
|
||||
uly = yo - y
|
||||
|
||||
# ox, oy -- the offsets that the source will be blitted at.
|
||||
# sx, sy -- the offset within the subsurface at which we begin.
|
||||
|
||||
if ulx < 0:
|
||||
ox = 0
|
||||
sx = -ulx
|
||||
else:
|
||||
ox = ulx
|
||||
sx = 0
|
||||
|
||||
if uly < 0:
|
||||
oy = 0
|
||||
sy = -uly
|
||||
else:
|
||||
oy = uly
|
||||
sy = 0
|
||||
|
||||
sw, sh = source.get_size()
|
||||
|
||||
sw = min(sw - sx, width)
|
||||
sh = min(sh - sy, height)
|
||||
|
||||
if sw <= 0 or sh <= 0:
|
||||
continue
|
||||
|
||||
rv.blit(source.subsurface((sx, sy, sw, sh)),
|
||||
(ox, oy))
|
||||
|
||||
|
||||
self.subsurfaces[pos] = rv
|
||||
rv.depends_on(self)
|
||||
|
||||
return rv
|
||||
|
||||
def depends_on(self, render):
|
||||
"""
|
||||
Used to indicate that this render depends on another
|
||||
render. Useful, for example, if we use pygame_surface to make
|
||||
a surface, and then blit that surface into another render.
|
||||
"""
|
||||
|
||||
render.parents.append(self)
|
||||
@@ -22,6 +22,8 @@ def get_font(fn, size):
|
||||
return rv
|
||||
|
||||
|
||||
|
||||
|
||||
class Text(renpy.display.core.Displayable):
|
||||
"""
|
||||
A Displayable that can display text on the screen.
|
||||
@@ -55,6 +57,8 @@ class Text(renpy.display.core.Displayable):
|
||||
"""
|
||||
|
||||
|
||||
super(Text, self).__init__()
|
||||
|
||||
self.text = text
|
||||
self.style = renpy.style.Style(style, properties)
|
||||
self.slow = slow
|
||||
@@ -96,8 +100,7 @@ class Text(renpy.display.core.Displayable):
|
||||
Called to split the text into a string with newline characters
|
||||
at line endings where wrapping has occured.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
font = get_font(self.style.font, self.style.size)
|
||||
|
||||
lines = [ ]
|
||||
@@ -154,7 +157,7 @@ class Text(renpy.display.core.Displayable):
|
||||
if not hasattr(self, "laidout"):
|
||||
self.layout(width - dsxo)
|
||||
|
||||
surf = renpy.display.surface.Surface(self.width + dsxo, self.height + dsyo)
|
||||
surf = renpy.display.render.Render(self.width + dsxo, self.height + dsyo)
|
||||
font = get_font(self.style.font, self.style.size)
|
||||
|
||||
laidout = self.laidout
|
||||
@@ -164,7 +167,7 @@ class Text(renpy.display.core.Displayable):
|
||||
chars = int(st * renpy.config.annoying_text_cps)
|
||||
if chars < len(laidout):
|
||||
laidout = laidout[:chars]
|
||||
renpy.game.interface.redraw(0)
|
||||
renpy.display.render.redraw(self, 0)
|
||||
else:
|
||||
self.slow = False
|
||||
else:
|
||||
|
||||
+402
-41
@@ -1,7 +1,57 @@
|
||||
import renpy
|
||||
from renpy.display.render import render
|
||||
import pygame
|
||||
from pygame.constants import *
|
||||
|
||||
# This is a utility function that attempts to refactor an old and a new
|
||||
# Fixed into four Fixeds: below, old, new, and above. Since only the
|
||||
# old and new need transitions, this can be a significant win.
|
||||
def refactor_fixed(in_old, in_new):
|
||||
|
||||
Fixed = renpy.display.layout.Fixed
|
||||
|
||||
out_below = Fixed()
|
||||
out_old = Fixed()
|
||||
out_new = Fixed()
|
||||
out_above = Fixed()
|
||||
|
||||
if (not isinstance(in_old, Fixed)) or (not isinstance(in_new, Fixed)):
|
||||
return out_below, in_old, in_new, out_above
|
||||
|
||||
old_list = in_old.get_widget_time_list()
|
||||
new_list = in_new.get_widget_time_list()
|
||||
|
||||
# Merge the beginnings of the lists.
|
||||
while old_list and new_list:
|
||||
if old_list[0] == new_list[0]:
|
||||
out_below.add(new_list[0][0], new_list[0][1])
|
||||
old_list.pop(0)
|
||||
new_list.pop(0)
|
||||
|
||||
else:
|
||||
break
|
||||
|
||||
# Merge the ends of the lists.
|
||||
above_list = [ ]
|
||||
|
||||
while old_list and new_list:
|
||||
if old_list[-1] == new_list[-1]:
|
||||
above_list.insert(0, new_list[-1])
|
||||
old_list.pop()
|
||||
new_list.pop()
|
||||
else:
|
||||
break
|
||||
|
||||
for widget, time in above_list:
|
||||
out_above.add(widget, time)
|
||||
|
||||
for widget, time in old_list:
|
||||
out_old.add(widget, time)
|
||||
|
||||
for widget, time in new_list:
|
||||
out_new.add(widget, time)
|
||||
|
||||
return out_below, out_old, out_new, out_above
|
||||
|
||||
class Transition(renpy.display.core.Displayable):
|
||||
"""
|
||||
@@ -12,38 +62,47 @@ class Transition(renpy.display.core.Displayable):
|
||||
def __init__(self, delay):
|
||||
self.delay = delay
|
||||
self.offsets = [ ]
|
||||
self.events = True
|
||||
|
||||
def event(self, ev, x, y):
|
||||
event_list = self.new_scene_list[:]
|
||||
event_list.reverse()
|
||||
|
||||
offsets = self.offsets[:]
|
||||
offsets.reverse()
|
||||
|
||||
for (key, st, disp), (xo, yo) in zip(event_list, offsets):
|
||||
rv = disp.event(ev, x - xo, y - yo)
|
||||
if rv is not None:
|
||||
return rv
|
||||
|
||||
return None
|
||||
if self.events:
|
||||
return self.new_widget.event(ev, x, y)
|
||||
else:
|
||||
return None
|
||||
|
||||
class Fade(Transition):
|
||||
"""
|
||||
This is a transition that involves fading to a certain color, then
|
||||
holding that color for a certain amount of time, then fading in the
|
||||
new scene.
|
||||
This returns an object that can be used as an argument to a with
|
||||
statement to fade the old scene into a solid color, waits for a
|
||||
given amount of time, and then fades from the solid color into
|
||||
the new scene.
|
||||
|
||||
@param in_time: The amount of time that will be spent
|
||||
fading from the old scene to the solid color. A float, given as
|
||||
seconds.
|
||||
|
||||
@param hold_time: The amount of time that will be spent
|
||||
displaying the solid color. A float, given as seconds.
|
||||
|
||||
@param out_time: The amount of time that will be spent
|
||||
fading from the solid color to the new scene. A float, given as
|
||||
seconds.
|
||||
|
||||
@param color: The solid color that will be faded
|
||||
to. This is an RGB triple, where each element is in the range 0
|
||||
to 255. This defaults to black.
|
||||
"""
|
||||
|
||||
def __init__(self, out_time, hold_time, in_time,
|
||||
old_scene_list, new_scene_list, color=(0, 0, 0)):
|
||||
old_widget=None, new_widget=None, color=(0, 0, 0)):
|
||||
|
||||
super(Fade, self).__init__(out_time + hold_time + in_time)
|
||||
|
||||
self.out_time = out_time
|
||||
self.hold_time = hold_time
|
||||
self.in_time = in_time
|
||||
self.old_scene_list = old_scene_list
|
||||
self.new_scene_list = new_scene_list
|
||||
self.old_widget = old_widget
|
||||
self.new_widget = new_widget
|
||||
self.color = color
|
||||
|
||||
# self.frames = 0
|
||||
@@ -55,31 +114,29 @@ class Fade(Transition):
|
||||
|
||||
# self.frames += 1
|
||||
|
||||
rv = renpy.display.surface.Surface(width, height)
|
||||
rv = renpy.display.render.Render(width, height)
|
||||
|
||||
events = False
|
||||
|
||||
if st < self.out_time:
|
||||
scene_list = self.old_scene_list
|
||||
widget = self.old_widget
|
||||
alpha = int(255 * (st / self.out_time))
|
||||
|
||||
elif st < self.out_time + self.hold_time:
|
||||
scene_list = None
|
||||
widget = None
|
||||
alpha = 255
|
||||
|
||||
else:
|
||||
scene_list = self.new_scene_list
|
||||
widget = self.new_widget
|
||||
alpha = 255 - int(255 * ((st - self.out_time - self.hold_time) / self.in_time))
|
||||
events = True
|
||||
|
||||
if scene_list:
|
||||
surf, offsets = renpy.display.core.render_scene_list(scene_list,
|
||||
width,
|
||||
height)
|
||||
if widget:
|
||||
surf = render(widget, width, height, st)
|
||||
|
||||
rv.blit(surf, (0, 0))
|
||||
|
||||
if events:
|
||||
self.offsets = offsets
|
||||
self.events = events
|
||||
|
||||
# Just to be sure.
|
||||
if alpha < 0:
|
||||
@@ -91,35 +148,339 @@ class Fade(Transition):
|
||||
rv.fill(self.color[:3] + (alpha,))
|
||||
|
||||
if st < self.in_time + self.hold_time + self.out_time:
|
||||
renpy.game.interface.redraw(0)
|
||||
renpy.display.render.redraw(self, 0)
|
||||
|
||||
return rv
|
||||
|
||||
class Dissolve(Transition):
|
||||
# This was a nifty idea that just didn't work out, since we can't vary
|
||||
# the alpha on an image with an alpha channel. Too bad.
|
||||
|
||||
def __init__(self, time, old_scene_list, new_scene_list):
|
||||
# class Dissolve(Transition):
|
||||
|
||||
# def __init__(self, time, old_widget, new_widget):
|
||||
# super(Dissolve, self).__init__(time)
|
||||
|
||||
# self.time = time
|
||||
# self.below, self.old, self.new, self.above = refactor_fixed(old_widget, new_widget)
|
||||
|
||||
# def event(self, ev, x, y):
|
||||
|
||||
# rv = self.above.event(ev, x, y)
|
||||
|
||||
# if rv is None:
|
||||
# rv = self.new.event(ev, x, y)
|
||||
|
||||
# if rv is None:
|
||||
# rv = self.below.event(ev, x, y)
|
||||
|
||||
# return rv
|
||||
|
||||
# def render(self, width, height, st):
|
||||
|
||||
# rv = renpy.display.render.Render(width, height)
|
||||
|
||||
# # Below.
|
||||
# below = render(self.below, width, height, st)
|
||||
# rv.blit(below, (0, 0))
|
||||
|
||||
# if st < self.time:
|
||||
# # Old.
|
||||
# old = render(self.old, width, height, st)
|
||||
# rv.blit(old, (0, 0))
|
||||
|
||||
# # New.
|
||||
# alpha = min(255, int(255 * st / self.time))
|
||||
# new = render(self.new, width, height, st)
|
||||
|
||||
# if alpha < 255:
|
||||
# surf = new.pygame_surface(False)
|
||||
# renpy.display.render.mutable_surface(surf)
|
||||
# surf.set_alpha(alpha, RLEACCEL)
|
||||
# rv.blit(surf, (0, 0))
|
||||
# rv.depends_on(new)
|
||||
# else:
|
||||
# rv.blit(new, (0, 0))
|
||||
|
||||
# # Above.
|
||||
# above = render(self.above, width, height, st)
|
||||
# rv.blit(above, (0, 0))
|
||||
|
||||
|
||||
# if st < self.time:
|
||||
# renpy.display.render.redraw(self, 0)
|
||||
|
||||
# return rv
|
||||
|
||||
|
||||
|
||||
class Dissolve(Transition):
|
||||
"""
|
||||
This dissolves from the old scene to the new scene, by
|
||||
overlaying the new scene on top of the old scene and varying its
|
||||
alpha from 0 to 255.
|
||||
|
||||
@param delay: The amount of time the dissolve will take.
|
||||
"""
|
||||
|
||||
def __init__(self, time, old_widget=None, new_widget=None):
|
||||
super(Dissolve, self).__init__(time)
|
||||
|
||||
self.time = time
|
||||
self.old_scene_list = old_scene_list
|
||||
self.new_scene_list = new_scene_list
|
||||
self.old_widget = old_widget
|
||||
self.new_widget = new_widget
|
||||
self.events = False
|
||||
|
||||
def render(self, width, height, st):
|
||||
|
||||
rsl = renpy.display.core.render_scene_list
|
||||
if st >= self.time:
|
||||
self.events = True
|
||||
return render(self.new_widget, width, height, st)
|
||||
|
||||
rv, offsets = rsl(self.old_scene_list, width, height)
|
||||
surftree, self.offsets = rsl(self.new_scene_list, width, height)
|
||||
surf = surftree.pygame_surface(False)
|
||||
rv = render(self.old_widget, width, height, st)
|
||||
|
||||
top = render(self.new_widget, width, height, st)
|
||||
|
||||
surf = top.pygame_surface(False)
|
||||
renpy.display.render.mutable_surface(surf)
|
||||
|
||||
alpha = min(255, int(255 * st / self.time))
|
||||
|
||||
surf.set_alpha(alpha)
|
||||
|
||||
surf.set_alpha(alpha, RLEACCEL)
|
||||
rv.blit(surf, (0, 0))
|
||||
|
||||
if st < self.time:
|
||||
renpy.game.interface.redraw(0)
|
||||
|
||||
renpy.display.render.redraw(self, 0)
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
|
||||
class CropMove(Transition):
|
||||
"""
|
||||
The CropMove transition works by placing the old and the new image
|
||||
on two layers, called the top and the bottom. (Normally the new
|
||||
image is on the top, but that can be changed in some modes.) The
|
||||
bottom layer is always drawn in full. The top image is first
|
||||
cropped to a rectangle, and then that rectangle drawn onto
|
||||
the screen at a specified position. Start and end crop rectangles
|
||||
and positions can be selected by the supplied mode, or
|
||||
specified manually. The result is a surprisingly flexible
|
||||
transition.
|
||||
|
||||
This transition has many modes, simplifying its use. We can group
|
||||
these modes into three groups: wipes, slides, and other.
|
||||
|
||||
In a wipe, the image stays fixed, and more of it is revealed as
|
||||
the transition progresses. For example, in "wiperight", a wipe from left to right, first the left edge of the image is
|
||||
revealed at the left edge of the screen, then the center of the image,
|
||||
and finally the right side of the image at the right of the screen.
|
||||
Other supported wipes are "wipeleft", "wipedown", and "wipeup".
|
||||
|
||||
In a slide, the image moves. So in a "slideright", the right edge of the
|
||||
image starts at the left edge of the screen, and moves to the right
|
||||
as the transition progresses. Other slides are "slideleft", "slidedown",
|
||||
and "slideup".
|
||||
|
||||
There are also slideaways, in which the old image moves on top of
|
||||
the new image. Slideaways include "slideawayright", "slideawayleft",
|
||||
"slideawayup", and "slideawaydown".
|
||||
|
||||
We also support a rectangular iris in with "irisin" and a
|
||||
rectangular iris out with "irisout". Finally, "custom" lets the
|
||||
user define new transitions, if these ones are not enough.
|
||||
"""
|
||||
|
||||
def __init__(self, time,
|
||||
mode="fromleft",
|
||||
startcrop=(0.0, 0.0, 0.0, 1.0),
|
||||
startpos=(0.0, 0.0),
|
||||
endcrop=(0.0, 0.0, 1.0, 1.0),
|
||||
endpos=(0.0, 0.0),
|
||||
topnew=True,
|
||||
old_widget=None,
|
||||
new_widget=None):
|
||||
|
||||
"""
|
||||
@param time: The time that this transition will last for, in seconds.
|
||||
|
||||
@param mode: One of the modes given above.
|
||||
|
||||
The following parameters are only respected if the mode is "custom".
|
||||
|
||||
@param startcrop: The starting rectangle that is cropped out of the
|
||||
top image. A 4-element tuple containing x, y, width, and height.
|
||||
|
||||
@param startpos: The starting place that the top image is drawn
|
||||
to the screen at, a 2-element tuple containing x and y.
|
||||
|
||||
@param startcrop: The starting rectangle that is cropped out of the
|
||||
top image. A 4-element tuple containing x, y, width, and height.
|
||||
|
||||
@param startpos: The starting place that the top image is drawn
|
||||
to the screen at, a 2-element tuple containing x and y.
|
||||
|
||||
@param topnew: If True, the top layer contains the new
|
||||
image. Otherwise, the top layer contains the old image.
|
||||
"""
|
||||
|
||||
|
||||
self.time = time
|
||||
|
||||
if mode == "wiperight":
|
||||
startpos = (0.0, 0.0)
|
||||
startcrop = (0.0, 0.0, 0.0, 1.0)
|
||||
endpos = (0.0, 0.0)
|
||||
endcrop = (0.0, 0.0, 1.0, 1.0)
|
||||
topnew = True
|
||||
|
||||
elif mode == "wipeleft":
|
||||
startpos = (1.0, 0.0)
|
||||
startcrop = (1.0, 0.0, 0.0, 1.0)
|
||||
endpos = (0.0, 0.0)
|
||||
endcrop = (0.0, 0.0, 1.0, 1.0)
|
||||
topnew = True
|
||||
|
||||
elif mode == "wipedown":
|
||||
startpos = (0.0, 0.0)
|
||||
startcrop = (0.0, 0.0, 1.0, 0.0)
|
||||
endpos = (0.0, 0.0)
|
||||
endcrop = (0.0, 0.0, 1.0, 1.0)
|
||||
topnew = True
|
||||
|
||||
elif mode == "wipeup":
|
||||
startpos = (0.0, 1.0)
|
||||
startcrop = (0.0, 1.0, 1.0, 0.0)
|
||||
endpos = (0.0, 0.0)
|
||||
endcrop = (0.0, 0.0, 1.0, 1.0)
|
||||
topnew = True
|
||||
|
||||
elif mode == "slideright":
|
||||
startpos = (0.0, 0.0)
|
||||
startcrop = (1.0, 0.0, 0.0, 1.0)
|
||||
endpos = (0.0, 0.0)
|
||||
endcrop = (0.0, 0.0, 1.0, 1.0)
|
||||
topnew = True
|
||||
|
||||
elif mode == "slideleft":
|
||||
startpos = (1.0, 0.0)
|
||||
startcrop = (0.0, 0.0, 0.0, 1.0)
|
||||
endpos = (0.0, 0.0)
|
||||
endcrop = (0.0, 0.0, 1.0, 1.0)
|
||||
topnew = True
|
||||
|
||||
elif mode == "slideup":
|
||||
startpos = (0.0, 1.0)
|
||||
startcrop = (0.0, 0.0, 1.0, 0.0)
|
||||
endpos = (0.0, 0.0)
|
||||
endcrop = (0.0, 0.0, 1.0, 1.0)
|
||||
topnew = True
|
||||
|
||||
elif mode == "slidedown":
|
||||
startpos = (0.0, 0.0)
|
||||
startcrop = (0.0, 1.0, 1.0, 0.0)
|
||||
endpos = (0.0, 0.0)
|
||||
endcrop = (0.0, 0.0, 1.0, 1.0)
|
||||
topnew = True
|
||||
|
||||
elif mode == "slideawayleft":
|
||||
endpos = (0.0, 0.0)
|
||||
endcrop = (1.0, 0.0, 0.0, 1.0)
|
||||
startpos = (0.0, 0.0)
|
||||
startcrop = (0.0, 0.0, 1.0, 1.0)
|
||||
topnew = False
|
||||
|
||||
elif mode == "slideawayright":
|
||||
endpos = (1.0, 0.0)
|
||||
endcrop = (0.0, 0.0, 0.0, 1.0)
|
||||
startpos = (0.0, 0.0)
|
||||
startcrop = (0.0, 0.0, 1.0, 1.0)
|
||||
topnew = False
|
||||
|
||||
elif mode == "slideawaydown":
|
||||
endpos = (0.0, 1.0)
|
||||
endcrop = (0.0, 0.0, 1.0, 0.0)
|
||||
startpos = (0.0, 0.0)
|
||||
startcrop = (0.0, 0.0, 1.0, 1.0)
|
||||
topnew = False
|
||||
|
||||
elif mode == "slideawayup":
|
||||
endpos = (0.0, 0.0)
|
||||
endcrop = (0.0, 1.0, 1.0, 0.0)
|
||||
startpos = (0.0, 0.0)
|
||||
startcrop = (0.0, 0.0, 1.0, 1.0)
|
||||
topnew = False
|
||||
|
||||
elif mode == "irisout":
|
||||
startpos = (0.5, 0.5)
|
||||
startcrop = (0.5, 0.5, 0.0, 0.0)
|
||||
endpos = (0.0, 0.0)
|
||||
endcrop = (0.0, 0.0, 1.0, 1.0)
|
||||
topnew = True
|
||||
|
||||
elif mode == "irisin":
|
||||
startpos = (0.0, 0.0)
|
||||
startcrop = (0.0, 0.0, 1.0, 1.0)
|
||||
endpos = (0.5, 0.5)
|
||||
endcrop = (0.5, 0.5, 0.0, 0.0)
|
||||
topnew = False
|
||||
|
||||
|
||||
elif mode == "custom":
|
||||
pass
|
||||
else:
|
||||
raise Exception("Invalid mode %s passed into boxwipe." % mode)
|
||||
|
||||
self.delay = time
|
||||
self.time = time
|
||||
|
||||
self.startpos = startpos
|
||||
self.endpos = endpos
|
||||
|
||||
self.startcrop = startcrop
|
||||
self.endcrop = endcrop
|
||||
|
||||
self.topnew = topnew
|
||||
|
||||
self.old_widget = old_widget
|
||||
self.new_widget = new_widget
|
||||
|
||||
self.events = False
|
||||
|
||||
if topnew:
|
||||
self.bottom = old_widget
|
||||
self.top = new_widget
|
||||
else:
|
||||
self.bottom = new_widget
|
||||
self.top = old_widget
|
||||
|
||||
def render(self, width, height, st):
|
||||
|
||||
time = 1.0 * st / self.time
|
||||
|
||||
# Done rendering.
|
||||
if time >= 1.0:
|
||||
self.events = True
|
||||
return render(self.new_widget, width, height, st)
|
||||
|
||||
# How we scale each element of a tuple.
|
||||
scales = (width, height, width, height)
|
||||
|
||||
def interpolate_tuple(t0, t1):
|
||||
return tuple([ int(s * (a * (1.0 - time) + b * time))
|
||||
for a, b, s in zip(t0, t1, scales) ])
|
||||
|
||||
crop = interpolate_tuple(self.startcrop, self.endcrop)
|
||||
pos = interpolate_tuple(self.startpos, self.endpos)
|
||||
|
||||
rv = renpy.display.render.Render(width, height)
|
||||
|
||||
rv.blit(render(self.bottom, width, height, st), (0, 0))
|
||||
|
||||
top = render(self.top, width, height, st)
|
||||
ss = top.subsurface(crop)
|
||||
rv.blit(ss, pos)
|
||||
|
||||
renpy.display.render.redraw(self, 0)
|
||||
return rv
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
import renpy
|
||||
from renpy.display.render import render
|
||||
import pygame
|
||||
import sys # for maxint
|
||||
|
||||
class MovieInfo(object):
|
||||
|
||||
def __init__(self, filename, loops, fullscreen, size=None):
|
||||
self.filename = filename
|
||||
self.loops = loops + 1
|
||||
self.fullscreen = fullscreen
|
||||
self.size = size
|
||||
|
||||
# The movie that is currently playing, if any.
|
||||
movie = None
|
||||
|
||||
# If the movie is running in a widget, this is the surface corresponding
|
||||
# to that widget.
|
||||
surface = None
|
||||
|
||||
# The current movie info.
|
||||
current_info = None
|
||||
|
||||
# The number of loops the current movie has made.
|
||||
loops = 0
|
||||
|
||||
def movie_stop(clear=True):
|
||||
"""
|
||||
This stops the currently playing movie.
|
||||
"""
|
||||
|
||||
global movie
|
||||
global loops
|
||||
|
||||
if movie:
|
||||
movie.stop()
|
||||
movie = None
|
||||
surface = None
|
||||
loops = 0
|
||||
|
||||
renpy.display.audio.enable_mixer()
|
||||
|
||||
if clear:
|
||||
renpy.game.context().scene_lists.movie = None
|
||||
|
||||
|
||||
def movie_start_fullscreen(filename, loops=0):
|
||||
"""
|
||||
This starts a MPEG-1 movie playing in fullscreen mode. While the movie is
|
||||
playing (that is, until the next call to movie_stop), interactions will
|
||||
not display anything on the screen.
|
||||
|
||||
@param filename: The filename of the MPEG-1 move that we're playing.
|
||||
|
||||
@param loops: The number of additional times the movie should be looped. -1 to loop it forever.
|
||||
"""
|
||||
|
||||
movie_stop()
|
||||
renpy.game.context().scene_lists.movie = MovieInfo(filename, loops, True)
|
||||
|
||||
def movie_start_displayable(filename, size, loops=0):
|
||||
"""
|
||||
This starts a MPEG-1 movie playing in displayable mode. One or more Movie()
|
||||
widgets must be displayed if the movie is to be shown to the user.
|
||||
|
||||
@param filename: The filename of the MPEG-1 move that we're playing.
|
||||
|
||||
@param size: A tuple containing the size of the movie on the screen. For example, (640, 480).
|
||||
|
||||
@param loops: The number of additional times the movie should be looped. -1 to loop it forever.
|
||||
"""
|
||||
|
||||
movie_stop()
|
||||
renpy.game.context().scene_lists.movie = MovieInfo(filename, loops, False, size)
|
||||
|
||||
|
||||
def interact():
|
||||
"""
|
||||
This is called at the start of an interaction. It starts the required
|
||||
movie playing, if it's necessary. It returns True if the movie is fullscreen
|
||||
and therefore nothing else should be drawn on the screen, or False
|
||||
otherwise.
|
||||
"""
|
||||
|
||||
try:
|
||||
|
||||
global movie
|
||||
global surface
|
||||
global current_info
|
||||
global loops
|
||||
|
||||
info = renpy.game.context().scene_lists.movie
|
||||
|
||||
# Has the info changed? If so, stop the movie.
|
||||
if info is not current_info:
|
||||
movie_stop(False)
|
||||
current_info = info
|
||||
|
||||
# No movie to play.
|
||||
if not info:
|
||||
return False
|
||||
|
||||
# Movie not playing, start it up.
|
||||
if not movie:
|
||||
|
||||
# Needed so we get movie sound.
|
||||
renpy.display.audio.disable_mixer()
|
||||
|
||||
m = pygame.movie.Movie(renpy.loader.transfn(info.filename))
|
||||
|
||||
if info.fullscreen:
|
||||
s = None
|
||||
|
||||
m.set_display(pygame.display.get_surface(),
|
||||
(0, 0,
|
||||
renpy.config.screen_width,
|
||||
renpy.config.screen_height))
|
||||
else:
|
||||
s = pygame.Surface(info.size)
|
||||
m.set_display(s, (0, 0) + info.size)
|
||||
|
||||
movie = m
|
||||
surface = s
|
||||
|
||||
if not movie.get_busy():
|
||||
if not info.loops or loops < info.loops:
|
||||
movie.rewind()
|
||||
movie.play()
|
||||
loops += 1
|
||||
else:
|
||||
movie_stop()
|
||||
|
||||
|
||||
# Movie is playing (by now).
|
||||
return info.fullscreen
|
||||
|
||||
except:
|
||||
movie_stop()
|
||||
|
||||
if renpy.config.debug_sound:
|
||||
raise
|
||||
else:
|
||||
renpy.display.audio.enable_mixer()
|
||||
return False
|
||||
|
||||
|
||||
class Movie(renpy.display.layout.Null):
|
||||
"""
|
||||
This is a displayable that displays the current movie. In general,
|
||||
a movie should be playing whenever this is on the screen.
|
||||
That movie should have been started using movie_start_displayable
|
||||
before this is shown on the screen, and hidden before this is
|
||||
removed.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, style='image_placement', **properties):
|
||||
super(Movie, self).__init__(style=style, **properties)
|
||||
|
||||
def render(self, width, height, st):
|
||||
renpy.display.render.redraw(self, 0)
|
||||
|
||||
|
||||
if surface:
|
||||
renpy.display.render.mutable_surface(surface)
|
||||
|
||||
w, h = surface.get_size()
|
||||
rv = renpy.display.render.Render(w, h)
|
||||
rv.blit(surface, (0, 0))
|
||||
return rv
|
||||
else:
|
||||
return super(Movie, self).render(width, height, st)
|
||||
|
||||
+1
-2
@@ -36,8 +36,7 @@ class Context(object):
|
||||
oldsl = context.scene_lists
|
||||
self.runtime = context.runtime
|
||||
|
||||
import renpy.display.core as dcore
|
||||
self.scene_lists = dcore.SceneLists(oldsl)
|
||||
self.scene_lists = renpy.display.core.SceneLists(oldsl)
|
||||
|
||||
def goto_label(self, node_name):
|
||||
"""
|
||||
|
||||
+86
-21
@@ -12,8 +12,9 @@ from renpy.display.behavior import *
|
||||
from renpy.display.image import *
|
||||
|
||||
from renpy.curry import curry
|
||||
from renpy.music import music_start, music_stop
|
||||
from renpy.sound import play
|
||||
from renpy.display.audio import music_start, music_stop
|
||||
from renpy.display.audio import play
|
||||
from renpy.display.video import movie_start_fullscreen, movie_start_displayable, movie_stop
|
||||
from renpy.loadsave import *
|
||||
from renpy.python import py_eval as eval
|
||||
from renpy.python import rng as random
|
||||
@@ -32,8 +33,8 @@ def checkpoint():
|
||||
|
||||
renpy.game.log.checkpoint()
|
||||
|
||||
def interact(*widgets, **kwargs):
|
||||
return renpy.game.interface.interact(transient=widgets, **kwargs)
|
||||
# def interact(**kwargs):
|
||||
# return renpy.game.interface.interact(**kwargs)
|
||||
|
||||
def scene_lists(index=-1):
|
||||
"""
|
||||
@@ -124,13 +125,15 @@ def input(prompt, default='', length=None):
|
||||
function will return.
|
||||
"""
|
||||
|
||||
vbox = renpy.display.layout.VBox()
|
||||
win = renpy.display.layout.Window(vbox, style='input_window')
|
||||
renpy.ui.window(style='input_window')
|
||||
renpy.ui.vbox()
|
||||
|
||||
vbox.add(renpy.display.text.Text(prompt, style='input_prompt'))
|
||||
vbox.add(renpy.display.behavior.Input(default, length=length, style='input_text'))
|
||||
renpy.ui.text(prompt, style='input_prompt')
|
||||
renpy.ui.input(default, length=length, style='input_text')
|
||||
|
||||
return interact(win)
|
||||
renpy.ui.close()
|
||||
|
||||
return renpy.ui.interact()
|
||||
|
||||
def menu(items, set_expr):
|
||||
"""
|
||||
@@ -186,7 +189,7 @@ def display_menu(items, window_style='menu_window'):
|
||||
renpy.ui.window(style=window_style)
|
||||
renpy.ui.menu(items)
|
||||
|
||||
rv = interact()
|
||||
rv = renpy.ui.interact()
|
||||
checkpoint()
|
||||
|
||||
return rv
|
||||
@@ -231,7 +234,7 @@ def display_say(who, what, who_style='say_label',
|
||||
"""
|
||||
|
||||
# If we're going to do an interaction, then saybehavior needs
|
||||
# to be here, right before ui.text.
|
||||
# to be here.
|
||||
if interact:
|
||||
renpy.ui.saybehavior()
|
||||
|
||||
@@ -290,7 +293,7 @@ def imagemap(ground, selected, hotspots, unselected=None, overlays=False,
|
||||
|
||||
renpy.ui.keymousebehavior()
|
||||
|
||||
rv = interact(suppress_overlay=(not overlays))
|
||||
rv = renpy.ui.interact(suppress_overlay=(not overlays))
|
||||
checkpoint()
|
||||
return rv
|
||||
|
||||
@@ -298,7 +301,9 @@ def imagemap(ground, selected, hotspots, unselected=None, overlays=False,
|
||||
def pause(delay=None, music=None):
|
||||
"""
|
||||
When called, this pauses and waits for the user to click before
|
||||
advancing the script.
|
||||
advancing the script. If given a delay parameter, the Ren'Py will
|
||||
wait for that amount of time before continuing, unless a user clicks to
|
||||
interrupt the delay.
|
||||
|
||||
@param delay: The number of seconds to delay.
|
||||
|
||||
@@ -313,15 +318,45 @@ def pause(delay=None, music=None):
|
||||
"""
|
||||
|
||||
if music is not None:
|
||||
newdelay = renpy.music.music_delay(music)
|
||||
newdelay = renpy.display.audio.music_delay(music)
|
||||
|
||||
if newdelay is not None:
|
||||
delay = newdelay
|
||||
|
||||
sayb = renpy.display.behavior.SayBehavior(delay=delay)
|
||||
scene_list_add('transient', sayb)
|
||||
|
||||
return interact()
|
||||
renpy.ui.saybehavior()
|
||||
|
||||
if delay:
|
||||
renpy.ui.pausebehavior(delay, False)
|
||||
|
||||
return renpy.ui.interact()
|
||||
|
||||
def movie_cutscene(filename, delay, loops=0):
|
||||
"""
|
||||
This displays an MPEG-1 cutscene for the specified number of
|
||||
seconds. The user can click to interrupt the cutscene.
|
||||
Overlays and Underlays are disabled for the duration of the cutscene.
|
||||
|
||||
@param filename: The name of a file containing an MPEG-1 movie.
|
||||
|
||||
@param delay: The number of seconds to wait before ending the cutscene. Normally the length of the movie, in seconds.
|
||||
|
||||
@param loops: The number of extra loops to show, -1 to loop forever.
|
||||
|
||||
Returns True if the movie was terminated by the user, or False if the
|
||||
given delay elapsed uninterrupted.
|
||||
"""
|
||||
|
||||
movie_start_fullscreen(filename, loops=loops)
|
||||
|
||||
renpy.ui.saybehavior()
|
||||
renpy.ui.pausebehavior(delay, False)
|
||||
|
||||
rv = renpy.ui.interact(suppress_overlay=True, suppress_underlay=True, show_mouse=False)
|
||||
|
||||
movie_stop()
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
def with(trans):
|
||||
"""
|
||||
@@ -339,7 +374,7 @@ def with(trans):
|
||||
renpy.game.interface.set_transition(trans)
|
||||
return renpy.game.interface.interact(show_mouse=False,
|
||||
trans_pause=True,
|
||||
suppress_overlay=True)
|
||||
suppress_overlay=not renpy.config.overlay_during_wait)
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -423,17 +458,21 @@ def windows():
|
||||
import sys
|
||||
return hasattr(sys, 'winver')
|
||||
|
||||
def transition(trans):
|
||||
def transition(trans, layer=None):
|
||||
"""
|
||||
Sets the transition that will be used for the next
|
||||
interaction. This is useful when the next interaction doesn't take
|
||||
a with clause, as is the case with pause, input, and imagemap.
|
||||
|
||||
@param layer: If the layer setting is not None, then the transition
|
||||
will be applied only to the layer named. Please note that only some
|
||||
transitions can be applied to specific layers.
|
||||
"""
|
||||
|
||||
if trans is None:
|
||||
renpy.game.interface.with_none()
|
||||
else:
|
||||
renpy.game.interface.set_transition(trans)
|
||||
renpy.game.interface.set_transition(trans, layer)
|
||||
|
||||
def clear_game_runtime():
|
||||
"""
|
||||
@@ -458,6 +497,32 @@ def get_game_runtime():
|
||||
|
||||
return renpy.game.context().runtime / 1000.0
|
||||
|
||||
def exists(filename):
|
||||
"""
|
||||
Returns true if the given filename can be found in the
|
||||
searchpath. This only works if a physical file exists on disk. It
|
||||
won't find the file if it's inside of an archive.
|
||||
"""
|
||||
|
||||
try:
|
||||
renpy.loader.transfn(filename)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def restart_interaction():
|
||||
"""
|
||||
Calling this restarts the current interaction. This will immediately end
|
||||
any ongoing transition, and will call all of the overlay functions again.
|
||||
|
||||
This should be called whenever widgets are added or removed over the course
|
||||
of an interaction, or when the information used to construct the overlay
|
||||
changes.
|
||||
"""
|
||||
|
||||
renpy.game.interface.restart_interaction = True
|
||||
|
||||
|
||||
call_in_new_context = renpy.game.call_in_new_context
|
||||
curried_call_in_new_context = renpy.curry.curry(renpy.game.call_in_new_context)
|
||||
|
||||
|
||||
+1
-1
@@ -147,4 +147,4 @@ def call_in_new_context(label):
|
||||
context.run()
|
||||
|
||||
contexts.pop()
|
||||
interface.redraw(0)
|
||||
interface.force_redraw = True
|
||||
|
||||
+1
-1
@@ -574,7 +574,7 @@ class RollbackLog(renpy.object.Object):
|
||||
rb.rollback()
|
||||
|
||||
# Disable the next transition, as it's pointless.
|
||||
renpy.game.interface.supress_transition = True
|
||||
renpy.game.interface.suppress_transition = True
|
||||
|
||||
# If necessary, reset the RNG.
|
||||
if force:
|
||||
|
||||
+9
-1
@@ -91,7 +91,15 @@ class Script(object):
|
||||
raise Exception("Could not load %s or %s." % (fn, alt))
|
||||
|
||||
|
||||
self.initcode.sort()
|
||||
# Make the sort stable.
|
||||
initcode = [ (prio, index, code) for index, (prio, code) in
|
||||
enumerate(self.initcode) ]
|
||||
|
||||
initcode.sort()
|
||||
|
||||
self.initcode = [ (prio, code) for prio, index, code in initcode ]
|
||||
|
||||
|
||||
|
||||
# Do some generic init here.
|
||||
|
||||
|
||||
+9
-5
@@ -22,12 +22,14 @@ Image = renpy.display.image.Image
|
||||
Solid = renpy.display.image.Solid
|
||||
Frame = renpy.display.image.Frame
|
||||
Animation = renpy.display.image.Animation
|
||||
Movie = renpy.display.video.Movie
|
||||
|
||||
Position = renpy.curry.curry(renpy.display.layout.Position)
|
||||
Pan = renpy.curry.curry(renpy.display.layout.Pan)
|
||||
Move = renpy.curry.curry(renpy.display.layout.Move)
|
||||
|
||||
Fade = renpy.curry.curry(renpy.display.transition.Fade)
|
||||
Dissolve = renpy.curry.curry(renpy.display.transition.Dissolve)
|
||||
CropMove = renpy.curry.curry(renpy.display.transition.CropMove)
|
||||
|
||||
def _return(v):
|
||||
"""
|
||||
@@ -98,11 +100,12 @@ class Character(object):
|
||||
self.window_style = window_style
|
||||
self.properties = properties
|
||||
|
||||
def __call__(self, what):
|
||||
def __call__(self, what, interact=True):
|
||||
renpy.display_say(self.name, what,
|
||||
who_style=self.who_style,
|
||||
what_style=self.what_style,
|
||||
window_style=self.window_style,
|
||||
interact=interact,
|
||||
**self.properties)
|
||||
|
||||
class DynamicCharacter(object):
|
||||
@@ -134,7 +137,7 @@ class DynamicCharacter(object):
|
||||
self.window_style = window_style
|
||||
self.properties = properties
|
||||
|
||||
def __call__(self, what):
|
||||
def __call__(self, what, interact=True):
|
||||
import renpy.python as python
|
||||
|
||||
renpy.display_say(python.py_eval(self.name_expr),
|
||||
@@ -142,13 +145,14 @@ class DynamicCharacter(object):
|
||||
who_style=self.who_style,
|
||||
what_style=self.what_style,
|
||||
window_style=self.window_style,
|
||||
interact=interact,
|
||||
**self.properties)
|
||||
|
||||
# Conveniently get rid of all the packages we had imported before.
|
||||
import renpy.exports as renpy
|
||||
|
||||
def narrator(what):
|
||||
renpy.display_say(None, what, what_style='say_thought')
|
||||
def narrator(what, interact=True):
|
||||
renpy.display_say(None, what, what_style='say_thought', interact=interact)
|
||||
|
||||
menu = renpy.display_menu
|
||||
|
||||
|
||||
+17
-1
@@ -1,7 +1,15 @@
|
||||
import renpy
|
||||
|
||||
# A list of style prefixes we care about, including no prefix.
|
||||
prefixes = [ 'hover_', 'idle_', '' ]
|
||||
prefixes = [ 'hover_', 'idle_', 'activate_', '' ]
|
||||
|
||||
substitutes = dict(
|
||||
xmargin = [ 'left_margin', 'right_margin' ],
|
||||
ymargin = [ 'top_margin', 'bottom_margin' ],
|
||||
|
||||
xpadding = [ 'left_padding', 'right_padding' ],
|
||||
ypadding = [ 'top_padding', 'bottom_padding' ],
|
||||
)
|
||||
|
||||
class StyleManager(object):
|
||||
"""
|
||||
@@ -61,6 +69,7 @@ class Style(object):
|
||||
"""
|
||||
|
||||
def __getstate__(self):
|
||||
|
||||
return dict(properties=self.properties,
|
||||
prefix=self.prefix,
|
||||
parent=self.parent)
|
||||
@@ -76,6 +85,13 @@ class Style(object):
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
|
||||
if key in substitutes:
|
||||
for i in substitutes[key]:
|
||||
self.__setattr__(i, value)
|
||||
|
||||
|
||||
return
|
||||
|
||||
for prefix in prefixes:
|
||||
prefkey = prefix + key
|
||||
|
||||
|
||||
+104
-24
@@ -16,11 +16,10 @@
|
||||
|
||||
import renpy
|
||||
|
||||
# The current widget (None to add widgets directly to the
|
||||
# transient display list.)
|
||||
current = None
|
||||
# The current widget. (Should never become None.)
|
||||
current = 'transient'
|
||||
|
||||
# A stack of current widgets.
|
||||
# A stack of current widgets and/or layers.
|
||||
current_stack = [ ]
|
||||
|
||||
# True if the current widget should be used at most once.
|
||||
@@ -36,12 +35,12 @@ def interact(**kwargs):
|
||||
@param show_mouse: Should the mouse be shown during this
|
||||
interaction? Only advisory, as this doesn't work reliably.
|
||||
|
||||
@param suppress_overlay: This suppresses the display of the overlay during
|
||||
this interaction.
|
||||
@param suppress_overlay: This suppresses the display of the overlay
|
||||
during this interaction.
|
||||
"""
|
||||
|
||||
if current_stack:
|
||||
raise Exception("ui.interact called with non-empty widget stack. Did you forget a ui.close()?")
|
||||
raise Exception("ui.interact called with non-empty widget/layer stack. Did you forget a ui.close() somewhere?")
|
||||
|
||||
return renpy.game.interface.interact(**kwargs)
|
||||
|
||||
@@ -56,8 +55,8 @@ def add(w, make_current=False, once=False):
|
||||
global current
|
||||
global current_once
|
||||
|
||||
if current is None:
|
||||
renpy.game.context(-1).scene_lists.add('transient', w)
|
||||
if isinstance(current, str):
|
||||
renpy.game.context(-1).scene_lists.add(current, w)
|
||||
else:
|
||||
current.add(w)
|
||||
|
||||
@@ -73,19 +72,38 @@ def add(w, make_current=False, once=False):
|
||||
|
||||
return w
|
||||
|
||||
|
||||
def layer(name):
|
||||
"""
|
||||
This causes widgets to be added to the named layer, until a
|
||||
matching call to ui.close().
|
||||
"""
|
||||
|
||||
global current_once
|
||||
global current
|
||||
|
||||
if not isinstance(current, str):
|
||||
raise Exception("Opening a layer while a widget is open is not allowed.")
|
||||
|
||||
if name not in renpy.config.layers:
|
||||
raise Exception("'%s' is not a known layer." % name)
|
||||
|
||||
current_stack.append(current)
|
||||
current_once = False
|
||||
current = name
|
||||
|
||||
def close():
|
||||
"""
|
||||
Closes the current widget. This means that we will no longer add
|
||||
things to the current widget, but will instead start adding things
|
||||
to the parent of that widget, or the screen if the widget has no
|
||||
parent. Calling this when there is no current widget will lead to
|
||||
an exception being thrown.
|
||||
This closes the currently open widget or layer. If a widget is
|
||||
closed, then we start adding to its parent, or the layer if no
|
||||
parent is open. If a layer is closed, we return to the previously
|
||||
open layer. An error is thrown if we close the last open layer.
|
||||
"""
|
||||
|
||||
global current
|
||||
|
||||
if current is None:
|
||||
raise Exception("ui.close() called when there is no open widget.")
|
||||
if not current_stack:
|
||||
raise Exception("ui.close() called to close the last open layer or widget.")
|
||||
|
||||
if current_once:
|
||||
raise Exception("ui.close() called when expecting a widget.")
|
||||
@@ -148,6 +166,36 @@ def vbox(padding=0, **properties):
|
||||
|
||||
return add(renpy.display.layout.VBox(padding, **properties), True)
|
||||
|
||||
def grid(cols, rows, padding=0, xfill=False, yfill=False, **properties):
|
||||
"""
|
||||
This creates a layout that places widgets in an evenly spaced
|
||||
grid. New widges are added to this vbox unil ui.close() is called.
|
||||
Widgets are added by going from left to right within a single row,
|
||||
and down to the start of the next row when a row is full. All cells
|
||||
must be filled (that is, exactly col * rows widgets must be added to
|
||||
the grid.)
|
||||
|
||||
The children of this widget should have a fixed size that does not
|
||||
vary based on the space allocated to them. Failure to observe this
|
||||
restriction could lead to really odd layouts, or things being
|
||||
rendered off screen.
|
||||
|
||||
Each cell of the grid is exactly the same size. By default, the
|
||||
grid is the smallest size that can accommodate all of its
|
||||
children, but it can be expanded to consume all available space in
|
||||
a given dimension by setting xfill or yfill to True, as appropriate.
|
||||
|
||||
@param cols: The number of columns in this grid.
|
||||
@param rows: The number of rows in this grid.
|
||||
@param padding: The amount of space to leave between rows and columns.
|
||||
@param xfill: True if the grid should consume all available width.
|
||||
@param yfill: True if the grid should consume all available height.
|
||||
"""
|
||||
|
||||
return add(renpy.display.layout.Grid(cols, rows, padding, xfill=xfill, yfill=yfill, **properties), True)
|
||||
|
||||
|
||||
|
||||
def fixed(**properties):
|
||||
"""
|
||||
This creates a layout that places widgets at fixed locations
|
||||
@@ -204,14 +252,34 @@ def saybehavior():
|
||||
"""
|
||||
This is a psuedo-widget that adds the say behavior to the
|
||||
screen. The say behavior is to return True if the left mouse is
|
||||
clicked or enter is pressed. It also returns true in various other
|
||||
cases if the current statement has already been seen. This widget
|
||||
clicked or enter is pressed. It also returns True in various other
|
||||
cases, such as if the current statement has already been seen. This widget
|
||||
should not be added to any other widget, but should instead be
|
||||
only added to the screen itself.
|
||||
"""
|
||||
|
||||
return add(renpy.display.behavior.SayBehavior())
|
||||
|
||||
def pausebehavior(delay, result=False):
|
||||
"""
|
||||
This is a psuedo-widget that adds the pause behavior to the
|
||||
screen. The pause behavior is to return the supplied result when
|
||||
the given number of seconds elapses. This widget should not be
|
||||
added to any other widget, but should instead be only added to the
|
||||
screen itself.
|
||||
|
||||
Please note that this widget will always pause for the given
|
||||
amount of time. If you want a pause that can be interrupted by
|
||||
the user, add in a saybehavior.
|
||||
|
||||
@param delay: The amount of time to pause, in seconds.
|
||||
|
||||
@param result: The result that will be retuned after the delay time
|
||||
elapses.
|
||||
"""
|
||||
|
||||
return add(renpy.display.behavior.PauseBehavior(delay, result))
|
||||
|
||||
def menu(menuitems, **properties):
|
||||
"""
|
||||
This creates a new menu widget. Unlike the menu statement or
|
||||
@@ -221,12 +289,24 @@ def menu(menuitems, **properties):
|
||||
@param menuitems: A list of tuples that are the items to be added
|
||||
to this menu. The first element of a tuple is a string that is
|
||||
used for this menuitem. The second element is the value to be
|
||||
returned from renpy.interact() if this item is selected, or None
|
||||
returned from ui.interact() if this item is selected, or None
|
||||
if this item is a non-selectable caption.
|
||||
"""
|
||||
|
||||
return add(renpy.display.behavior.Menu(menuitems, **properties))
|
||||
|
||||
def input(default, length=None, **properties):
|
||||
"""
|
||||
This creats a new input widget. This widget accepts textual input
|
||||
until the user hits enter, and then returns that text.
|
||||
|
||||
@param default: The default text that fills the input.
|
||||
|
||||
@param length: If set, the maximum number of characters that will be
|
||||
returned by this input.
|
||||
"""
|
||||
|
||||
return add(renpy.display.behavior.Input(default, length=length, **properties))
|
||||
|
||||
def image(filename, **properties):
|
||||
"""
|
||||
@@ -241,7 +321,7 @@ def imagemap(ground, selected, hotspots, unselected=None,
|
||||
"""
|
||||
This is the widget that implements imagemaps. Parameters are
|
||||
roughtly the same as renpy.imagemap. The value of the hotspot is
|
||||
returned when renpy.interact() returns.
|
||||
returned when ui.interact() returns.
|
||||
"""
|
||||
|
||||
return add(renpy.display.image.ImageMap(ground, selected,
|
||||
@@ -254,7 +334,7 @@ def button(clicked=None, **properties):
|
||||
This creates a button that can be clicked by the user. When this
|
||||
button is clicked or otherwise selected, the function supplied as
|
||||
the clicked argument is called. If it returns a value, that value
|
||||
is returned from renpy.interact().
|
||||
is returned from ui.interact().
|
||||
|
||||
Buttons created with this function contain another widget,
|
||||
specifically the next widget to be added. As a convenience, one
|
||||
@@ -272,7 +352,7 @@ def textbutton(text, clicked=None, text_style='button_text', **properties):
|
||||
This creates a button that is labelled with some text. When the
|
||||
button is clicked or otherwise selected, the function supplied as
|
||||
the clicked argument is called. If it returns a value, that value
|
||||
is returned from renpy.interact().
|
||||
is returned from ui.interact().
|
||||
|
||||
@param text: The text of this button.
|
||||
|
||||
@@ -295,7 +375,7 @@ def imagebutton(idle_image, hover_image, clicked=None,
|
||||
while the second is the hover image, which is used when the mouse
|
||||
is over the image. If the button is clicked or otherwise selected,
|
||||
then the clicked argument is called. If it returns a value, that
|
||||
value is returned from renpy.interact().
|
||||
value is returned from ui.interact().
|
||||
|
||||
@param idle_image: The file name of the image used when this
|
||||
button is idle.
|
||||
@@ -337,7 +417,7 @@ def bar(width, height, range, value, clicked=None, **properties):
|
||||
widget. It is called with a single argument, which is the value
|
||||
corresponding to the location at which the mouse button was clicked.
|
||||
If this function returns a value, that value is returned from
|
||||
renpy.interact().
|
||||
ui.interact().
|
||||
|
||||
For best results, if clicked is set then width should be at least
|
||||
twice as big as range.
|
||||
|
||||
+10
@@ -72,6 +72,16 @@ if __name__ == "__main__":
|
||||
|
||||
type, value, tb = sys.exc_info()
|
||||
|
||||
|
||||
print >>f, type.__name__ + ":",
|
||||
print >>f, unicode(e).encode('utf-8')
|
||||
print >>f
|
||||
print >>f, renpy.game.exception_info
|
||||
|
||||
print >>f
|
||||
print >>f, "-- Full Traceback ------------------------------------------------------------"
|
||||
print >>f
|
||||
|
||||
traceback.print_tb(tb, None, sys.stdout)
|
||||
traceback.print_tb(tb, None, f)
|
||||
|
||||
|
||||
+30
-8
@@ -1,6 +1,8 @@
|
||||
init:
|
||||
$ config.window_title = "Test Game"
|
||||
# $ config.profile = True
|
||||
$ config.profile = True
|
||||
|
||||
$ config.debug_sound = True
|
||||
|
||||
# Set up the size of the screen.
|
||||
$ config.screen_width = 800
|
||||
@@ -20,8 +22,8 @@ init:
|
||||
# Styles.
|
||||
$ style.window.background = Frame("frame.png", 120, 25)
|
||||
|
||||
$ style.menu_choice.hover_sound = "chev1.wav"
|
||||
$ style.menu_choice.activate_sound = "chev2.wav"
|
||||
$ style.menu.hover_sound = "chev1.wav"
|
||||
$ style.menu.activate_sound = "chev2.wav"
|
||||
|
||||
$ style.imagemap.hover_sound = "chev3.wav"
|
||||
$ style.imagemap.activate_sound = "chev4.wav"
|
||||
@@ -45,6 +47,8 @@ init:
|
||||
"9a_vhappy.png", 0.25,
|
||||
"9a_concerned.png")
|
||||
|
||||
image movie = Movie()
|
||||
|
||||
# Character objects.
|
||||
$ e = Character('Eileen', color=(200, 255, 200, 255))
|
||||
$ pov = DynamicCharacter('pov_name', color=(255, 0, 0, 255))
|
||||
@@ -78,6 +82,7 @@ label main_menu:
|
||||
# Stuff that happens before the real main menu.
|
||||
|
||||
scene black
|
||||
|
||||
|
||||
show text "American Bishoujo\nPresents" \
|
||||
at Move((0.5, 0.0), (0.5, 0.5), 4.0,
|
||||
@@ -111,14 +116,16 @@ label start:
|
||||
ui.interact()
|
||||
|
||||
|
||||
$ the_value = 10
|
||||
|
||||
label foo:
|
||||
|
||||
|
||||
"Hey, let's make sure that the
|
||||
multi-line text\ \ \ still
|
||||
works right."
|
||||
|
||||
$ renpy.movie_cutscene("On_Your_Mark.mpg", 450.0)
|
||||
$ the_value = 10
|
||||
|
||||
label foo:
|
||||
|
||||
python hide:
|
||||
|
||||
def bar_clicked(value):
|
||||
@@ -130,7 +137,7 @@ label start:
|
||||
ui.textbutton("Continue", clicked = lambda : False)
|
||||
ui.close()
|
||||
|
||||
rv = renpy.interact()
|
||||
rv = ui.interact()
|
||||
|
||||
if rv is not False:
|
||||
store.the_value = rv
|
||||
@@ -139,6 +146,21 @@ label start:
|
||||
|
||||
|
||||
|
||||
$ renpy.movie_start_displayable('On_Your_Mark.mpg', (400, 300))
|
||||
show movie
|
||||
|
||||
p "I'm now showing a movie."
|
||||
|
||||
p "It's using alpha blending and all that."
|
||||
|
||||
p "Bully!"
|
||||
|
||||
$ renpy.movie_stop()
|
||||
hide movie
|
||||
|
||||
|
||||
|
||||
|
||||
p "This is a test."
|
||||
|
||||
$ overtext = 'This is overtext.'
|
||||
|
||||
Reference in New Issue
Block a user