4.4.2
This commit is contained in:
@@ -1,3 +1,60 @@
|
||||
New in 4.4.2
|
||||
------------
|
||||
|
||||
Improved the configurability of the say and menu statements, also
|
||||
changing the way they work. Say statements without a speaker specified
|
||||
are routed through the narrator character, while all menu statements
|
||||
are routed through the menu function. Customizing these functions can
|
||||
customize how Ren'Py interacts with the user, making it a much more
|
||||
flexible engine.
|
||||
|
||||
As part of this, we changed the way character objects work. They are
|
||||
now called directly to display dialogue, rather than having the say
|
||||
method called on them.
|
||||
|
||||
If your game uses variables named "narrator" or "menu", you will need
|
||||
to rename them.
|
||||
|
||||
Changed the way spaces are handled by the text widget. Specifically,
|
||||
they are no longer merged, so it's possible to include spaces in
|
||||
dialogue and thoughts. Please note that the parser still merges
|
||||
adjacent spaces, unless you escape them with a backslash.
|
||||
|
||||
Added a new widget, ui.sizer, that can shrink the amount of space
|
||||
allocated to its child.
|
||||
|
||||
Added two new text properties, first_indent and rest_indent. These
|
||||
properties control how many pixels of indentation to put before each
|
||||
line of text.
|
||||
|
||||
Added a function, color, that can translate a hex triple or quadruple
|
||||
into a Ren'Py color.
|
||||
|
||||
New in 4.4.1
|
||||
------------
|
||||
|
||||
4.4.1 is being released to test some sound fixes, and so it isn't
|
||||
really isn't that complete or well-tested.
|
||||
|
||||
Fixed some longstanding bugs with midi music on Windows. First of all,
|
||||
we now ship with the 1.2.6 version of SDL_mixer, which fixes a bug in
|
||||
1.2.5. That bug caused panning to go to the hard left. We also try to
|
||||
read the windows midi device volumes, and set our volume to match the
|
||||
first one we can read. This should prevent really loud midi music from
|
||||
happening.
|
||||
|
||||
Also, increased the size of the sound buffer to 4096 samples, to
|
||||
prevent skipping.
|
||||
|
||||
Some new features:
|
||||
|
||||
Now, adding interact=False to a Character object will prevent an
|
||||
interaction from occuring.
|
||||
|
||||
Implemented functions to get the amount of time elapsed during the
|
||||
game, renpy.clear_game_runtime() and renpy.get_game_runtime().
|
||||
|
||||
|
||||
New in 4.4
|
||||
----------
|
||||
|
||||
|
||||
+5
-1
@@ -28,6 +28,9 @@ init -250:
|
||||
|
||||
green = (0, 128, 0, 255)
|
||||
|
||||
# Magic.
|
||||
style.default.enable_hover = True
|
||||
|
||||
# Text properties.
|
||||
style.default.font = "Vera.ttf"
|
||||
style.default.antialias = True
|
||||
@@ -38,7 +41,8 @@ init -250:
|
||||
style.default.minwidth = 0
|
||||
style.default.textalign = 0
|
||||
style.default.text_y_fudge = 0
|
||||
style.default.enable_hover = True
|
||||
style.default.first_indent = 0
|
||||
style.default.rest_indent = 0
|
||||
|
||||
# Change this if you're not using Vera 22.
|
||||
if renpy.windows():
|
||||
|
||||
@@ -88,6 +88,9 @@ label start:
|
||||
# that we won the date.
|
||||
$ date = False
|
||||
|
||||
# Clear the game runtime timer, so it doesn't reflect time spent
|
||||
# sitting at the main menu.
|
||||
$ renpy.clear_game_runtime()
|
||||
|
||||
# Start some music playing in the background.
|
||||
$ renpy.music_start('sun-flower-slow-drag.mid')
|
||||
@@ -594,6 +597,10 @@ label ending:
|
||||
who encouraged him.'
|
||||
|
||||
"We can't wait to see what you do with this. Good luck!"
|
||||
|
||||
$ minutes, seconds = divmod(int(renpy.get_game_runtime()), 60)
|
||||
"It took you %(minutes)d minutes and %(seconds)d seconds to
|
||||
finish this demo."
|
||||
|
||||
$ renpy.full_restart()
|
||||
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@ def main():
|
||||
cp("archive_images.bat")
|
||||
cp("run_game.py", license=license)
|
||||
cp("archiver.py", license=license)
|
||||
cp("build_exe.py", license=license)
|
||||
# cp("build_exe.py", license=license)
|
||||
cp("add_from.py", license=license)
|
||||
cp("renpy-mode.el")
|
||||
|
||||
|
||||
@@ -383,6 +383,20 @@ centered "American Bishoujo presents..."
|
||||
centered "The Ren'Py Demo Game"
|
||||
</example>
|
||||
|
||||
<p>
|
||||
The way this is done is that the character object is called with
|
||||
the string to display. The character object doesn't actually have to
|
||||
be a Character object, it can be any python callable. It's responsible
|
||||
for displaying the dialogue to the user. In the case of a thought,
|
||||
the contents of the narrator variable is used to display the text to
|
||||
the user.
|
||||
</p>
|
||||
|
||||
<var name="narrator" value="...">
|
||||
A function that expects a single string as input, that is called to
|
||||
display narration to the user.
|
||||
</var>
|
||||
|
||||
<p>
|
||||
The say statement also takes a with clause that is used to control
|
||||
the transition that is used to introduce the dialogue or
|
||||
@@ -513,6 +527,14 @@ menu what_to_do:
|
||||
the user has chosen to go shopping.
|
||||
</p>
|
||||
|
||||
<var name="menu" value="...">
|
||||
The menu variable contains the function that is called to display
|
||||
the menu to the user. By default, this variable contains a
|
||||
function that passes its only argument to renpy.display_menu. You
|
||||
can read the documentation of that function to find out what menu
|
||||
expects as input.
|
||||
</var>
|
||||
|
||||
<h3>Displaying Images</h3>
|
||||
|
||||
<p>
|
||||
@@ -1726,6 +1748,12 @@ e "Pleased to meet you, %(name)s."
|
||||
|
||||
<!-- func renpy.windows -->
|
||||
|
||||
<!-- func renpy.clear_game_runtime -->
|
||||
|
||||
<!-- func renpy.get_game_runtime -->
|
||||
|
||||
<!-- func color -->
|
||||
|
||||
<p>
|
||||
Finally, there exists an object named renpy.random. This object is
|
||||
a random number generator that implements the <a
|
||||
@@ -2236,6 +2264,20 @@ init:
|
||||
of empty space that should be to the left of each line of
|
||||
text. (To center text, it should be 0.5.)
|
||||
</prop>
|
||||
|
||||
<prop name="first_indent">
|
||||
This is used to give, in pixels, the indentation of the first line
|
||||
of text in the text widget. It can be used to indent the first line of
|
||||
a paragraph of text. (Not that that's a good idea on a computer
|
||||
monitor, better to leave a blank line between paragraphs.)
|
||||
</prop>
|
||||
|
||||
<prop name="rest_indent">
|
||||
This is used to give, in pixels, the indentation of the second
|
||||
and later lines of a text widget. It can be used to give a hanging
|
||||
indent to quoted dialogue.
|
||||
</prop>
|
||||
|
||||
|
||||
<h4>Window Properties</h4>
|
||||
|
||||
@@ -2804,6 +2846,8 @@ automatically present in the game namespace.
|
||||
|
||||
<!-- func ui.button -->
|
||||
|
||||
<!-- func ui.sizer -->
|
||||
|
||||
<h4>Multiple-Child Widgets</h4>
|
||||
|
||||
<!-- func ui.vbox -->
|
||||
|
||||
+2
-2
@@ -2,9 +2,9 @@
|
||||
# order.
|
||||
|
||||
# Some version numbers and things.
|
||||
version = "Ren'Py 4.4 \"Christmas Bonus\""
|
||||
version = "Ren'Py 4.4.2"
|
||||
script_version = 5
|
||||
savegame_suffix = "-2.save"
|
||||
savegame_suffix = "-3.save"
|
||||
|
||||
|
||||
# Can be first, because has no dependencies, and may be imported
|
||||
|
||||
@@ -349,7 +349,14 @@ class Button(renpy.display.layout.Window):
|
||||
if map_event(ev, "button_select"):
|
||||
if inside and self.clicked:
|
||||
renpy.sound.play(self.style.activate_sound)
|
||||
return self.clicked()
|
||||
|
||||
rv = self.clicked()
|
||||
|
||||
if rv is not None:
|
||||
return rv
|
||||
else:
|
||||
raise renpy.display.core.IgnoreEvent()
|
||||
|
||||
|
||||
return super(Button, self).event(ev, x, y)
|
||||
|
||||
@@ -475,7 +482,12 @@ class Bar(renpy.display.core.Displayable):
|
||||
value = max(value, 0)
|
||||
|
||||
|
||||
return self.clicked(value)
|
||||
rv = self.clicked(value)
|
||||
|
||||
if rv is not None:
|
||||
return rv
|
||||
else:
|
||||
raise renpy.display.core.IgnoreEvent()
|
||||
|
||||
def render(self, width, height, st):
|
||||
|
||||
|
||||
+13
-17
@@ -11,6 +11,9 @@ import cStringIO
|
||||
# KEYREPEATEVENT = USEREVENT + 1
|
||||
DISPLAYTIME = USEREVENT + 2
|
||||
|
||||
# The number of msec
|
||||
DISPLAYTIME_INTERVAL = 50
|
||||
|
||||
class IgnoreEvent(Exception):
|
||||
"""
|
||||
Exception that is raised when we want to ignore an event, but
|
||||
@@ -355,10 +358,9 @@ class Display(object):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
# It shouldn't matter if pygame is already initialized.
|
||||
pygame.init()
|
||||
renpy.sound.init()
|
||||
|
||||
pygame.init()
|
||||
|
||||
self.fullscreen = renpy.game.preferences.fullscreen
|
||||
fsflag = 0
|
||||
|
||||
@@ -629,7 +631,7 @@ class Interface(object):
|
||||
# pygame.time.set_timer(KEYREPEATEVENT, renpy.config.skip_delay)
|
||||
|
||||
# Set up display time event.
|
||||
pygame.time.set_timer(DISPLAYTIME, 50)
|
||||
pygame.time.set_timer(DISPLAYTIME, DISPLAYTIME_INTERVAL)
|
||||
|
||||
# Clear some events.
|
||||
pygame.event.clear((MOUSEMOTION, DISPLAYTIME,
|
||||
@@ -734,8 +736,6 @@ class Interface(object):
|
||||
if show_mouse:
|
||||
self.display.draw_mouse()
|
||||
|
||||
# Update the playing music, if necessary.
|
||||
renpy.music.restore()
|
||||
|
||||
# If we need to redraw again, do it if we don't have an
|
||||
# event going on.
|
||||
@@ -753,20 +753,16 @@ class Interface(object):
|
||||
self.profile_time = time.time()
|
||||
|
||||
if ev.type == DISPLAYTIME:
|
||||
pygame.event.clear([DISPLAYTIME])
|
||||
|
||||
events = 1 + len(pygame.event.get([DISPLAYTIME]))
|
||||
|
||||
renpy.game.context().runtime += events * DISPLAYTIME_INTERVAL
|
||||
|
||||
ev = pygame.event.Event(DISPLAYTIME, {},
|
||||
duration=(time.time() - start_time))
|
||||
|
||||
|
||||
# if ev.type == KEYREPEATEVENT:
|
||||
# pygame.time.set_timer(KEYREPEATEVENT, 0)
|
||||
|
||||
|
||||
# i = renpy.display.behavior.is_pressed(pygame.key.get_pressed(),
|
||||
# "repeating")
|
||||
|
||||
# if i:
|
||||
# ev = pygame.event.Event(KEYDOWN, key=i, unicode=u'')
|
||||
# Update the playing music, if necessary.
|
||||
renpy.music.restore()
|
||||
|
||||
# Handle skipping.
|
||||
renpy.display.behavior.skipping(ev)
|
||||
|
||||
+29
-1
@@ -119,7 +119,7 @@ class Container(renpy.display.core.Displayable):
|
||||
class Position(Container):
|
||||
"""
|
||||
Controls the placement of a displayable on the screen, using
|
||||
supplied positon properties. This is the non-curried form of
|
||||
supplied position properties. This is the non-curried form of
|
||||
Position, which should be used when the user has directly created
|
||||
the displayable that will be shown on the screen.
|
||||
"""
|
||||
@@ -531,3 +531,31 @@ class Move(Container):
|
||||
return rv
|
||||
|
||||
|
||||
class Sizer(Container):
|
||||
"""
|
||||
This is a widget that can change the size allocated to the widget that
|
||||
it contains. Please note that it can only shrink the widget, and that
|
||||
not all widgets respond well to having their areas shrunk. (For example,
|
||||
this has no effect on an image.)
|
||||
"""
|
||||
|
||||
def __init__(self, maxwidth, maxheight, child,
|
||||
style='default', **properties):
|
||||
|
||||
super(Sizer, self).__init__()
|
||||
self.add(child)
|
||||
|
||||
self.maxwidth = maxwidth
|
||||
self.maxheight = maxheight
|
||||
|
||||
self.style = renpy.style.Style(style, properties)
|
||||
|
||||
def render(self, width, height, st):
|
||||
|
||||
if self.maxwidth:
|
||||
width = min(width, self.maxwidth)
|
||||
|
||||
if self.maxheight:
|
||||
height = min(height, self.maxheight)
|
||||
|
||||
return super(Sizer, self).render(width, height, st)
|
||||
|
||||
+27
-11
@@ -107,8 +107,12 @@ class Text(renpy.display.core.Displayable):
|
||||
|
||||
maxwidth = 0
|
||||
|
||||
# The indent of the current line.
|
||||
indent = self.style.first_indent
|
||||
|
||||
|
||||
for p in pars:
|
||||
words = p.split()
|
||||
words = p.split(' ')
|
||||
|
||||
line = ""
|
||||
|
||||
@@ -124,12 +128,13 @@ class Text(renpy.display.core.Displayable):
|
||||
|
||||
lw, lh = font.size(line + " " + w)
|
||||
|
||||
if lw < width:
|
||||
if lw + indent < width:
|
||||
line += " " + w
|
||||
maxwidth = max(maxwidth, lw)
|
||||
else:
|
||||
lines.append(line)
|
||||
line = w
|
||||
indent = self.style.rest_indent
|
||||
|
||||
lines.append(line)
|
||||
|
||||
@@ -168,26 +173,38 @@ class Text(renpy.display.core.Displayable):
|
||||
|
||||
lines = laidout.split('\n')
|
||||
|
||||
first_indent = self.style.first_indent
|
||||
rest_indent = self.style.rest_indent
|
||||
|
||||
|
||||
# Common rendering code.
|
||||
def render_lines(x, y, color):
|
||||
|
||||
y += self.style.text_y_fudge
|
||||
|
||||
indent = first_indent
|
||||
|
||||
for l in lines:
|
||||
ls = font.render(l, self.style.antialias, color)
|
||||
lw, lh = ls.get_size()
|
||||
xo = int((self.width - lw) * self.style.textalign)
|
||||
surf.blit(ls, (x + xo, y + font.get_descent()))
|
||||
y += font.get_linesize() + self.style.line_height_fudge
|
||||
|
||||
fudge = 1
|
||||
lw, lh = ls.get_size()
|
||||
lw += indent
|
||||
|
||||
xo = int((self.width - lw) * self.style.textalign)
|
||||
surf.blit(ls, (x + xo + indent, y + font.get_descent()))
|
||||
|
||||
y += font.get_linesize() + self.style.line_height_fudge
|
||||
indent = rest_indent
|
||||
|
||||
|
||||
|
||||
|
||||
# Render drop-shadow.
|
||||
if self.style.drop_shadow:
|
||||
render_lines(dsxo, dsyo + fudge, self.style.drop_shadow_color)
|
||||
render_lines(dsxo, dsyo, self.style.drop_shadow_color)
|
||||
|
||||
# Render foreground.
|
||||
render_lines(0, 0 + fudge, self.style.color)
|
||||
render_lines(0, 0, self.style.color)
|
||||
|
||||
return surf
|
||||
|
||||
@@ -199,8 +216,7 @@ class Text(renpy.display.core.Displayable):
|
||||
if not self.slow:
|
||||
return None
|
||||
|
||||
if ( ev.type == MOUSEBUTTONDOWN and ev.button == 1) or \
|
||||
( ev.type == KEYDOWN and (ev.key == K_RETURN or ev.key == K_SPACE)):
|
||||
if renpy.display.behavior.map_event(ev, "dismiss"):
|
||||
|
||||
self.slow = False
|
||||
raise renpy.display.core.IgnoreEvent()
|
||||
|
||||
@@ -20,6 +20,8 @@ class Context(object):
|
||||
context.
|
||||
|
||||
@ivar rollback: True if this context participates in rollbacks.
|
||||
|
||||
@ivar runtime: The time spent in this context, in milliseconds.
|
||||
"""
|
||||
|
||||
def __init__(self, rollback, context=None):
|
||||
@@ -27,10 +29,12 @@ class Context(object):
|
||||
self.current = None
|
||||
self.return_stack = [ ]
|
||||
self.rollback = rollback
|
||||
self.runtime = 0
|
||||
|
||||
oldsl = None
|
||||
if context:
|
||||
oldsl = context.scene_lists
|
||||
self.runtime = context.runtime
|
||||
|
||||
import renpy.display.core as dcore
|
||||
self.scene_lists = dcore.SceneLists(oldsl)
|
||||
@@ -111,6 +115,7 @@ class Context(object):
|
||||
rv.return_stack = self.return_stack[:]
|
||||
rv.current = self.current
|
||||
rv.scene_lists = self.scene_lists.rollback_copy()
|
||||
rv.runtime = self.runtime
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
+40
-10
@@ -132,7 +132,7 @@ def input(prompt, default='', length=None):
|
||||
|
||||
return interact(win)
|
||||
|
||||
def menu(items, set_expr, window_style='menu_window'):
|
||||
def menu(items, set_expr):
|
||||
"""
|
||||
Displays a menu, and returns to the user the value of the selected
|
||||
choice. Also handles conditions and the menuset.
|
||||
@@ -161,7 +161,7 @@ def menu(items, set_expr, window_style='menu_window'):
|
||||
return None
|
||||
|
||||
# Show the menu.
|
||||
rv = display_menu(items, window_style=window_style)
|
||||
rv = renpy.store.menu(items)
|
||||
|
||||
# If we have a set, fill it in with the label of the chosen item.
|
||||
if set is not None and rv is not None:
|
||||
@@ -203,11 +203,12 @@ def say(who, what):
|
||||
what = what % renpy.game.store
|
||||
|
||||
if who is None:
|
||||
display_say(who, what, what_style='say_thought')
|
||||
elif isinstance(who, (str, unicode)):
|
||||
who = renpy.store.narrator
|
||||
|
||||
if isinstance(who, (str, unicode)):
|
||||
display_say(who, what, what_style='say_dialogue')
|
||||
else:
|
||||
who.say(what)
|
||||
who(what)
|
||||
|
||||
def display_say(who, what, who_style='say_label',
|
||||
what_style='say_dialogue',
|
||||
@@ -216,6 +217,8 @@ def display_say(who, what, who_style='say_label',
|
||||
who_suffix=': ',
|
||||
what_prefix='',
|
||||
what_suffix='',
|
||||
interact=True,
|
||||
slow=True,
|
||||
**properties):
|
||||
"""
|
||||
@param who: Who is saying the dialogue, or None if it's not being
|
||||
@@ -226,6 +229,11 @@ def display_say(who, what, who_style='say_label',
|
||||
For documentation of the various prefixes, suffixes, and styles,
|
||||
please read the documentation for Character.
|
||||
"""
|
||||
|
||||
# If we're going to do an interaction, then saybehavior needs
|
||||
# to be here, right before ui.text.
|
||||
if interact:
|
||||
renpy.ui.saybehavior()
|
||||
|
||||
if who is not None:
|
||||
who = who_prefix + who + who_suffix
|
||||
@@ -238,13 +246,12 @@ def display_say(who, what, who_style='say_label',
|
||||
if who is not None:
|
||||
renpy.ui.text(who, style=who_style, **properties)
|
||||
|
||||
renpy.ui.text(what, style=what_style, slow=True)
|
||||
renpy.ui.text(what, style=what_style, slow=slow)
|
||||
renpy.ui.close()
|
||||
|
||||
renpy.ui.saybehavior()
|
||||
|
||||
interact()
|
||||
checkpoint()
|
||||
if interact:
|
||||
renpy.ui.interact()
|
||||
checkpoint()
|
||||
|
||||
def imagemap(ground, selected, hotspots, unselected=None, overlays=False,
|
||||
style='imagemap', **properties):
|
||||
@@ -428,6 +435,29 @@ def transition(trans):
|
||||
else:
|
||||
renpy.game.interface.set_transition(trans)
|
||||
|
||||
def clear_game_runtime():
|
||||
"""
|
||||
Resets the game runtime timer down to 0.
|
||||
|
||||
The game runtime counter counts the number of seconds that have
|
||||
elapsed while waiting for user input in the current context. (So
|
||||
it doesn't count time spent in the game menu.)
|
||||
"""
|
||||
|
||||
renpy.game.context().runtime = 0
|
||||
|
||||
def get_game_runtime():
|
||||
"""
|
||||
Returns the number of seconds that have elapsed in gameplay since
|
||||
the last call to clear_game_timer, as a float.
|
||||
|
||||
The game runtime counter counts the number of seconds that have
|
||||
elapsed while waiting for user input in the current context. (So
|
||||
it doesn't count time spent in the game menu.)
|
||||
"""
|
||||
|
||||
return renpy.game.context().runtime / 1000.0
|
||||
|
||||
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
@@ -146,7 +146,7 @@ def main(basepath):
|
||||
renpy.config.backup()
|
||||
|
||||
# Load the script.
|
||||
game.script = renpy.script.load_script(game.basepath)
|
||||
game.script = renpy.script.load_script()
|
||||
|
||||
# Start things running.
|
||||
|
||||
|
||||
+139
@@ -2,6 +2,133 @@
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
@@ -61,6 +188,10 @@ def restore():
|
||||
"""
|
||||
|
||||
global current_music
|
||||
global fading
|
||||
|
||||
compute_midi_msf()
|
||||
set_music_volume(1.0)
|
||||
|
||||
new_music = renpy.game.context().scene_lists.music
|
||||
|
||||
@@ -75,11 +206,19 @@ def restore():
|
||||
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:
|
||||
|
||||
+9
-7
@@ -35,7 +35,7 @@ class Script(object):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, dir):
|
||||
def __init__(self, node_callback=None):
|
||||
"""
|
||||
Loads the script by parsing all of the given files, and then
|
||||
walking the various ASTs to initialize this Script object.
|
||||
@@ -80,12 +80,12 @@ class Script(object):
|
||||
|
||||
# print "Loading", fn
|
||||
|
||||
if self.load_file(fn):
|
||||
if self.load_file(fn, node_callback):
|
||||
continue
|
||||
|
||||
print "Couldn't load %s, trying %s instead." % (fn, alt)
|
||||
|
||||
if self.load_file(alt):
|
||||
if self.load_file(alt, node_callback):
|
||||
continue
|
||||
|
||||
raise Exception("Could not load %s or %s." % (fn, alt))
|
||||
@@ -95,7 +95,7 @@ class Script(object):
|
||||
|
||||
# Do some generic init here.
|
||||
|
||||
def load_file(self, fn):
|
||||
def load_file(self, fn, node_callback):
|
||||
|
||||
if fn.endswith(".rpy"):
|
||||
stmts = renpy.parser.parse(fn)
|
||||
@@ -135,6 +135,9 @@ class Script(object):
|
||||
# Check each node individually.
|
||||
for node in all_stmts:
|
||||
|
||||
if node_callback:
|
||||
node_callback(node)
|
||||
|
||||
# Check to see if the name is defined twice. If it is,
|
||||
# report the error.
|
||||
name = node.name
|
||||
@@ -174,9 +177,8 @@ class Script(object):
|
||||
|
||||
return label in self.namemap
|
||||
|
||||
def load_script(dir):
|
||||
|
||||
rv = Script(dir)
|
||||
def load_script():
|
||||
rv = Script()
|
||||
return rv
|
||||
|
||||
|
||||
|
||||
+13
-3
@@ -7,10 +7,20 @@ from pygame.constants import *
|
||||
|
||||
def init():
|
||||
try:
|
||||
pygame.mixer.init(renpy.config.sound_sample_rate)
|
||||
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:
|
||||
if renpy.config.debug_sound:
|
||||
raise
|
||||
try:
|
||||
pygame.mixer.pre_init()
|
||||
except:
|
||||
if renpy.config.debug_sound:
|
||||
raise
|
||||
|
||||
def play(fn, loops=0):
|
||||
"""
|
||||
|
||||
+54
-5
@@ -79,12 +79,17 @@ class Character(object):
|
||||
few other keyword parameters:
|
||||
|
||||
@param who_prefix: A prefix that is prepended to the name.
|
||||
|
||||
@param who_suffix: A suffix that is appended to the name.
|
||||
|
||||
@param who_suffix: A suffix that is appended to the name. (Defaults to ':')
|
||||
|
||||
@param what_prefix: A prefix that is prepended to the text body.
|
||||
|
||||
@param what_suffix: A suffix that is appended to the text body.
|
||||
|
||||
@param interact: If True (the default), then each line said
|
||||
through this character causes an interaction. If False, then
|
||||
the window is added to the screen, but control immediately
|
||||
proceeds. You'll need to call ui.interact yourself to show it.
|
||||
"""
|
||||
|
||||
self.name = name
|
||||
@@ -93,7 +98,7 @@ class Character(object):
|
||||
self.window_style = window_style
|
||||
self.properties = properties
|
||||
|
||||
def say(self, what):
|
||||
def __call__(self, what):
|
||||
renpy.display_say(self.name, what,
|
||||
who_style=self.who_style,
|
||||
what_style=self.what_style,
|
||||
@@ -129,7 +134,7 @@ class DynamicCharacter(object):
|
||||
self.window_style = window_style
|
||||
self.properties = properties
|
||||
|
||||
def say(self, what):
|
||||
def __call__(self, what):
|
||||
import renpy.python as python
|
||||
|
||||
renpy.display_say(python.py_eval(self.name_expr),
|
||||
@@ -139,10 +144,54 @@ class DynamicCharacter(object):
|
||||
window_style=self.window_style,
|
||||
**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')
|
||||
|
||||
menu = renpy.display_menu
|
||||
|
||||
def color(s):
|
||||
"""
|
||||
This function converts a hexcode into a color/alpha tuple. Leading
|
||||
# marks are ignored. Colors can be rgb or rgba, with each element having
|
||||
either one or two digits. (So the strings can be 3, 4, 6, or 8 digits long,
|
||||
not including the optional #.) A missing alpha is interpreted as 255,
|
||||
fully opaque.
|
||||
|
||||
For example, color('#123a') returns (17, 34, 51, 170), while
|
||||
color('c0c0c0') returns (192, 192, 192, 255).
|
||||
"""
|
||||
|
||||
if s[0] == '#':
|
||||
s = s[1:]
|
||||
|
||||
if len(s) == 6:
|
||||
r = int(s[0]+s[1], 16)
|
||||
g = int(s[2]+s[3], 16)
|
||||
b = int(s[4]+s[5], 16)
|
||||
a = 255
|
||||
elif len(s) == 8:
|
||||
r = int(s[0]+s[1], 16)
|
||||
g = int(s[2]+s[3], 16)
|
||||
b = int(s[4]+s[5], 16)
|
||||
a = int(s[6]+s[7], 16)
|
||||
elif len(s) == 3:
|
||||
r = int(s[0], 16) * 0x11
|
||||
g = int(s[1], 16) * 0x11
|
||||
b = int(s[2], 16) * 0x11
|
||||
a = 255
|
||||
elif len(s) == 4:
|
||||
r = int(s[0], 16) * 0x11
|
||||
g = int(s[1], 16) * 0x11
|
||||
b = int(s[2], 16) * 0x11
|
||||
a = int(s[3], 16) * 0x11
|
||||
else:
|
||||
raise Exception("Argument to color() must be 3, 4, 6, or 8 hex digits long.")
|
||||
|
||||
return (r, g, b, a)
|
||||
|
||||
# The default transition.
|
||||
default_transition = None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user