Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5ea9d436ce | |||
| c05e4e528e | |||
| e10488b77a | |||
| 86e0cb3331 | |||
| c6397c08af | |||
| f9db64231d | |||
| f3c3c67def | |||
| 6d316a9c78 | |||
| 6e7e68b31a | |||
| a24e6b2af1 | |||
| 597b01c26b | |||
| a262eb0f60 |
+186
@@ -0,0 +1,186 @@
|
||||
|
||||
New in 4.2
|
||||
----------
|
||||
|
||||
Ren'Py now ships with a common directory that is used to store the
|
||||
library files, as well as the files needed by a minimal game. (Such as
|
||||
a default font.) The idea here is to allow one to copy a game
|
||||
directory from one version of Ren'Py to another, and have it just
|
||||
work.
|
||||
|
||||
One can upgrade from 4.1 to 4.2 by deleting script.rpy, script.rpyc,
|
||||
library.rpy, and library.rpyc, and then copying in the game
|
||||
directory.
|
||||
|
||||
We've also eliminated the default background images. To go back to the
|
||||
images used with Ren'Py 4.1, add the following lines to your script,
|
||||
and copy the images out of the 4.1 distribution, or your 4.1 based
|
||||
game.
|
||||
|
||||
[code]
|
||||
init:
|
||||
$ style.mm_root_window.background = Image("mainmenu.jpg")
|
||||
$ style.gm_root_window.background = Image("gamemenu.jpg")
|
||||
$ style.window.background = Frame("frame.png", 125, 25)
|
||||
[/code]
|
||||
|
||||
Added a new variable, config.hard_rollback_limit, which limits the
|
||||
number of steps the user can rollback the game, interactively. This
|
||||
limit now defaults to 10 steps. (suggested by Grey)
|
||||
|
||||
Added a second parameter to renpy.pause, that allows us to pause until
|
||||
a particular offset in the background music is reached. (suggested by
|
||||
Alessio)
|
||||
|
||||
Added keyboard mouse controls that can be used to move the mouse
|
||||
around on the game and main menus. This allows us to manipulate the
|
||||
game and main menus without having to touch the mouse. This is mostly
|
||||
for completeness. (suggested by Alessio)
|
||||
|
||||
Changed the way in which overlays work. We now have the variable
|
||||
config.overlay_functions, which contains a list of functions, each of
|
||||
which returns a list of displayables that will be added to the
|
||||
screen. (sorta-kinda suggested by Alessio)
|
||||
|
||||
Removed renpy.set_overlay, since config.overlay_functions is a more
|
||||
flexible way of doing the same thing.
|
||||
|
||||
Now, defining the label main_menu allows you to totally replace the
|
||||
main menu with something of your own devising. Changed the
|
||||
documentation to reflect this, as well as the changes in startup that
|
||||
happened in 4.1.
|
||||
|
||||
Added a program called "add_from", which adds from clauses to all bare
|
||||
call statements found in the program.
|
||||
|
||||
Replaced the menu_selected and menu_unselected styles with a single
|
||||
menu_choice style which participates in the hover protocol. This may
|
||||
change user code, if you change the menu colors. To fix this, replace
|
||||
style.menu_selected.color with style.menu_choice.hover_color, and
|
||||
style.menu_unselected.color with style.menu_choice.idle_color.
|
||||
|
||||
Added the ability to play sound effects, by calling the renpy.play
|
||||
function. Added a preference that allows the user to control if
|
||||
sound effects are played or not.
|
||||
|
||||
Added sound styles, which are used to define the sounds that are
|
||||
played when buttons, imagemaps, and menu choices are hovered and
|
||||
activated. (By default, no such sounds are played.)
|
||||
|
||||
Implemented that annoying thing where text is typed on the screen one
|
||||
character at a time. Also added a preference that allows the user to
|
||||
disable it. Used all the restraint I could muster to avoid defaulting
|
||||
that preference to off.
|
||||
|
||||
Added the ability to fade out music when changing music
|
||||
tracks. This is controlled by the config.fade_music variable.
|
||||
(suggested by Alessio)
|
||||
|
||||
Added parameterized images, and the ability to show text as image
|
||||
using a command like:
|
||||
|
||||
[code]
|
||||
show text "American Bishoujo Presents..."
|
||||
[/code]
|
||||
|
||||
Added a Move function, which can be used in at to allow an image to be
|
||||
moved on the screen. This is best used when the image is smaller than
|
||||
the screen. (suggested by Alessio)
|
||||
|
||||
Changed the format of archive files, to make them somewhat harder to
|
||||
reverse-engineer. This requires the user to regenerate the archive
|
||||
files. To make this easier, we now ship a batch file (called
|
||||
archive_images.bat) that automatically archives the images found in
|
||||
the game directory.
|
||||
|
||||
Added a new function renpy.wait that is equivalent to the wait
|
||||
statement, and made it and renpy.pause return True if they are
|
||||
interrupted, or False if the run to their natural completions. This
|
||||
now makes it possible to jump somewhere else when the user click,
|
||||
rather than blindly going to the next label. For example:
|
||||
|
||||
[code]
|
||||
# Actually, this runs before the library main menu.
|
||||
label main_menu:
|
||||
scene black
|
||||
|
||||
show text "American Bishoujo\nPresents" \
|
||||
at Move((0.5, 0.0), (0.5, 0.5), 3.0,
|
||||
xanchor='center', yanchor='bottom')
|
||||
|
||||
if renpy.pause(6):
|
||||
jump _library_main_menu
|
||||
|
||||
show text "A PyTom Game" at Position(xpos=0.5, ypos=0.5,
|
||||
xanchor="center", yanchor="bottom")
|
||||
|
||||
if renpy.with(fade) or renpy.pause(4):
|
||||
jump _library_main_menu
|
||||
|
||||
$ renpy.transition(fade)
|
||||
jump _library_main_menu
|
||||
[code]
|
||||
|
||||
|
||||
|
||||
Updated renpy-mode.el to add imenu support, so (X)Emacs users can use
|
||||
the speedbar to jump around in a script. If you don't know what this
|
||||
means, you probably don't care.
|
||||
|
||||
Improved the comments in the demo script, to make it a better
|
||||
example. Added a syntax-highlighted version of the demo script to the
|
||||
tutorial, to make it easier on peoples eyes.
|
||||
|
||||
|
||||
|
||||
New in 4.1
|
||||
----------
|
||||
|
||||
Added the ability to customize the main menu by giving a list of
|
||||
buttons on the main menu and the labels that they jump the user to.
|
||||
|
||||
Added the ability to center-click to hide any displayed text or menus.
|
||||
|
||||
Added the ability to have a color mouse that we can move around the
|
||||
screen.
|
||||
|
||||
Added a persistent object, which has fields that are persistent across
|
||||
games. Made the preferences part of this object, so that they are
|
||||
persisted across sessions. For simplicity's sake, made seen_ever part
|
||||
of that persistent object.
|
||||
|
||||
Added a special character called "centered", which causes the text it
|
||||
displays to be centered in the screen without any window. Added two
|
||||
new styles (centered_window and centered_text), which are used for
|
||||
this, and a new property, textalign, which controls the horizontal
|
||||
alignment of text within the Text object itself.
|
||||
|
||||
Keywords are now special in all contexts. So you can no longer include
|
||||
keywords in image names. Sorry.
|
||||
|
||||
Improved parse error messages. They now include the text of the
|
||||
line and a caret indication the location in the line where the parse
|
||||
error occurred.
|
||||
|
||||
Added a with clause to say statements and menus. This lets one display
|
||||
dialogue, thoughts, or menus using a transition. In conjunction with
|
||||
this, changed a bit the semantics of with statements, and with clauses
|
||||
on show, hide, and scene statements. Rewrote the section on
|
||||
transitions in the documentation to reflect the new reality.
|
||||
|
||||
Added the ability to translate the text of the game menu. Together
|
||||
with the ability to change the main menu, this makes Ren'Py fully
|
||||
localizable, except for error messages. Check out the Localization
|
||||
section in the tutorial for more details.
|
||||
|
||||
Added confirmations for quitting and overwriting a save game. This
|
||||
also includes quitting by trying to close the window.
|
||||
|
||||
Added a preferences screen, which lets users change the Ren'Py
|
||||
preferences. Right now, this includes Windowed/Fullscreen, Music
|
||||
Enabled/Disabled, CTRL Skips Unread/All messages, and Transitions
|
||||
controls which transitions are performed.
|
||||
|
||||
Added a Pan function which can be used in an at clause. This allows us
|
||||
to pan over a background image.
|
||||
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# This program adds froms to every unqualified call found in the game
|
||||
# directory. It's not perfect, but it's better than nothing.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
|
||||
def find_labels(fn, labels):
|
||||
|
||||
f = file(fn, "r")
|
||||
|
||||
for l in f:
|
||||
|
||||
m = re.match(r'^\s*call\s+\w+\s+from\s+(\w+)\s*$', l)
|
||||
if m:
|
||||
labels[m.group(1)] = True
|
||||
|
||||
f.close()
|
||||
|
||||
def replace_labels(fn, labels):
|
||||
|
||||
f = file(fn, "r")
|
||||
of = file(fn + ".new", "w")
|
||||
|
||||
|
||||
def replaceit(m):
|
||||
target = m.group(2)
|
||||
|
||||
num = 0
|
||||
|
||||
while True:
|
||||
num += 1
|
||||
label = "_call_%s_%d" % (target, num)
|
||||
|
||||
if label not in labels:
|
||||
break
|
||||
|
||||
labels[label] = True
|
||||
|
||||
return m.group(1) + "call " + target + " from " + label + m.group(3)
|
||||
|
||||
for l in f:
|
||||
l = re.sub(r'^(\s*)call\s+(\w+)(\s*$)', replaceit, l)
|
||||
of.write(l)
|
||||
|
||||
f.close()
|
||||
of.close()
|
||||
|
||||
os.rename(fn, fn + ".bak")
|
||||
os.rename(fn + ".new", fn)
|
||||
|
||||
def main():
|
||||
|
||||
gamedir = "game"
|
||||
|
||||
if len(sys.argv) >= 2:
|
||||
gamedir = sys.argv[1]
|
||||
|
||||
print "Processing files in", gamedir
|
||||
|
||||
files = [ gamedir + "/" + i for i in os.listdir(gamedir)
|
||||
if i.endswith(".rpy") ]
|
||||
|
||||
labels = { }
|
||||
|
||||
for fn in files:
|
||||
print "Finding labels in", fn
|
||||
find_labels(fn, labels)
|
||||
|
||||
for fn in files:
|
||||
print "Replacing labels in", fn
|
||||
replace_labels(fn, labels)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,2 @@
|
||||
cd images
|
||||
..\archiver.exe images *.png *.jpg
|
||||
+45
-5
@@ -7,9 +7,27 @@
|
||||
import sys
|
||||
import os
|
||||
import encodings.zlib_codec
|
||||
import random
|
||||
|
||||
from cPickle import loads, dumps, HIGHEST_PROTOCOL
|
||||
|
||||
# The most we will go without inserting some padding. 10k.
|
||||
padding_every = 10240
|
||||
|
||||
# The amount of padding we will add.
|
||||
padding_max = 32
|
||||
|
||||
def randpadding():
|
||||
|
||||
plen = random.randint(1, padding_max)
|
||||
|
||||
rv = ""
|
||||
|
||||
for i in range(0, plen):
|
||||
rv += chr(random.randint(1, 255))
|
||||
|
||||
return rv
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print "Usage: %s <file-prefix> <files ...>" % sys.argv[0]
|
||||
@@ -25,18 +43,40 @@ def main():
|
||||
|
||||
index = { }
|
||||
|
||||
random.seed()
|
||||
|
||||
offset = 0
|
||||
|
||||
for fn in sys.argv[2:]:
|
||||
index[fn] = [ ]
|
||||
|
||||
print "Adding %s..." % fn
|
||||
|
||||
data = file(fn, "rd").read()
|
||||
dlen = len(data)
|
||||
datafile = file(fn, "rb")
|
||||
|
||||
archivef.write(data)
|
||||
while True:
|
||||
|
||||
index[fn] = (offset, dlen)
|
||||
offset += dlen
|
||||
# Pad with junk.
|
||||
padding = randpadding()
|
||||
archivef.write(padding)
|
||||
offset += len(padding)
|
||||
|
||||
# Pick a random block size.
|
||||
block = random.randint(1, padding_every)
|
||||
|
||||
data = datafile.read(block)
|
||||
|
||||
if not data:
|
||||
break
|
||||
|
||||
dlen = len(data)
|
||||
|
||||
archivef.write(data)
|
||||
|
||||
index[fn].append((offset, dlen))
|
||||
offset += dlen
|
||||
|
||||
datafile.close()
|
||||
|
||||
archivef.close()
|
||||
|
||||
|
||||
+23
-3
@@ -2,10 +2,30 @@
|
||||
|
||||
from distutils.core import setup
|
||||
import py2exe
|
||||
import sys
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print "Usage: build_exe.py <prefix>"
|
||||
print ""
|
||||
print "Builds <prefix>.exe."
|
||||
print "Expects icons in <prefix>.ico."
|
||||
|
||||
sys.exit(-1)
|
||||
|
||||
def program(name):
|
||||
return dict(script="run_game.py",
|
||||
icon_resources=[ (0, name + ".ico"),
|
||||
],
|
||||
dest_base=name)
|
||||
|
||||
programs = [
|
||||
program(sys.argv[1]),
|
||||
]
|
||||
|
||||
sys.argv[1:] = [ 'py2exe' ]
|
||||
|
||||
setup(name="RenPy",
|
||||
windows=[ dict(script="run_game.py",
|
||||
icon_resources=[(1, "icon.ico")]) ],
|
||||
console=[ "archiver.py" ],
|
||||
windows=programs,
|
||||
console=[ "archiver.py", "add_from.py" ],
|
||||
zipfile='lib/renpy.zip',
|
||||
)
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,55 @@
|
||||
This package was debianized by Michael Fedrowitz <michaelf@debian.org>
|
||||
on Sun, 23 Feb 2003.
|
||||
|
||||
It was downloaded from
|
||||
'http://ftp.gnome.org/pub/GNOME/sources/ttf-bitstream-vera/'.
|
||||
|
||||
Upstream Author: Bitstream, Inc.
|
||||
|
||||
Copyright:
|
||||
|
||||
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
|
||||
Vera is a trademark of Bitstream, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the fonts accompanying this license ("Fonts") and associated
|
||||
documentation files (the "Font Software"), to reproduce and distribute
|
||||
the Font Software, including without limitation the rights to use,
|
||||
copy, merge, publish, distribute, and/or sell copies of the Font
|
||||
Software, and to permit persons to whom the Font Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright and trademark notices and this permission notice
|
||||
shall be included in all copies of one or more of the Font Software
|
||||
typefaces.
|
||||
|
||||
The Font Software may be modified, altered, or added to, and in
|
||||
particular the designs of glyphs or characters in the Fonts may be
|
||||
modified and additional glyphs or characters may be added to the
|
||||
Fonts, only if the fonts are renamed to names not containing either
|
||||
the words "Bitstream" or the word "Vera".
|
||||
|
||||
This License becomes null and void to the extent applicable to Fonts
|
||||
or Font Software that has been modified and is distributed under the
|
||||
"Bitstream Vera" names.
|
||||
|
||||
The Font Software may be sold as part of a larger software package but
|
||||
no copy of one or more of the Font Software typefaces may be sold by
|
||||
itself.
|
||||
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
|
||||
BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
|
||||
OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
|
||||
SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the names of Gnome, the Gnome
|
||||
Foundation, and Bitstream Inc., shall not be used in advertising or
|
||||
otherwise to promote the sale, use or other dealings in this Font
|
||||
Software without prior written authorization from the Gnome Foundation
|
||||
or Bitstream Inc., respectively. For further information, contact:
|
||||
fonts at gnome dot org.
|
||||
@@ -32,10 +32,32 @@ init -500:
|
||||
# The height of a thumbnail.
|
||||
library.thumbnail_height = 75
|
||||
|
||||
# The contents of the main menu.
|
||||
library.main_menu = [
|
||||
( "Start Game", "start" ),
|
||||
( "Continue Game", "_continue" ),
|
||||
( "Quit Game", "_quit" ),
|
||||
]
|
||||
|
||||
# Used to translate strings in the library.
|
||||
library.translations = { }
|
||||
|
||||
# This is updated to give the user an idea of where a save is
|
||||
# taking place.
|
||||
save_name = ''
|
||||
|
||||
|
||||
# The function that's used to translate strings in the game menu.
|
||||
init:
|
||||
python:
|
||||
def _(s):
|
||||
"""
|
||||
Translates s into another language or something.
|
||||
"""
|
||||
|
||||
if s in library.translations:
|
||||
return library.translations[s]
|
||||
else:
|
||||
return s
|
||||
|
||||
##############################################################################
|
||||
|
||||
@@ -46,17 +68,28 @@ init:
|
||||
def _screenshot():
|
||||
renpy.screenshot("screenshot.bmp")
|
||||
|
||||
# Are the windows currently hidden?
|
||||
_windows_hidden = False
|
||||
|
||||
# Hides the windows.
|
||||
def _hide_windows():
|
||||
global _windows_hidden
|
||||
|
||||
# Installs the keymap that lets the user do things like invoke the
|
||||
# game menu.
|
||||
label _install_keymap:
|
||||
|
||||
if _windows_hidden:
|
||||
return
|
||||
|
||||
try:
|
||||
_windows_hidden = True
|
||||
renpy.interact(renpy.SayBehavior())
|
||||
finally:
|
||||
_windows_hidden = False
|
||||
|
||||
# A keymouse object that we use on the mainmenu and gamemenu
|
||||
# screens.
|
||||
_keymouse = renpy.KeymouseBehavior()
|
||||
|
||||
# Set up the default keymap.
|
||||
python hide:
|
||||
config.underlay = [ ]
|
||||
config.overlay = [ ]
|
||||
|
||||
# The default keymap.
|
||||
km = renpy.Keymap(
|
||||
K_PAGEUP = renpy.rollback,
|
||||
@@ -66,37 +99,34 @@ label _install_keymap:
|
||||
m = renpy.toggle_music,
|
||||
K_ESCAPE = renpy.curried_call_in_new_context("_game_menu"),
|
||||
mouse_3 = renpy.curried_call_in_new_context("_game_menu"),
|
||||
mouse_2 = _hide_windows,
|
||||
)
|
||||
|
||||
config.underlay.append(km)
|
||||
config.underlay = [ km ]
|
||||
|
||||
return
|
||||
|
||||
# This is the true starting point of the program. Sssh... Don't
|
||||
# tell anyone.
|
||||
label _start:
|
||||
|
||||
$ _started = False
|
||||
|
||||
call _main_menu
|
||||
|
||||
$ _started = True
|
||||
call _install_keymap
|
||||
|
||||
jump start
|
||||
jump _main_menu
|
||||
|
||||
# This shows the main menu to the user.
|
||||
label _main_menu:
|
||||
label _main_menu:
|
||||
|
||||
# jump start
|
||||
# Let the user completely override the main menu.
|
||||
if renpy.has_label("main_menu"):
|
||||
jump main_menu
|
||||
|
||||
label _library_main_menu:
|
||||
|
||||
python hide:
|
||||
|
||||
# Show the main menu screen.
|
||||
vbox = renpy.VBox()
|
||||
|
||||
vbox.add(renpy.TextButton('New Game', clicked=_return("start")))
|
||||
vbox.add(renpy.TextButton('Continue Game', clicked=_return("continue")))
|
||||
vbox.add(renpy.TextButton('Quit', clicked=_return("quit")))
|
||||
for text, label in library.main_menu:
|
||||
vbox.add(renpy.TextButton(text, clicked=_return(label)))
|
||||
|
||||
menu_window = renpy.Window(vbox, style='mm_menu_window')
|
||||
|
||||
@@ -105,17 +135,22 @@ label _main_menu:
|
||||
|
||||
root_window = renpy.Window(fixed, style='mm_root_window')
|
||||
|
||||
store._result = renpy.interact(root_window)
|
||||
store._result = renpy.interact(_keymouse, root_window,
|
||||
suppress_overlay=True,
|
||||
suppress_underlay=True)
|
||||
|
||||
if _result == "start":
|
||||
pass
|
||||
elif _result == "continue":
|
||||
$ renpy.call_in_new_context("_game_menu")
|
||||
elif _result == "quit":
|
||||
$ renpy.quit()
|
||||
# Computed jump to the appropriate label.
|
||||
$ renpy.jump(_result)
|
||||
|
||||
return
|
||||
|
||||
# Used to call the game menu.
|
||||
label _continue:
|
||||
$ renpy.call_in_new_context("_load_menu")
|
||||
|
||||
jump _library_main_menu
|
||||
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Code for the game menu.
|
||||
@@ -128,12 +163,12 @@ init -500:
|
||||
def _game_nav(selected):
|
||||
|
||||
buttons = [
|
||||
( "return", "Return to Game", "_return"),
|
||||
( "load", "Load Game", "_load_screen" ),
|
||||
( "save", "Save Game", "_save_screen" ),
|
||||
# ( "prefs", "Preferences", "_prefs_screen" ),
|
||||
( "mainmenu", "Main Menu", "_full_restart" ),
|
||||
( "quit", "Quit Game", "_quit" ),
|
||||
( "return", _("Return to Game"), "_return"),
|
||||
( "load", _("Load Game"), "_load_screen" ),
|
||||
( "save", _("Save Game"), "_save_screen" ),
|
||||
( "prefs", _("Preferences"), "_prefs_screen" ),
|
||||
( "mainmenu", _("Main Menu"), "_full_restart" ),
|
||||
( "quit", _("Quit Game"), "_confirm_quit" ),
|
||||
]
|
||||
|
||||
vbox = renpy.VBox()
|
||||
@@ -167,21 +202,24 @@ init -500:
|
||||
for w in widgets:
|
||||
fixed.add(w)
|
||||
|
||||
return renpy.interact(win)
|
||||
return renpy.interact(_keymouse, win,
|
||||
suppress_underlay=True,
|
||||
suppress_overlay=True
|
||||
)
|
||||
|
||||
_file_picker_index = 0
|
||||
|
||||
def _render_filename(filename, newest_filename):
|
||||
|
||||
if filename is None:
|
||||
return renpy.Text("Save in new slot.", style='file_picker_new_slot')
|
||||
return renpy.Text(_("Save in new slot."), style='file_picker_new_slot')
|
||||
|
||||
hbox = renpy.HBox(padding=library.padding)
|
||||
|
||||
if filename == newest_filename:
|
||||
hbox.add(renpy.Text("New", style='file_picker_new'))
|
||||
hbox.add(renpy.Text(_("New"), style='file_picker_new'))
|
||||
else:
|
||||
hbox.add(renpy.Text("Old", style='file_picker_old'))
|
||||
hbox.add(renpy.Text(_("Old"), style='file_picker_old'))
|
||||
|
||||
hbox.add(renpy.load_screenshot(filename))
|
||||
|
||||
@@ -223,9 +261,9 @@ init -500:
|
||||
|
||||
|
||||
hbox.add(tb(fpi > 0,
|
||||
'Previous Page', _return(("fpidelta", -1))))
|
||||
_('Previous Page'), _return(("fpidelta", -1))))
|
||||
hbox.add(tb(fpi + library.file_page_length < len(files),
|
||||
'Next Page', _return(("fpidelta", +1))))
|
||||
_('Next Page'), _return(("fpidelta", +1))))
|
||||
vbox.add(hbox)
|
||||
|
||||
for i in cur_files:
|
||||
@@ -249,41 +287,122 @@ init -500:
|
||||
if type == "fpidelta":
|
||||
store._file_picker_index += value * library.file_page_length
|
||||
|
||||
def _yesno_prompt(screen, message):
|
||||
|
||||
prompt = renpy.Text(message, style='yesno_prompt')
|
||||
yes = renpy.TextButton(_("Yes"), style='yesno_yes',
|
||||
clicked=_return(True))
|
||||
no = renpy.TextButton(_("No"), style='yesno_no',
|
||||
clicked=_return(False))
|
||||
|
||||
return _game_interact(screen, prompt, yes, no)
|
||||
|
||||
# Returns a button for a single preference and value.
|
||||
def _prefbutton(label, var, value):
|
||||
|
||||
def clicked():
|
||||
setattr(_preferences, var, value)
|
||||
return True
|
||||
|
||||
style = 'button'
|
||||
text_style = 'button_text'
|
||||
|
||||
if getattr(_preferences, var) == value:
|
||||
style = 'selected_button'
|
||||
text_style = 'selected_button_text'
|
||||
|
||||
return renpy.TextButton(_(label), style=style,
|
||||
text_style=text_style, clicked=clicked)
|
||||
|
||||
# Returns a vbox for a single preference.
|
||||
def _prefvbox(label, var, entries):
|
||||
|
||||
rv = renpy.VBox(style='prefs_pref')
|
||||
rv.add(renpy.Text(_(label), style='prefs_label'))
|
||||
|
||||
for blabel, value in entries:
|
||||
rv.add(_prefbutton(blabel, var, value))
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
|
||||
|
||||
label _game_menu:
|
||||
|
||||
# disable keystrokes.
|
||||
$ config.underlay = [ ]
|
||||
$ config.overlay = [ ]
|
||||
label _load_menu:
|
||||
$ renpy.take_screenshot((library.thumbnail_width, library.thumbnail_height))
|
||||
$ renpy.set_overlay([])
|
||||
|
||||
if _started:
|
||||
jump _save_screen
|
||||
else:
|
||||
jump _load_screen
|
||||
jump _load_screen
|
||||
|
||||
label _game_menu:
|
||||
$ renpy.take_screenshot((library.thumbnail_width, library.thumbnail_height))
|
||||
|
||||
jump _save_screen
|
||||
|
||||
label _load_screen:
|
||||
|
||||
python:
|
||||
_fn = _file_picker("load", renpy.saved_game_filenames() )
|
||||
|
||||
call _install_keymap
|
||||
|
||||
python:
|
||||
renpy.load(_fn)
|
||||
|
||||
jump _game_menu
|
||||
jump _load_screen
|
||||
|
||||
label _save_screen:
|
||||
$ _fn = _file_picker("save", renpy.saved_game_filenames() + [ None ] )
|
||||
|
||||
python hide:
|
||||
fn = _file_picker("save", renpy.saved_game_filenames() + [ None ] )
|
||||
renpy.save(fn, renpy.time.strftime("%Y-%m-%d %H:%M:%S\n") + save_name)
|
||||
if not _fn or _yesno_prompt("save", _("Are you sure you want to overwrite your save?")):
|
||||
$ renpy.save(_fn, renpy.time.strftime("%Y-%m-%d %H:%M:%S\n") + save_name)
|
||||
|
||||
jump _save_screen
|
||||
|
||||
# The preferences screen.
|
||||
label _prefs_screen:
|
||||
|
||||
python hide:
|
||||
prefs_left = [
|
||||
( 'Display', 'fullscreen',
|
||||
[ ('Window', False), ('Fullscreen', True) ] ),
|
||||
( 'Music', 'music',
|
||||
[ ('Enabled', True), ('Disabled', False) ] ),
|
||||
( 'Sound Effects', 'sound',
|
||||
[ ('Enabled', True), ('Disabled', False) ] ),
|
||||
]
|
||||
|
||||
prefs_right = [
|
||||
('CTRL Skips', 'skip_unseen',
|
||||
[ ('Seen Messages', False), ('All Messages', True) ] ),
|
||||
('Transitions', 'transitions',
|
||||
[ ('All', 2), ('Some', 1), ('None', 0) ]),
|
||||
]
|
||||
|
||||
if config.annoying_text_cps:
|
||||
prefs_right.append(('Text Display', 'fast_text', [ ('Slow', False), ('Fast', True) ]))
|
||||
|
||||
vbox_left = renpy.VBox(padding=library.padding * 3, style='prefs_left')
|
||||
|
||||
for label, var, entries in prefs_left:
|
||||
vbox_left.add(_prefvbox(label, var, entries))
|
||||
|
||||
vbox_right = renpy.VBox(padding=library.padding * 3, style='prefs_right')
|
||||
|
||||
for label, var, entries in prefs_right:
|
||||
vbox_right.add(_prefvbox(label, var, entries))
|
||||
|
||||
_game_interact("prefs", vbox_left, vbox_right)
|
||||
|
||||
jump _prefs_screen
|
||||
|
||||
|
||||
|
||||
|
||||
# Asks the user if he wants to quit.
|
||||
label _confirm_quit:
|
||||
if _yesno_prompt("quit", _("Are you sure you want to end the game?")):
|
||||
jump _quit
|
||||
else:
|
||||
jump _return
|
||||
|
||||
label _quit:
|
||||
$ renpy.quit()
|
||||
|
||||
@@ -293,6 +412,11 @@ label _full_restart:
|
||||
# Return to the game, after restoring the keymap.
|
||||
label _return:
|
||||
|
||||
call _install_keymap
|
||||
return
|
||||
|
||||
# Random nice things to have.
|
||||
init:
|
||||
$ centered = Character(None, what_style="centered_text", window_style="centered_window")
|
||||
image text = renpy.ParameterizedText(style="centered_text")
|
||||
|
||||
|
||||
+110
-23
@@ -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 -100:
|
||||
init -250:
|
||||
python hide:
|
||||
|
||||
style.create('default', None,
|
||||
@@ -27,6 +27,7 @@ init -100:
|
||||
style.default.drop_shadow = (2, 2)
|
||||
style.default.drop_shadow_color = (0, 0, 0, 128)
|
||||
style.default.minwidth = 0
|
||||
style.default.textalign = 0
|
||||
|
||||
# Change this if you're not using Vera 22.
|
||||
if renpy.windows():
|
||||
@@ -51,11 +52,15 @@ init -100:
|
||||
style.default.xanchor = 'left'
|
||||
style.default.yanchor = 'top'
|
||||
|
||||
# Sound properties.
|
||||
style.default.hover_sound = None
|
||||
style.default.activate_sound = None
|
||||
|
||||
# The base style for the large windows.
|
||||
style.create('window', 'default',
|
||||
'(window, placement) The base style for the windows that contain dialogue, thoughts, and menus.')
|
||||
|
||||
style.window.background = Frame("frame.png", 125, 25)
|
||||
style.window.background = Solid((0, 0, 128, 128))
|
||||
style.window.xpadding = 10
|
||||
style.window.ypadding = 5
|
||||
style.window.xmargin = 10
|
||||
@@ -72,7 +77,7 @@ init -100:
|
||||
|
||||
# This style controls the default placement of images on the screen.
|
||||
|
||||
style.create('image_placement', None,
|
||||
style.create('image_placement', 'default',
|
||||
'This style is used to control the default placement of images on the screen.')
|
||||
|
||||
style.image_placement.xpos = 0.5
|
||||
@@ -106,17 +111,11 @@ init -100:
|
||||
"""(text) The style that is used to render a menu
|
||||
caption.""")
|
||||
|
||||
style.create('menu_selected', 'default',
|
||||
"""(text) The style that is used to render a
|
||||
selected menu choice.""")
|
||||
style.create('menu_choice', 'default',
|
||||
"""(text, hover, sound) The style that is used to render a menu choice.""")
|
||||
|
||||
style.menu_selected.color = (255, 255, 0, 255) # yellow
|
||||
|
||||
style.create('menu_unselected', 'default',
|
||||
"""(text) The style that is used to render an
|
||||
unselected menu choice.""")
|
||||
|
||||
style.menu_unselected.color = (0, 255, 255, 255) # cyan
|
||||
style.menu_choice.hover_color = (255, 255, 0, 255) # yellow
|
||||
style.menu_choice.idle_color = (0, 255, 255, 255) # cyan
|
||||
|
||||
style.create('menu_window', 'window',
|
||||
'(window, position) The default style for windows containing a menu.')
|
||||
@@ -133,18 +132,49 @@ init -100:
|
||||
style.create('input_window', 'window',
|
||||
'(window, position) The style used for the window of an input box.')
|
||||
|
||||
# Styles used by centered.
|
||||
style.create('centered_window', 'default',
|
||||
'(window) The style that is used for a "window" containing centered text.')
|
||||
|
||||
style.create('centered_text', 'default',
|
||||
'(text) The style used for centered text.')
|
||||
|
||||
style.centered_window.xpos = 0.5
|
||||
style.centered_window.xanchor = 'center'
|
||||
style.centered_window.xfill = False
|
||||
|
||||
style.centered_window.ypos = 0.5
|
||||
style.centered_window.yanchor = 'center'
|
||||
style.centered_window.yfill = False
|
||||
|
||||
style.centered_window.xpadding = 10
|
||||
|
||||
style.centered_text.textalign = 0.5
|
||||
style.centered_text.xpos = 0.5
|
||||
style.centered_text.ypos = 0.5
|
||||
style.centered_text.xanchor = 'center'
|
||||
style.centered_text.yanchor = 'center'
|
||||
|
||||
# Styles that are used by imagemaps
|
||||
style.create('imagemap', 'image_placement',
|
||||
'(sound, position) The style that is used for imagemaps.')
|
||||
|
||||
|
||||
# Styles that are used by all Buttons.
|
||||
style.create('button', 'default',
|
||||
'(window, hover) The default style used for buttons in the main and game menus.')
|
||||
'(window, sound, hover) The default style used for buttons in the main and game menus.')
|
||||
|
||||
style.button.xpos = 0.5
|
||||
style.button.xanchor = 'center'
|
||||
|
||||
style.create('button_text', 'default',
|
||||
'(text, hover) The default style used for the label of a button.')
|
||||
|
||||
style.button_text.xpos = 0.0
|
||||
style.button_text.xanchor = 'left'
|
||||
style.button_text.xpos = 0.5
|
||||
style.button_text.xanchor = 'center'
|
||||
style.button_text.size = 24
|
||||
style.button_text.color = (0, 255, 255, 255)
|
||||
style.button_text.hover_color = (128, 255, 255, 255)
|
||||
|
||||
# Selected button.
|
||||
style.create('selected_button', 'button',
|
||||
@@ -160,18 +190,19 @@ init -100:
|
||||
style.create('disabled_button', 'button',
|
||||
'(window, hover) The style that is used for a disabled button.')
|
||||
|
||||
style.disabled_button.hover_sound = None
|
||||
style.disabled_button.activate_sound = None
|
||||
|
||||
style.create('disabled_button_text', 'button_text',
|
||||
'(text, hover) The style that is used for the label of a disabled button.')
|
||||
|
||||
style.disabled_button_text.color = (128, 128, 128, 255)
|
||||
|
||||
|
||||
|
||||
# Styles that are used when laying out the main menu.
|
||||
style.create('mm_root_window', 'default',
|
||||
'(window) The style used for the root window of the main menu. This is primarily used to set a background for the main menu.')
|
||||
|
||||
style.mm_root_window.background = renpy.Image("mainmenu.jpg")
|
||||
style.mm_root_window.background = Solid((0, 0, 0, 255))
|
||||
|
||||
style.create('mm_menu_window', 'default',
|
||||
'(window, position) A window that contains the choices in the main menu. Change this to change the placement of these choices on the main menu screen.')
|
||||
@@ -192,19 +223,19 @@ init -100:
|
||||
style.create('gm_root_window', 'default',
|
||||
'(window) The style used for the root window of the game menu. This is primarily used to change the background of the game menu.')
|
||||
|
||||
style.gm_root_window.background = renpy.Image("gamemenu.jpg")
|
||||
style.gm_root_window.background = Solid((0, 0, 0, 255))
|
||||
|
||||
style.create('gm_nav_window', 'default',
|
||||
'(window, placement) The style used by a window containing buttons that allow the user to navigate through the different screens of the game menu.')
|
||||
'(window, position) The style used by a window containing buttons that allow the user to navigate through the different screens of the game menu.')
|
||||
|
||||
style.gm_nav_window.xpos = 0.95
|
||||
style.gm_nav_window.xpos = 0.9
|
||||
style.gm_nav_window.xanchor = 'right'
|
||||
style.gm_nav_window.ypos = 0.95
|
||||
style.gm_nav_window.yanchor = 'bottom'
|
||||
|
||||
|
||||
style.create('file_picker_window', 'default',
|
||||
'(window, placement) A window containing the file picker that is used to choose slots for loading and saving.')
|
||||
'(window, position) A window containing the file picker that is used to choose slots for loading and saving.')
|
||||
|
||||
style.file_picker_window.xpos = 10
|
||||
style.file_picker_window.xanchor = 'left'
|
||||
@@ -244,3 +275,59 @@ init -100:
|
||||
style.create('file_picker_new_slot', 'file_picker_text',
|
||||
'(text) The style that is used for the new slot indicator in the file picker.')
|
||||
|
||||
|
||||
style.create('yesno_prompt', '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_prompt.ypos = 0.25
|
||||
style.yesno_prompt.yanchor = 'center'
|
||||
|
||||
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'
|
||||
|
||||
|
||||
# Preferences
|
||||
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.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"
|
||||
|
||||
+229
-100
@@ -2,8 +2,14 @@
|
||||
# public domain. Feel free to use it as the basis for your own
|
||||
# game.
|
||||
|
||||
|
||||
# This init block runs first, and sets up all sorts of things that
|
||||
# are used by the rest of the game. Variables that are set in init
|
||||
# blocks are _not_ saved, unless they are changed later on in the
|
||||
# program.
|
||||
|
||||
init:
|
||||
# Set up the size of the screen.
|
||||
# Set up the size of the screen, and the window title.
|
||||
$ config.screen_width = 800
|
||||
$ config.screen_height = 600
|
||||
$ config.window_title = "The Ren'Py Demo Game"
|
||||
@@ -11,14 +17,24 @@ init:
|
||||
# Set up the library.
|
||||
$ library.file_page_length = 3
|
||||
|
||||
# Positions of things on the screen.
|
||||
# Change some styles, to add images in the background of
|
||||
# the menus and windows.
|
||||
$ style.mm_root_window.background = Image("mainmenu.jpg")
|
||||
$ style.gm_root_window.background = Image("gamemenu.jpg")
|
||||
$ style.window.background = Frame("frame.png", 125, 25)
|
||||
|
||||
# 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()
|
||||
|
||||
# Transitions between scenes.
|
||||
# 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) # Dissolve.
|
||||
$ dissolve = Dissolve(0.5)
|
||||
|
||||
# Now, we declare the images that are used in the program.
|
||||
|
||||
# Backgrounds.
|
||||
image carillon = Image("carillon.jpg")
|
||||
@@ -31,30 +47,53 @@ init:
|
||||
image eileen vhappy = Image("9a_vhappy.png")
|
||||
image eileen concerned = Image("9a_concerned.png")
|
||||
|
||||
# Finally, the character object. This object lets us have the
|
||||
# character say dialogue without us having to repeatedly type
|
||||
# her name. It also lets us change the color of her name.
|
||||
|
||||
# Character objects.
|
||||
$ e = Character('Eileen', color=(200, 255, 200, 255))
|
||||
|
||||
|
||||
# The actual game starts here.
|
||||
# The start label marks the place where the main menu jumps to to
|
||||
# begin the actual game.
|
||||
|
||||
label start:
|
||||
|
||||
# The save_name variable sets the name of the save game. Like all
|
||||
# variables declared outside of init blocks, this variable is
|
||||
# saved and restored with a save file.
|
||||
$ save_name = "Introduction"
|
||||
|
||||
# Did we win the date?
|
||||
# This variable is only used by our game. If it's true, it means
|
||||
# that we won the date.
|
||||
$ date = False
|
||||
|
||||
# Start some music playing in the background.
|
||||
$ renpy.music_start('sun-flower-slow-drag.mid')
|
||||
|
||||
|
||||
# Now, set up the first scene. We first fade in our washington
|
||||
# background, and then we dissolve in the image of Eileen on top
|
||||
# of it.
|
||||
scene washington with fade
|
||||
show eileen vhappy with dissolve
|
||||
|
||||
"Girl" "Hi, and welcome to the Ren'Py 4.0 demo program."
|
||||
# Display a line of dialogue. In this case, we manually specify
|
||||
# who's saying the line of dialoge.
|
||||
"Girl" "Hi, and welcome to the Ren'Py 4 demo program."
|
||||
|
||||
# This instantly replaces the very happy picture of Eileen with
|
||||
# one showing her merely happy. It demonstrates how the show
|
||||
# statement lets characters change emotions.
|
||||
show eileen happy
|
||||
|
||||
# Another line of dialogue.
|
||||
"Girl" "My name is Eileen, and while I plan to one day star in a
|
||||
real game, for now I'm here to tell you about Ren'Py."
|
||||
|
||||
# This line used the e character object, which displays Eileen's
|
||||
# name in green. The use of a short name for a character object
|
||||
# lets us save typing when writing the bulk of the dialogue.
|
||||
e "Ren'Py is a language and engine for writing and playing visual
|
||||
novel games."
|
||||
|
||||
@@ -65,34 +104,207 @@ label start:
|
||||
e "I can tell you about the features of Ren'Py games, or how to write
|
||||
your own game. What do you want to know about?"
|
||||
|
||||
# This variable is used to save the choices that have been made in
|
||||
# the main menu.
|
||||
$ seen_set = [ ]
|
||||
|
||||
label choices:
|
||||
label choices:
|
||||
|
||||
# We change the save name here.
|
||||
$ save_name = "Question Menu"
|
||||
|
||||
# This is the main menu, that lets the user decide what he wants
|
||||
# to hear about.
|
||||
menu:
|
||||
|
||||
# The set menu clause ensures that each menu choice can only
|
||||
# be chosen once.
|
||||
set seen_set
|
||||
|
||||
# This is a menu choice. When chosen, the statements in its
|
||||
# block are executed.
|
||||
"What are some features of Ren'Py games?":
|
||||
call features from call_features
|
||||
|
||||
# We call the features label. The from clause needs to be
|
||||
# here to ensure that save games work, even after we
|
||||
# change the script. It was added automatically.
|
||||
call features from _call_features_1
|
||||
|
||||
# When we're done talking about features, jump back up
|
||||
# to choices.
|
||||
jump choices
|
||||
|
||||
# Another choice.
|
||||
"How do I write my own games with it?":
|
||||
call writing from call_writing
|
||||
call writing from _call_writing_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.)
|
||||
"Where can I find out more?" if seen_set:
|
||||
call find_out_more from call_find_out_more
|
||||
call find_out_more from _call_find_out_more_1
|
||||
jump choices
|
||||
|
||||
"Why are we in Washington, DC?":
|
||||
call washington from call_washington
|
||||
call washington from _call_washington_1
|
||||
jump choices
|
||||
|
||||
"I think I've heard enough." if seen_set:
|
||||
jump ending
|
||||
|
||||
|
||||
# This is the section on writing games.
|
||||
label writing:
|
||||
|
||||
# Change the title of the save games.
|
||||
$ save_name = "Writing Games"
|
||||
|
||||
# We start off with a bunch of dialogue.
|
||||
e "If you want to write a game, I recommend that you read the
|
||||
Ren'Py tutorial, which you can get from our web page,
|
||||
http://www.bishoujo.us/renpy/."
|
||||
|
||||
e "But here, we'll go over some of the basics of writing Ren'Py
|
||||
scripts. It might make sense if you open the source for this
|
||||
game."
|
||||
|
||||
e "The source for this game can be found in the file
|
||||
game/script.rpy."
|
||||
|
||||
e "The goal of Ren'Py is to make writing the game similar to
|
||||
typing up the script on the computer."
|
||||
|
||||
e "For example, a line of dialogue is expressed by putting the
|
||||
character's name next to the dialogue string."
|
||||
|
||||
# A string by itself like this displays without a name associated
|
||||
# with it. So it's useful for dialogue and narration.
|
||||
"I somehow remember that strings by themselves are displayed as
|
||||
thoughts or narration."
|
||||
|
||||
e "The menu statement makes it easy to create menus."
|
||||
|
||||
e "A number of statements let you control what is shown on the
|
||||
screen."
|
||||
|
||||
# This scene statement has a with clause associated with it. In
|
||||
# this case (based on what is defined in the init clause at the
|
||||
# top of this script), it causes a fade to black, and then back
|
||||
# to the new scene.
|
||||
scene whitehouse with fade
|
||||
|
||||
e "The scene statement clears the scene list, which is the list of
|
||||
things that are shown on the screen."
|
||||
|
||||
# This shows an image, and dissolves it in.
|
||||
show eileen happy with dissolve
|
||||
|
||||
e "The show statement shows another image on the screen."
|
||||
|
||||
# The at clause here, displays the character on the left side of
|
||||
# the screen.
|
||||
show eileen happy at left with dissolve
|
||||
|
||||
e "Images can take at clauses that specify where on the screen
|
||||
they are shown."
|
||||
|
||||
show eileen vhappy at left
|
||||
|
||||
e "Showing a new image with the same first part of the name
|
||||
replaces the image in the scene list."
|
||||
|
||||
hide eileen with dissolve
|
||||
|
||||
e "Finally, the hide statement hides an image, which is useful
|
||||
when a character leaves the scene."
|
||||
|
||||
show eileen happy with dissolve
|
||||
|
||||
e "Don't worry, I'm not going anywhere."
|
||||
|
||||
e "The with statement is used to cause transitions to
|
||||
happen. Transitions like fade..."
|
||||
|
||||
# This statement hides the transient stuff from being included
|
||||
# in the next fade.
|
||||
with None
|
||||
|
||||
# This with statement causes things to fade without changing the
|
||||
# scene.
|
||||
with fade
|
||||
|
||||
e "... or dissolve ..."
|
||||
|
||||
# In this block, the scene statement clears the scene list. So we
|
||||
# have to reshow the eileen happy image, so that it appears that
|
||||
# just the background is dissolving. Sneaky.
|
||||
with None
|
||||
scene washington
|
||||
show eileen happy
|
||||
with dissolve
|
||||
|
||||
e "... are easily invoked."
|
||||
|
||||
e "As of version 4.2, Ren'Py supports image maps, which are like
|
||||
another form of menu. Let's try one."
|
||||
|
||||
# This is an imagemap. It consists of two images, and a list of
|
||||
# hotspots. For each hotspot we give the coordinates of the left,
|
||||
# top, right, and bottom sides, and the value to return if it is
|
||||
# picked.
|
||||
|
||||
$ result = renpy.imagemap("ground.png", "selected.png", [
|
||||
(100, 100, 300, 400, "eileen"),
|
||||
(500, 100, 700, 400, "lucy")
|
||||
])
|
||||
|
||||
# We've assigned the chosen result from the imagemap to the
|
||||
# result variable. We can use an if statement to vary what
|
||||
# happens based on the user's choice.
|
||||
|
||||
if result == "eileen":
|
||||
show eileen vhappy
|
||||
e "You picked me!"
|
||||
|
||||
elif result == "lucy":
|
||||
show eileen concerned
|
||||
e "It looks like you picked Lucy."
|
||||
|
||||
# Eileen is being a bit possesive here. :-P
|
||||
if date:
|
||||
e "You can forget about Saturday."
|
||||
$ date = False
|
||||
|
||||
show eileen happy
|
||||
|
||||
e "Ren'Py supports music, such as what's playing in the
|
||||
background..."
|
||||
|
||||
# This plays a sound effect.
|
||||
$ renpy.play("18005551212.wav")
|
||||
|
||||
e "... and sound effects, like the one that just played."
|
||||
|
||||
e "Ren'Py also includes a number of control statements, and even
|
||||
lets you include python code."
|
||||
|
||||
e "Rather than go into this here, you can read all about it in the
|
||||
tutorial."
|
||||
|
||||
e "If you want to make changes, you can edit the script for this
|
||||
game by editing game/script.rpy"
|
||||
|
||||
e "When you've made a change, just re-run the game to see your
|
||||
change in action."
|
||||
|
||||
e "Would you like to know about something else?"
|
||||
|
||||
# We return back up to the menu that lets the user pick a topic.
|
||||
return
|
||||
|
||||
# This ends the well-commented portion of this script.
|
||||
|
||||
label features:
|
||||
|
||||
$ save_name = "Features"
|
||||
@@ -120,6 +332,9 @@ label features:
|
||||
e "The game menu also lets you restart or quit the game. But you
|
||||
wouldn't want to do that, would you?"
|
||||
|
||||
e "Finally, the game menu lets you set up the game
|
||||
preferences. These preferences are saved between games."
|
||||
|
||||
show eileen vhappy
|
||||
|
||||
e "The next feature is really neat."
|
||||
@@ -182,92 +397,6 @@ label after_rollback:
|
||||
|
||||
return
|
||||
|
||||
label writing:
|
||||
|
||||
$ save_name = "Writing Games"
|
||||
|
||||
e "If you want to write a game, I recommend that you read the
|
||||
Ren'Py tutorial, which you can get from our web page,
|
||||
http://www.bishoujo.us/renpy/."
|
||||
|
||||
e "But here, we'll go over some of the basics of writing Ren'Py
|
||||
scripts. It might make sense if you open the source for this
|
||||
game."
|
||||
|
||||
e "The source for this game can be found in the file
|
||||
game/script.rpy."
|
||||
|
||||
e "The goal of Ren'Py is to make writing the game similar to
|
||||
typing up the script on the computer."
|
||||
|
||||
e "For example, a line of dialogue is expressed by putting the
|
||||
character's name next to the dialogue string."
|
||||
|
||||
"I somehow remember that strings by themselves are displayed as
|
||||
thoughts or narration."
|
||||
|
||||
|
||||
e "The menu statement makes it easy to create menus."
|
||||
|
||||
e "A number of statements let you control what is shown on the
|
||||
screen."
|
||||
|
||||
scene whitehouse with fade
|
||||
|
||||
e "The scene statement clears the scene list, which is the list of
|
||||
things that are shown on the screen."
|
||||
|
||||
show eileen happy with dissolve
|
||||
|
||||
e "The show statement shows another image on the screen."
|
||||
|
||||
show eileen happy at left with dissolve
|
||||
|
||||
e "Images can take at clauses that specify where on the screen
|
||||
they are shown."
|
||||
|
||||
show eileen vhappy at left
|
||||
|
||||
e "Showing a new image with the same first part of the name
|
||||
replaces the image in the scene list."
|
||||
|
||||
hide eileen with dissolve
|
||||
|
||||
e "Finally, the hide statement hides an image, which is useful
|
||||
when a character leaves the scene."
|
||||
|
||||
show eileen happy with dissolve
|
||||
|
||||
e "Don't worry, I'm not going anywhere."
|
||||
|
||||
e "The with statement is used to cause transitions to
|
||||
happen. Transitions like fade..."
|
||||
|
||||
with fade
|
||||
|
||||
e "... or dissolve ..."
|
||||
|
||||
scene washington
|
||||
show eileen happy
|
||||
with dissolve
|
||||
|
||||
e "... are easily invoked."
|
||||
|
||||
e "Ren'Py also includes a number of control statements, and even
|
||||
lets you include python code."
|
||||
|
||||
e "Rather than go into this here, you can read all about it in the
|
||||
tutorial."
|
||||
|
||||
e "If you want to make changes, you can edit the script for this
|
||||
game by editing game/script.rpy"
|
||||
|
||||
e "When you've made a change, just re-run the game to see your
|
||||
change in action."
|
||||
|
||||
e "Would you like to know about something else?"
|
||||
|
||||
return
|
||||
|
||||
label find_out_more:
|
||||
|
||||
@@ -405,7 +534,6 @@ label post_netherlands:
|
||||
|
||||
return
|
||||
|
||||
|
||||
label ending:
|
||||
|
||||
$ save_name = "Ending"
|
||||
@@ -437,3 +565,4 @@ label ending:
|
||||
"We can't wait to see what you do with this. Good luck!"
|
||||
|
||||
$ renpy.full_restart()
|
||||
|
||||
|
||||
+10
-3
@@ -21,8 +21,7 @@ def copy_file(source, dest, license=""):
|
||||
df.write(license)
|
||||
|
||||
data = sf.read()
|
||||
if dest.endswith(".txt") or dest.endswith(".py") or dest.endswith(".rpy"):
|
||||
print "DOSIFY", dest
|
||||
if dest.endswith(".txt") or dest.endswith(".py") or dest.endswith(".rpy") or dest.endswith(".bat"):
|
||||
data = dosify(data)
|
||||
|
||||
df.write(data)
|
||||
@@ -92,6 +91,7 @@ def main():
|
||||
license=license)
|
||||
|
||||
doc_files = [
|
||||
'example.html',
|
||||
'tutorial.html',
|
||||
'style.css',
|
||||
]
|
||||
@@ -104,14 +104,21 @@ def main():
|
||||
copy_tree(gamedir, target + "/game",
|
||||
should_copy = lambda fn : not fn.startswith(".") and not fn.endswith("~"))
|
||||
|
||||
copy_tree("common", target + "/common",
|
||||
should_copy = lambda fn : not fn.startswith(".") and not fn.endswith("~"))
|
||||
|
||||
def cp(x, license=""):
|
||||
copy_file(x, target + "/" + x)
|
||||
|
||||
cp("CHANGELOG.txt")
|
||||
cp("LICENSE.txt")
|
||||
cp("README_RENPY.txt")
|
||||
cp("archive_images.bat")
|
||||
cp("run_game.py", license=license)
|
||||
cp("archiver.py", license=license)
|
||||
|
||||
cp("build_exe.py", license=license)
|
||||
cp("add_from.py", license=license)
|
||||
cp("renpy-mode.el")
|
||||
|
||||
|
||||
|
||||
|
||||
+10
-2
@@ -1,4 +1,12 @@
|
||||
tutorial.html: tutorial.xml preprocess.py stylesheet.xslt style.css
|
||||
all:: tutorial.html example.html
|
||||
|
||||
tutorial.html: tutorial.xml preprocess.py stylesheet.xslt style.css styles.xml
|
||||
python preprocess.py tutorial.xml > tutorial.hi.xml
|
||||
xsltproc stylesheet.xslt tutorial.hi.xml > tutorial.html
|
||||
cp tutorial.html style.css ~/ab/website/renpy/devel/doc
|
||||
cp tutorial.html style.css ~/ab/website/renpy/devel/doc
|
||||
|
||||
|
||||
example.html: example.xml preprocess.py stylesheet.xslt style.css styles.xml
|
||||
python preprocess.py example.xml > example.hi.xml
|
||||
xsltproc stylesheet.xslt example.hi.xml > example.html
|
||||
cp example.html style.css ~/ab/website/renpy/devel/doc
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<doc>
|
||||
<title>Demo Script Example</title>
|
||||
|
||||
<p><a href="tutorial.html">Return to the tutorial.</a></p>
|
||||
|
||||
<example>
|
||||
<!-- include ../demo2/script.rpy -->
|
||||
</example>
|
||||
|
||||
<p><a href="tutorial.html">Return to the tutorial.</a></p>
|
||||
|
||||
</doc>
|
||||
+15
-2
@@ -1,7 +1,8 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import sys
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
|
||||
import inspect
|
||||
|
||||
@@ -31,6 +32,7 @@ keywords = [
|
||||
r'\bpass\b',
|
||||
r'\bwith\b',
|
||||
r'\bat\b',
|
||||
r'\bpython\b',
|
||||
]
|
||||
|
||||
kwre = '|'.join(keywords)
|
||||
@@ -109,15 +111,26 @@ def function(m):
|
||||
|
||||
return '<function name="%(name)s" sig="%(args)s">%(doc)s</function>' % locals()
|
||||
|
||||
def include(m):
|
||||
|
||||
f = file(m.group(1))
|
||||
rv = f.read()
|
||||
f.close()
|
||||
return rv
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
f = file(sys.argv[1])
|
||||
s = f.read()
|
||||
f.close()
|
||||
|
||||
s = re.sub(r"(?s)<example>(.*?)</example>", example, s)
|
||||
|
||||
s = re.sub(r"<!-- func (\S+) -->", function, s)
|
||||
s = re.sub(r"<!-- include (\S+) -->", include, s)
|
||||
s = re.sub(r"<!-- date -->", time.strftime("%04Y-%02m-%02d %02H:%02M"), s)
|
||||
s = re.sub(r"(?s)<example>(.*?)</example>", example, s)
|
||||
|
||||
print s
|
||||
|
||||
|
||||
+571
-91
@@ -2,6 +2,10 @@
|
||||
<doc>
|
||||
<title>Writing Visual Novels with Ren'Py</title>
|
||||
<subtitle>The Ren'Py Tutorial</subtitle>
|
||||
|
||||
<p>
|
||||
<b>Last Updated:</b> <!-- date -->
|
||||
</p>
|
||||
|
||||
<toc/>
|
||||
|
||||
@@ -202,13 +206,34 @@ line 2
|
||||
<def>Keyword</def>s are words that appear in the source
|
||||
code. They're used to introduce a statement, or to delimit parts
|
||||
of a statement. You'll see keywords throughout the descriptions
|
||||
of statements. In grammar rules, keywords are in quotes.
|
||||
of statements. In grammar rules, keywords are in quotes. The
|
||||
keywords are:
|
||||
|
||||
</p><p>
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
at
|
||||
call
|
||||
hide
|
||||
if
|
||||
image
|
||||
init
|
||||
jump
|
||||
menu
|
||||
python
|
||||
return
|
||||
scene
|
||||
set
|
||||
show
|
||||
with
|
||||
while
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
|
||||
A <def>name</def>s consist of an alphabetic character or number,
|
||||
followed by zero or more alphabetic characters or underscores,
|
||||
so long as the string isn't a keyword.
|
||||
so long as the string isn't a keyword.
|
||||
|
||||
</p><p>
|
||||
|
||||
@@ -283,7 +308,7 @@ line 2
|
||||
a simple_expression followed by a string.
|
||||
</p>
|
||||
|
||||
<rule>say_statement -> ( simple_expression )? string</rule>
|
||||
<rule>say_statement -> ( simple_expression )? string ( "with" simple_expression )?</rule>
|
||||
|
||||
<p>
|
||||
We can distinguish two forms of the say statement, depending on
|
||||
@@ -344,6 +369,27 @@ e "I know that you're %(player_age)d years old, and your zodiac
|
||||
sign is %(player_sign)s."
|
||||
</example>
|
||||
|
||||
|
||||
<p>
|
||||
When the first object is a character object, that character object
|
||||
is given given complete control over how text is displated. As an
|
||||
example of this, the standard library includes a character object
|
||||
called "centered", which displays the text centered on the screen,
|
||||
without any background window.
|
||||
</p>
|
||||
|
||||
<example>
|
||||
centered "American Bishoujo presents..."
|
||||
centered "The Ren'Py Demo Game"
|
||||
</example>
|
||||
|
||||
<p>
|
||||
The say statement also takes a with clause that is used to control
|
||||
the transition that is used to introduce the dialogue or
|
||||
thought. Please see the section on transitions for details on how
|
||||
to use the with clause.
|
||||
</p>
|
||||
|
||||
<h3>Menus</h3>
|
||||
|
||||
<p>
|
||||
@@ -364,7 +410,7 @@ e "I know that you're %(player_age)d years old, and your zodiac
|
||||
</p><p>
|
||||
|
||||
The menu statement must have a block associated with it. This
|
||||
block must contain one or more menuitems in it. There are three
|
||||
block must contain one or more menuitems in it. There are four
|
||||
kinds of menuitems that can be contained in a menu block.
|
||||
|
||||
</p>
|
||||
@@ -402,6 +448,16 @@ e "I know that you're %(player_age)d years old, and your zodiac
|
||||
present, it's used to filter the list of choices shown to the
|
||||
user.
|
||||
|
||||
</p>
|
||||
|
||||
<rule>with_menuitem -> "with" simple_expression</rule>
|
||||
|
||||
<p>
|
||||
|
||||
The final kind of menuitem is a with clause. This is used to
|
||||
specify the transition that introduces this menu. Please see the
|
||||
section on transitions for a discussion of this.
|
||||
|
||||
</p><p>
|
||||
|
||||
When a menu is to be shown to the user, the first thing that
|
||||
@@ -618,6 +674,51 @@ hide eileen
|
||||
most scenes needed in a visual novel type game.
|
||||
</p>
|
||||
|
||||
<h4>Parameterized Images</h4>
|
||||
|
||||
<p>
|
||||
Show and scene statements may also take parameterized images. If a
|
||||
name of an image is a prefix of the name given in a scene or show
|
||||
statement, the rest of the name is considered to be
|
||||
parameters. Parameters may be names, but they may also be
|
||||
simple_expressions, which includes strings. Parameters are handed
|
||||
off to the object that the prefix is defined to, and that object
|
||||
is responsible for returning a displayable that can be shown to
|
||||
the user.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
An example of this is the pre-defined text displayable, which
|
||||
displays text as if it was an image. For example, the following
|
||||
code:
|
||||
</p>
|
||||
|
||||
<example>
|
||||
show text "American Bishoujo Presents..."
|
||||
</example>
|
||||
|
||||
<p>
|
||||
Will display the given text as if it was an image. The game will
|
||||
not pause while the text is being displayed. Such an image can
|
||||
also be processed through an at clause, as in the following code
|
||||
which moves the text down from the top of the screen, while
|
||||
waiting 10 seconds or until the user makes some input.
|
||||
</p>
|
||||
|
||||
<example>
|
||||
show text "A PyTom Game" \
|
||||
at Move((0.5, 0.0), (0.5, 0.5), 4.0,
|
||||
xanchor='center', yanchor='bottom')
|
||||
|
||||
$ renpy.pause(10)
|
||||
</example>
|
||||
|
||||
<p>
|
||||
Please note that parameterized images are replaced in the same way
|
||||
that normal images are. (So you can only have one image created
|
||||
with text on the screen at a time, without creating a second
|
||||
ParameterizedText object.)
|
||||
</p>
|
||||
|
||||
<h3>Image and Scene Functions</h3>
|
||||
|
||||
@@ -668,10 +769,10 @@ init:
|
||||
|
||||
<p>
|
||||
Position, when given position properties as arguments, returns a
|
||||
callable that can be passed to the "at" clause of a show, hide,
|
||||
or scene statement to display the image at the given
|
||||
location. See the section below on position properties to get
|
||||
a full explanation of how they are used to lay things out, but
|
||||
callable that can be passed to the "at" clause of a show or
|
||||
scene statement to display the image at the given location. See
|
||||
the section below on position properties to get a full
|
||||
explanation of how they are used to lay things out, but
|
||||
hopefully this example will show how Position can be used:
|
||||
</p>
|
||||
</function>
|
||||
@@ -688,6 +789,105 @@ init:
|
||||
show eileen happy at left
|
||||
</example>
|
||||
|
||||
<!-- 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
|
||||
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.
|
||||
</p>
|
||||
|
||||
<example>
|
||||
init:
|
||||
image text1 = renpy.ParameterizedText(ypos=0.25)
|
||||
|
||||
show text "centered."
|
||||
show text1 "1/4 of the way down the screen."
|
||||
</example>
|
||||
|
||||
<function name="Pan" sig="(startpos, endpos, time)">
|
||||
|
||||
<p>
|
||||
|
||||
Pan, when given the appropriate arguments, gives an object that
|
||||
can be passed to the at clause of that image to cause the image to
|
||||
be panned on the screen. The parameters startpos and endpos are
|
||||
tuples, containing the x and y coordinates of the upper-left hand
|
||||
corner of the screen relative to the image. Time is the time it
|
||||
will take this position to move from startpos to endpos.
|
||||
|
||||
</p><p>
|
||||
|
||||
As the current implementation of Ren'Py is quite limited, there
|
||||
are quite a few restrictions that we put on pan. The big one is
|
||||
that there always must be a screen's worth of pixels to the
|
||||
right and below the start and end positions. Failure to ensure
|
||||
this may lead to inconsistent rendering.
|
||||
|
||||
</p><p>
|
||||
|
||||
Hopefully, an example will demonstrate how Pan is used. For this
|
||||
example, assume that the screen is 800 x 600, and that the
|
||||
image marspan is 2400 x 600 pixels in size. We want to take 10
|
||||
seconds to pan from left to right on the image.
|
||||
|
||||
</p>
|
||||
|
||||
<example>
|
||||
scene marspan at Pan((0, 0), (1600, 0), 10.0)
|
||||
</example>
|
||||
|
||||
<p>
|
||||
Please note that the pan will be immediately displayed, and that
|
||||
Ren'Py will not wait for it to complete before moving on to
|
||||
the next statement. This may lead to the pan being overlayed
|
||||
with text or dialogue. You may want to use a call to renpy.pause
|
||||
to delay for the time it will take to complete the pan.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Finally, also note that when a pan is completed, the image locks
|
||||
into the ending position.
|
||||
</p>
|
||||
|
||||
</function>
|
||||
|
||||
<function name="Move" sig="(startpos, endpos, time, **properties)">
|
||||
|
||||
<p>
|
||||
Move is similar to Pan, insofar as it involves moving
|
||||
things. But where Pan moves the screen through an image, Move
|
||||
moves an image on the screen. Specifially, move changes the
|
||||
position style of an image with time.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Move takes as parameters a starting position, an ending
|
||||
position, the amount of time it takes to move from the starting
|
||||
position to the ending position, and extra position
|
||||
properties. The positions are given as tuples containing xpos
|
||||
and ypos properties. The positions may be integer or floating
|
||||
point, but it's not permissable to mix the two.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The following example moves a ball from the upper-left to the
|
||||
lower-right of the screen, taking 10 seconds to do so.
|
||||
</p>
|
||||
|
||||
<example>
|
||||
show ball at Move((0.0, 0.0), (1.0, 1.0), 10.0,
|
||||
xanchor="center", yanchor="center")
|
||||
</example>
|
||||
|
||||
</function>
|
||||
|
||||
<p>
|
||||
In general, one wants to use Pan when an image is bigger than the
|
||||
screen, and Move when it is smaller.
|
||||
</p>
|
||||
|
||||
<h3>Transitions</h3>
|
||||
|
||||
@@ -770,13 +970,21 @@ with dissolve
|
||||
will be instantly shown, and then the character image will be
|
||||
dissolved in over the background.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Another use of the "with None" statement is to remove transient
|
||||
elements before a transition begins. By default, the scene list
|
||||
includes transient elements like dialogue, thoughts, and
|
||||
menus. "with None" always executes without these elements, and so
|
||||
gets rid of them.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Finally, the "show", "hide", and "scene" statements all take a
|
||||
with clause. A statement with a with clause associated with it is
|
||||
actually converted into three statements: A "with None" statement,
|
||||
the original statement sans the with clause, and the with clause as
|
||||
a with statement. For example:
|
||||
The "show", "hide", and "scene" statements all take a with
|
||||
clause. One of these statement with a with clause associated with
|
||||
it is actually converted into three statements: A "with None"
|
||||
statement, the original statement sans the with clause, and the
|
||||
with clause as a with statement. For example:
|
||||
</p>
|
||||
|
||||
<example>
|
||||
@@ -807,6 +1015,36 @@ with dissolve
|
||||
other.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
We also allow with clauses to be supplied for say and menu
|
||||
statements. When a with clause is supplied on one of these
|
||||
statements, the transition is used to introduce the say or menu
|
||||
element. For example,
|
||||
</p>
|
||||
|
||||
<example>
|
||||
e "How are you doing?" with dissolve
|
||||
</example>
|
||||
|
||||
<p>
|
||||
Will dissolve in a line of dialogue. The line of dialogue will be
|
||||
dismissed immediately, unless it is followed by a with statement
|
||||
or clause that causes it to transition to something else.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
There is one variable that controls transitions:
|
||||
</p>
|
||||
|
||||
<dl>
|
||||
<var name="default_transition" value="None">
|
||||
If not none, specifies a default transition that is applied to
|
||||
all say and menu statements that are not provided a with
|
||||
clause. This is only considered if the transitions preference is
|
||||
set to "All".
|
||||
</var>
|
||||
</dl>
|
||||
|
||||
<p>
|
||||
Functions that return things that are useful as arguments to with
|
||||
statements or clauses are:
|
||||
@@ -911,8 +1149,11 @@ jump loop_start
|
||||
required here to ensure that saved games with return stacks can
|
||||
return to the proper place when loaded on a changed script. On
|
||||
the other hand, from clauses may be distracting when a game is
|
||||
still under development. A script will be provided to add from
|
||||
clauses to call statements right before a game is released.
|
||||
still under development. We provide with Ren'Py a program, called
|
||||
"add_from", that adds from clauses to all bare calls in the game
|
||||
directory. This program should be run before a final release of
|
||||
your game is made. <b>Be sure to make a backup of your game
|
||||
directory before running add_from.</b>
|
||||
|
||||
</p>
|
||||
|
||||
@@ -1129,11 +1370,16 @@ python:
|
||||
</p><p>
|
||||
|
||||
After the last init block has finished running, the display is
|
||||
initialized, and the actual game can begin. It's expected that
|
||||
each game will have a label named "start". The game is begun by
|
||||
jumping to this start label. Execution proceeds from there,
|
||||
terminating when the end of a file or a return statement is
|
||||
reached.
|
||||
initialized, and the actual game can begin. If the library is
|
||||
present it looks for a label named "main_menu". If it exists, then
|
||||
that label is jumped to as the main menu. Otherwise, the library
|
||||
main menu is displayed. While library.main_menu allows this menu
|
||||
to be customized, the default main menu calles the label "start"
|
||||
when the user chooses "Start Game". The library main menu can
|
||||
also be manually accessed by jumping to _library_main_menu, this
|
||||
is useful if you want to display an intro before the menu is
|
||||
shown.
|
||||
|
||||
</p>
|
||||
|
||||
<h3>Saving, Loading, and Rollback</h3>
|
||||
@@ -1184,6 +1430,7 @@ python:
|
||||
<li>The mappings of image names to Displayable created by the image
|
||||
statement.</li>
|
||||
<li>Configuration variables (config.varname).</li>
|
||||
<li>Library variables (library.varname).</li>
|
||||
<li>Styles (style.stylename).</li>
|
||||
</ul>
|
||||
|
||||
@@ -1344,6 +1591,47 @@ $ state.love_love_points = a + 1
|
||||
such a variable, will be saved, loaded, and rolled-back properly.
|
||||
</p>
|
||||
|
||||
<h3>Persistent Data</h3>
|
||||
|
||||
<p>
|
||||
Persistent data is data that is saved that is not associated with a
|
||||
single game. One possible use of it is to store information about
|
||||
things that have been unlocked, such as an image gallery that is
|
||||
only unlocked when an ending has been reached.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Persistent data is stored as fields on the "persistent"
|
||||
object. This object is special, as uninitialized fields are forced
|
||||
to take the value None. A change to this object is visible in
|
||||
every game that the player undertakes.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Take as an example an unlockable image gallery. The code to
|
||||
display the gallery look like:
|
||||
</p>
|
||||
|
||||
<example>
|
||||
label gallery:
|
||||
|
||||
if not persistent.gallery_unlocked:
|
||||
show background
|
||||
centered "You haven't unlocked this gallery yet."
|
||||
$ renpy.full_restart()
|
||||
|
||||
# Actually show the gallery here.
|
||||
</example>
|
||||
|
||||
<p>
|
||||
Then, to unlock the gallery, run the following code somewhere in
|
||||
your program.
|
||||
</p>
|
||||
|
||||
<example>
|
||||
$ persistent.gallery_unlocked = True
|
||||
</example>
|
||||
|
||||
<h3>Interaction Functions</h3>
|
||||
|
||||
<p>
|
||||
@@ -1371,12 +1659,32 @@ e "My name is shown in full, and in green."
|
||||
|
||||
<!-- func renpy.input -->
|
||||
|
||||
<!-- func renpy.input -->
|
||||
|
||||
<example>
|
||||
$ name = renpy.input("What is your name?", "Joe User", length=20)
|
||||
|
||||
e "Pleased to meet you, %(name)s."
|
||||
</example>
|
||||
|
||||
<!-- func renpy.imagemap -->
|
||||
|
||||
<example>
|
||||
$ result = renpy.imagemap("ground.png", "selected.png", [
|
||||
(100, 100, 300, 400, "eileen"),
|
||||
(500, 100, 700, 400, "lucy")
|
||||
])
|
||||
|
||||
|
||||
if result == "eileen":
|
||||
e "You picked me!"
|
||||
|
||||
elif result == "lucy":
|
||||
e "It looks like you picked Lucy."
|
||||
</example>
|
||||
|
||||
<!-- func renpy.transition -->
|
||||
|
||||
<!-- func renpy.full_restart -->
|
||||
|
||||
<!-- func renpy.quit -->
|
||||
@@ -1385,7 +1693,82 @@ e "Pleased to meet you, %(name)s."
|
||||
|
||||
<!-- func renpy.windows -->
|
||||
|
||||
<h3>Music Functions</h3>
|
||||
<h3>Overlays</h3>
|
||||
|
||||
<p>
|
||||
Overlays are used to display information above the scene currently
|
||||
displayed. The overlay is regenerated each time an interaction
|
||||
with the user begins, making it suitable for displaying to the
|
||||
user things like statistics or dates. The overlay is generally
|
||||
displayed whenever transient things (like dialogue, thoughts and
|
||||
menus) are.
|
||||
</p>
|
||||
|
||||
<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
|
||||
called for each interaction, which allows the overlay to change to
|
||||
reflect the status of game variables.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
As an example, take the following code fragement. When added to a
|
||||
program, this displays a date image in the upper-right corner of
|
||||
the screen (as is done in Kanon). The image shown is based on the
|
||||
variable date. If date is None, then no date is shown. Otherwise,
|
||||
a png file beginning with the value of date is shown.
|
||||
</p>
|
||||
|
||||
<example>
|
||||
init:
|
||||
$ date = "mar25"
|
||||
|
||||
python hide:
|
||||
def date_overlay():
|
||||
if date:
|
||||
return [ Image(date + ".png",
|
||||
xpos=1.0, xanchor="right",
|
||||
ypos=0.0, yanchor="top") ]
|
||||
else:
|
||||
return [ ]
|
||||
|
||||
config.overlay_functions.append(date_overlay)
|
||||
</example>
|
||||
|
||||
<p>
|
||||
Like all config variables, config.overlay_functions should only be
|
||||
changed in an init block. If you need to toggle an overlay on and
|
||||
off, then the overlay function should be conditioned on some
|
||||
normal variable, returning an empty list if it is false. This is
|
||||
done in the example above when date is None.
|
||||
</p>
|
||||
|
||||
|
||||
<h3>Sound and Music Functions</h3>
|
||||
|
||||
<h4>Sound</h4>
|
||||
|
||||
<p>
|
||||
Ren'py supports playing sounds in the background, using the
|
||||
renpy.play function. These sounds must be in wav files, but
|
||||
may live in an archive file.
|
||||
</p>
|
||||
|
||||
<!-- func renpy.play -->
|
||||
|
||||
<example>
|
||||
$ renpy.play("quindar.wav")
|
||||
e "Ground control to Major Tom."
|
||||
</example>
|
||||
|
||||
<p>
|
||||
Sounds can also be associated with buttons, menu choices, and
|
||||
imagemaps becoming hovered and activated. See the section on
|
||||
sound properties in styles for how to used this.
|
||||
</p>
|
||||
|
||||
<h4>Music</h4>
|
||||
|
||||
<p>
|
||||
Ren'Py supports playing music in the background of your
|
||||
@@ -1435,6 +1818,11 @@ show eileen wink
|
||||
e "Just kidding."
|
||||
</example>
|
||||
|
||||
<p>
|
||||
The config.fade_music variable is used to control the fading out
|
||||
of old music when a new track is started.
|
||||
</p>
|
||||
|
||||
<h3>Configuration Variables</h3>
|
||||
|
||||
<p>
|
||||
@@ -1452,7 +1840,6 @@ init:
|
||||
$ config.screen_width = 480
|
||||
</example>
|
||||
|
||||
|
||||
<dl>
|
||||
<var name="config.screen_width" value="800">
|
||||
This sets the width of the screen.
|
||||
@@ -1483,11 +1870,18 @@ init:
|
||||
for loading savegames when the script changes.)
|
||||
</var>
|
||||
|
||||
<var name="config.rollback_length" value="512">
|
||||
<var name="config.rollback_length" value="128">
|
||||
When there are more than this many statements in the rollback log,
|
||||
Ren'Py will consider trimming the log.
|
||||
</var>
|
||||
|
||||
<var name="config.hard_rollback_limit" value="10">
|
||||
This is the number of steps that Ren'Py will let the user
|
||||
interactively rollback. Set this to 0 to disable rollback
|
||||
entirely, although we don't recommend that, as rollback is
|
||||
useful to let the user see text he skipped by mistake.
|
||||
</var>
|
||||
|
||||
<var name="config.profile" value="False">
|
||||
If set to True, some profiling information will be output to
|
||||
stdout (wherever that may go to).
|
||||
@@ -1528,7 +1922,78 @@ init:
|
||||
<var name="config.archives" value="[ ]">
|
||||
A list of archive files that will be searched for images.
|
||||
</var>
|
||||
|
||||
<var name="config.mouse" value="None">
|
||||
If this variable contains a string, that string is taken as an
|
||||
image file that a color mouse cursor is loaded from. It's
|
||||
probably best given as a PNG file, in which case the mouse can
|
||||
have transparency and translucency. If this is None, a
|
||||
black-and-white hardware mouse is used, and the speed of Ren'Py
|
||||
is slightly increased.
|
||||
</var>
|
||||
|
||||
<var name="config.keymouse_distance" value="5">
|
||||
This gives the number of pixels that the mouse is moved every
|
||||
1/20th of a second when the keymouse is being used.
|
||||
</var>
|
||||
|
||||
<var name="config.sound_sample_rate" value="44100">
|
||||
The sample rate that the sound card will be run at. If all of
|
||||
your wav files are of a lower rate, changing this to that rate
|
||||
may make things more efficent.
|
||||
</var>
|
||||
|
||||
<var name="config.overlay_functions" value="[ ]">
|
||||
A list of functions. When called, each function is expected to
|
||||
return a list of displayables, which are added to the overlay
|
||||
list. See the section on overlays for more.
|
||||
</var>
|
||||
|
||||
<var name="config.annoying_text_cps" value="None">
|
||||
If not None, dialogue and thoughts will be "typed" onto the
|
||||
screen a few characters at a time. The value of this is the
|
||||
number of characters that will be typed onto the screen in a
|
||||
second. I strongly reccomend keeping this None. Not only does
|
||||
this interact poorly with the rest of Ren'Py, it's annoying.
|
||||
</var>
|
||||
|
||||
<var name="config.fade_music" value="0.0">
|
||||
This is the amount of time in seconds to spend fading the old track out
|
||||
before a new music track starts. This should probably be fairly
|
||||
short, so the wrong music doesn't play for too long.
|
||||
</var>
|
||||
|
||||
<var name="library.file_page_length" value="4">
|
||||
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.
|
||||
</var>
|
||||
|
||||
<var name="library.thumbnail_width" value="100">
|
||||
The width of the thumbnails that are taken when the game is
|
||||
saved. These thumbnails are shown when the game is
|
||||
loaded. Please note that the thumbnail is shown at the size it
|
||||
was taken at, rather than the value of this setting when the
|
||||
thumbnail is shown to the user.
|
||||
</var>
|
||||
|
||||
<var name="library.thumbnail_height" value="75">
|
||||
The width of the thumbnails that are taken when the game is
|
||||
saved. These thumbnails are shown when the game is
|
||||
loaded. Please note that the thumbnail is shown at the size it
|
||||
was taken at, rather than the value of this setting when the
|
||||
thumbnail is shown to the user.
|
||||
</var>
|
||||
|
||||
<var name="library.main_menu" value='[ ( "Start Game", "start" ), ("Continue Game", "_continue"), ("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.
|
||||
</var>
|
||||
|
||||
</dl>
|
||||
|
||||
<h3>Properties and Styles</h3>
|
||||
@@ -1625,6 +2090,15 @@ init:
|
||||
condition it on the result of renpy.windows().
|
||||
</prop>
|
||||
|
||||
<prop name="textalign">
|
||||
This is used to control the horizontal alignment of the lines of
|
||||
text in the area allocated to the Text widget containing that
|
||||
text. It only really has any effect if the text is more than one
|
||||
line long. It's a number between 0 and 1, which gives the fraction
|
||||
of empty space that should be to the left of each line of
|
||||
text. (To center text, it should be 0.5.)
|
||||
</prop>
|
||||
|
||||
<h4>Window Properties</h4>
|
||||
|
||||
<p>
|
||||
@@ -1739,6 +2213,27 @@ init:
|
||||
dimension. This can be one of 'top', 'center', or 'bottom'.
|
||||
</prop>
|
||||
|
||||
<h4>Sound Properties</h4>
|
||||
|
||||
<p>
|
||||
Some widgets can take sound style properties. These widgets can cause
|
||||
a sound to play when they become hovered (see below), and also
|
||||
when they are selected. Currently, the styles that can take sound
|
||||
properties belong to buttons, imagemaps, and menu choices. Sound
|
||||
style properties are either the name of a wav file which is played
|
||||
with renpy.play when appropriate event occurs, or None to disable
|
||||
sound playback.
|
||||
</p>
|
||||
|
||||
<prop name="hover_sound">
|
||||
The sound to play when this widget becomes hovered.
|
||||
</prop>
|
||||
|
||||
<prop name="activate_sound">
|
||||
The sound to play when the widget is activated, by clicking on or
|
||||
otherwise selecting it.
|
||||
</prop>
|
||||
|
||||
<h4>Hovering</h4>
|
||||
|
||||
<p>
|
||||
@@ -1762,7 +2257,7 @@ init:
|
||||
<example>
|
||||
init:
|
||||
style.button.hover_background = Solid((255, 255, 255, 255))
|
||||
style.button.idle_background = Solid(0, 0, 0, 255))
|
||||
style.button.idle_background = Solid((0, 0, 0, 255))
|
||||
</example>
|
||||
|
||||
|
||||
@@ -1845,72 +2340,7 @@ init:
|
||||
|
||||
<dl>
|
||||
|
||||
<renpy_style name="default">The default style that all styles inherit from.</renpy_style>
|
||||
|
||||
<renpy_style name="window"><renpy_style_inherits>default</renpy_style_inherits>(window, placement) The base style for the windows that contain dialogue, thoughts, and menus.</renpy_style>
|
||||
|
||||
<renpy_style name="image_placement">This style is used to control the default placement of images on the screen.</renpy_style>
|
||||
|
||||
<renpy_style name="say_label"><renpy_style_inherits>default</renpy_style_inherits>(text) The style that is used by default for the label of dialogue. The label is used to indicate who is saying something.</renpy_style>
|
||||
|
||||
<renpy_style name="say_dialogue"><renpy_style_inherits>default</renpy_style_inherits>(text) The style that is used by default for the text of dialogue.</renpy_style>
|
||||
|
||||
<renpy_style name="say_thought"><renpy_style_inherits>default</renpy_style_inherits>(text) The label that is used by default for the text of thoughts or narration, when no speaker is given.</renpy_style>
|
||||
|
||||
<renpy_style name="say_window"><renpy_style_inherits>window</renpy_style_inherits>(window, position) The default style for windows containing dialogue and thoughts.</renpy_style>
|
||||
|
||||
<renpy_style name="menu_caption"><renpy_style_inherits>default</renpy_style_inherits>(text) The style that is used to render a menu caption.</renpy_style>
|
||||
|
||||
<renpy_style name="menu_selected"><renpy_style_inherits>default</renpy_style_inherits>(text) The style that is used to render a selected menu choice.</renpy_style>
|
||||
|
||||
<renpy_style name="menu_unselected"><renpy_style_inherits>default</renpy_style_inherits>(text) The style that is used to render an unselected menu choice.</renpy_style>
|
||||
|
||||
<renpy_style name="menu_window"><renpy_style_inherits>window</renpy_style_inherits>(window, position) The default style for windows containing a menu.</renpy_style>
|
||||
|
||||
<renpy_style name="input_text"><renpy_style_inherits>default</renpy_style_inherits>(text) The style used for the text of an input box.</renpy_style>
|
||||
|
||||
<renpy_style name="input_prompt"><renpy_style_inherits>default</renpy_style_inherits>(text) The style used for the prompt of an input box.</renpy_style>
|
||||
|
||||
<renpy_style name="input_window"><renpy_style_inherits>window</renpy_style_inherits>(window, position) The style used for the window of an input box.</renpy_style>
|
||||
|
||||
<renpy_style name="button"><renpy_style_inherits>default</renpy_style_inherits>(window, hover) The default style used for buttons in the main and game menus.</renpy_style>
|
||||
|
||||
<renpy_style name="button_text"><renpy_style_inherits>default</renpy_style_inherits>(text, hover) The default style used for the label of a button.</renpy_style>
|
||||
|
||||
<renpy_style name="selected_button"><renpy_style_inherits>button</renpy_style_inherits>(window, hover) The style that is used for a selected button (for example, the active screen or a chosen preference).</renpy_style>
|
||||
|
||||
<renpy_style name="selected_button_text"><renpy_style_inherits>button_text</renpy_style_inherits>(text, hover) The style that is used for the label of a selected button.</renpy_style>
|
||||
|
||||
<renpy_style name="disabled_button"><renpy_style_inherits>button</renpy_style_inherits>(window, hover) The style that is used for a disabled button.</renpy_style>
|
||||
|
||||
<renpy_style name="disabled_button_text"><renpy_style_inherits>button_text</renpy_style_inherits>(text, hover) The style that is used for the label of a disabled button.</renpy_style>
|
||||
|
||||
<renpy_style name="mm_root_window"><renpy_style_inherits>default</renpy_style_inherits>(window) The style used for the root window of the main menu. This is primarily used to set a background for the main menu.</renpy_style>
|
||||
|
||||
<renpy_style name="mm_menu_window"><renpy_style_inherits>default</renpy_style_inherits>(window, position) A window that contains the choices in the main menu. Change this to change the placement of these choices on the main menu screen.</renpy_style>
|
||||
|
||||
<renpy_style name="mm_button"><renpy_style_inherits>button</renpy_style_inherits>(window, hover) The style that is used on buttons that are part of the main menu.</renpy_style>
|
||||
|
||||
<renpy_style name="mm_button_text"><renpy_style_inherits>button_text</renpy_style_inherits>(text, hover) The style that is used for the labels of buttons that are part of the main menu.</renpy_style>
|
||||
|
||||
<renpy_style name="gm_root_window"><renpy_style_inherits>default</renpy_style_inherits>(window) The style used for the root window of the game menu. This is primarily used to change the background of the game menu.</renpy_style>
|
||||
|
||||
<renpy_style name="gm_nav_window"><renpy_style_inherits>default</renpy_style_inherits>(window, placement) The style used by a window containing buttons that allow the user to navigate through the different screens of the game menu.</renpy_style>
|
||||
|
||||
<renpy_style name="file_picker_window"><renpy_style_inherits>default</renpy_style_inherits>(window, placement) A window containing the file picker that is used to choose slots for loading and saving.</renpy_style>
|
||||
|
||||
<renpy_style name="file_picker_entry"><renpy_style_inherits>button</renpy_style_inherits>(window, hover) The style that is used for each of the slots in the file picker.</renpy_style>
|
||||
|
||||
<renpy_style name="file_picker_text"><renpy_style_inherits>default</renpy_style_inherits>(text) A base style for all text that is displayed in the file picker.</renpy_style>
|
||||
|
||||
<renpy_style name="file_picker_new"><renpy_style_inherits>file_picker_text</renpy_style_inherits>(text) The style that is applied to the new indicator in the file picker.</renpy_style>
|
||||
|
||||
<renpy_style name="file_picker_old"><renpy_style_inherits>file_picker_text</renpy_style_inherits>(text) The style that is applied to the old indicator in the file picker.</renpy_style>
|
||||
|
||||
<renpy_style name="file_picker_extra_info"><renpy_style_inherits>file_picker_text</renpy_style_inherits>(text) The style that is applied to extra info in the file picker. The extra info is the save time, and the save_name if one exists.</renpy_style>
|
||||
|
||||
<renpy_style name="file_picker_new_slot"><renpy_style_inherits>file_picker_text</renpy_style_inherits>(text) The style that is used for the new slot indicator in the file picker.</renpy_style>
|
||||
|
||||
<!-- include styles.xml -->
|
||||
|
||||
</dl>
|
||||
|
||||
@@ -1965,6 +2395,47 @@ init:
|
||||
that need to be loaded.
|
||||
</p>
|
||||
|
||||
<h3>Localization</h3>
|
||||
|
||||
<p>
|
||||
While Ren'Py is by default set up to operate in an English
|
||||
speaking environment, it is not limited to such settings. Assuming
|
||||
a proper font is loaded, Ren'Py scripts can contain any language
|
||||
expressible in Unicode.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
There are two things in the Ren'Py library that may need to be
|
||||
translated into a user's language. The first is the main
|
||||
menu. There is no explicit support for doing this, but as the
|
||||
library.main_menu variable supports changing the text of the main
|
||||
menu, it also supports translating said text.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The second thing that needs to be translated is the game menu. The
|
||||
library.translations dictionary is used to translate text in the
|
||||
game menu into your language. If a key in this map corresponds to
|
||||
the English text that would be displayed, the value corresponding
|
||||
to that key is displayed again. For example:
|
||||
</p>
|
||||
|
||||
<example>
|
||||
init:
|
||||
$ library.translations = {
|
||||
"Yes" : u"HIja'",
|
||||
"No" : u"ghobe'",
|
||||
# etc.
|
||||
}
|
||||
</example>
|
||||
|
||||
<p>
|
||||
The u characters prefixed to the strings on the right, while not
|
||||
strictly necessary in this case, are used to tell Python that the
|
||||
string is in Unicode rather than ASCII. This is useful if your
|
||||
language uses non-ascii characters.
|
||||
</p>
|
||||
|
||||
<h3>Function Index</h3>
|
||||
|
||||
<funcindex/>
|
||||
@@ -1981,6 +2452,15 @@ init:
|
||||
|
||||
<styleindex/>
|
||||
|
||||
<h3>Example Script</h3>
|
||||
|
||||
<p>
|
||||
For your browsing pleasure, we present a syntax-highlighted
|
||||
version of the <a href="example.html">demo that ships with
|
||||
Ren'Py</a>. The start of this script has been commented to make
|
||||
it easier to understand.
|
||||
</p>
|
||||
|
||||
</doc>
|
||||
|
||||
<!-- (define-key xml-mode-map [(control return)] 'tompy-xml-ctrlret) -->
|
||||
+184
-5
@@ -1,9 +1,188 @@
|
||||
; (X)Emacs mode. Requires python-mode to be installed to work.
|
||||
;
|
||||
; To use, M-x load-file renpy-mode.el RET M-x renpy-mode RET
|
||||
|
||||
(require 'speedbar)
|
||||
(require 'python-mode)
|
||||
|
||||
; How we highlight a single keyword.
|
||||
(defun renpy-keyword (kw)
|
||||
(list
|
||||
(concat "\\b" kw "\\b")
|
||||
'( 0 font-lock-keyword-face)
|
||||
)
|
||||
)
|
||||
|
||||
; The big list of things we highlight.
|
||||
(setq renpy-font-lock-keywords
|
||||
(list
|
||||
(renpy-keyword "at")
|
||||
(renpy-keyword "call")
|
||||
(renpy-keyword "hide")
|
||||
(renpy-keyword "if")
|
||||
(renpy-keyword "image")
|
||||
(renpy-keyword "init")
|
||||
(renpy-keyword "jump")
|
||||
(renpy-keyword "menu")
|
||||
(renpy-keyword "python")
|
||||
(renpy-keyword "return")
|
||||
(renpy-keyword "scene")
|
||||
(renpy-keyword "set")
|
||||
(renpy-keyword "show")
|
||||
(renpy-keyword "with")
|
||||
(renpy-keyword "while")
|
||||
|
||||
; Python keywords we want to keep highlighting.
|
||||
|
||||
(renpy-keyword "and")
|
||||
(renpy-keyword "assert")
|
||||
(renpy-keyword "break")
|
||||
(renpy-keyword "class")
|
||||
(renpy-keyword "continue")
|
||||
(renpy-keyword "def")
|
||||
(renpy-keyword "del")
|
||||
(renpy-keyword "elif")
|
||||
(renpy-keyword "else")
|
||||
(renpy-keyword "except")
|
||||
(renpy-keyword "exec")
|
||||
(renpy-keyword "finally")
|
||||
(renpy-keyword "for")
|
||||
(renpy-keyword "from")
|
||||
(renpy-keyword "global")
|
||||
(renpy-keyword "import")
|
||||
(renpy-keyword "in")
|
||||
(renpy-keyword "is")
|
||||
(renpy-keyword "lambda")
|
||||
(renpy-keyword "not")
|
||||
(renpy-keyword "or")
|
||||
(renpy-keyword "pass")
|
||||
(renpy-keyword "print")
|
||||
(renpy-keyword "raise")
|
||||
(renpy-keyword "try")
|
||||
(renpy-keyword "yield")
|
||||
|
||||
'("\\$" (0 font-lock-keyword-face) )
|
||||
|
||||
'("\\b\\(label\\|menu\\)\\s-+\\(\\w+\\):" (1 font-lock-keyword-face) (2 font-lock-function-name-face))
|
||||
'("\\b\\(from\\)\\s-+\\(\\w+\\)" (1 font-lock-keyword-face) (2 font-lock-function-name-face))
|
||||
'("\\b\\(def\\|class\\)\\s-+\\(\\w+\\)" (1 font-lock-keyword-face) (2 font-lock-function-name-face))
|
||||
))
|
||||
|
||||
(setq renpy-generic-imenu
|
||||
'( ( "labels" "\\b\\(label\\|menu\\)\\s-+\\(\\w+\\):" 2)
|
||||
( "labels" "\\bcall\\s-+\\w+\\s-+from\\s-+\\(\\w+\\)" 1)
|
||||
( "python" "\\b\\(def\\|class\\)\\s-+\\(\\w+\\)" 2)
|
||||
))
|
||||
|
||||
(defun renpy-mode ()
|
||||
(interactive)
|
||||
(python-mode)
|
||||
(setq font-lock-keywords
|
||||
(append '( ("\\b\\(menu\\|call\\|\\$\\|python\\|image\\|scene\\|show\\|hide\\|init\\|set\\|jump\\|at\\|with\\)\\b" (0 font-lock-keyword-face))
|
||||
("\\b\\(label\\|menu\\)\\s-+\\(\\w+\\):" (1 font-lock-keyword-face) (2 font-lock-function-name-face))
|
||||
) python-font-lock-keywords))
|
||||
|
||||
; (setq font-lock-keywords
|
||||
; (append '( ("\\b\\(menu\\|call\\|\\$\\|python\\|image\\|scene\\|show\\|hide\\|init\\|set\\|jump\\|at\\|with\\)\\b" (0 font-lock-keyword-face))
|
||||
; ("\\b\\(label\\|menu\\)\\s-+\\(\\w+\\):" (1 font-lock-keyword-face) (2 font-lock-function-name-face))
|
||||
; ) python-font-lock-keywords))
|
||||
|
||||
(setq mode-name "RenPy")
|
||||
(setq major-mode 'renpy-mode)
|
||||
|
||||
(setq imenu-create-index-function 'imenu-default-create-index-function)
|
||||
(setq imenu-generic-expression renpy-generic-imenu)
|
||||
(setq font-lock-keywords renpy-font-lock-keywords)
|
||||
(setq semantic-toplevel-bovine-table nil)
|
||||
|
||||
(font-lock-mode 1)
|
||||
(font-lock-fontify-buffer))
|
||||
(font-lock-fontify-buffer)
|
||||
(auto-fill-mode 1)
|
||||
(setq indent-line-function 'renpy-indent-line)
|
||||
(setq fill-paragraph-function 'renpy-fill-paragraph)
|
||||
)
|
||||
|
||||
(speedbar-add-supported-extension ".rpy")
|
||||
|
||||
(setq auto-mode-alist (cons '("\\.rpy\\'" . renpy-mode) auto-mode-alist))
|
||||
|
||||
; Computes the start of the current string.
|
||||
(defun renpy-string-start ()
|
||||
(nth 8 (parse-partial-sexp (point-min) (point)))
|
||||
)
|
||||
|
||||
; Computes the amount of indentation needed to put the current string
|
||||
; in the right spot.
|
||||
(defun renpy-string-indentation ()
|
||||
(+ 1
|
||||
(save-excursion
|
||||
(- (goto-char (renpy-string-start))
|
||||
(progn (beginning-of-line) (point)))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
; Figures out the prefix, without the line indentation, required to
|
||||
; get strings to line up right after a fill.
|
||||
(defun renpy-string-fill-prefix ()
|
||||
(make-string
|
||||
(- (renpy-string-indentation)
|
||||
0
|
||||
; (save-excursion
|
||||
; (goto-char (renpy-string-start))
|
||||
; (current-indentation)
|
||||
; )
|
||||
) ?\ )
|
||||
)
|
||||
|
||||
|
||||
; Indents a paragraph. We also handle strings properly.
|
||||
(defun renpy-fill-paragraph (&optional justify)
|
||||
(interactive)
|
||||
(if (eq (py-in-literal) 'string)
|
||||
(let* ((string-indentation (renpy-string-indentation))
|
||||
(fill-prefix (renpy-string-fill-prefix))
|
||||
(fill-column (- fill-column string-indentation))
|
||||
(fill-paragraph-function nil)
|
||||
(indent-line-function nil)
|
||||
)
|
||||
|
||||
; Fixup the fill.
|
||||
;(save-excursion
|
||||
; (goto-char (+ 1 (nth 8 (parse-partial-sexp (point-min) (point)))))
|
||||
; (insert (make-string (renpy-string-indentation) ?\ ))
|
||||
; )
|
||||
|
||||
(message "fill prefix: %S" fill-prefix)
|
||||
; (py-fill-paragraph justify)
|
||||
|
||||
(py-fill-string (renpy-string-start))
|
||||
|
||||
;(save-excursion
|
||||
; (goto-char (+ 1 (nth 8 (parse-partial-sexp (point-min) (point)))))
|
||||
; (delete-char (renpy-string-indentation))
|
||||
; )
|
||||
|
||||
t
|
||||
)
|
||||
(py-fill-paragraph justify)
|
||||
)
|
||||
)
|
||||
|
||||
; Indents the current line.
|
||||
(defun renpy-indent-line (&optional arg)
|
||||
(interactive)
|
||||
|
||||
; Let python-mode indent. (Always needed to keep python-mode sane.)
|
||||
(py-indent-line)
|
||||
|
||||
; Reindent strings if appropriate.
|
||||
(save-excursion
|
||||
(beginning-of-line)
|
||||
(if (eq (py-in-literal) 'string)
|
||||
(progn
|
||||
(delete-horizontal-space)
|
||||
(indent-to (renpy-string-indentation))
|
||||
)
|
||||
))
|
||||
|
||||
(if ( < (current-column) (current-indentation) )
|
||||
(back-to-indentation) )
|
||||
|
||||
)
|
||||
|
||||
@@ -18,6 +18,7 @@ import renpy.parser
|
||||
import renpy.python # object
|
||||
import renpy.script
|
||||
import renpy.style
|
||||
import renpy.sound
|
||||
|
||||
import renpy.display
|
||||
import renpy.display.core # object
|
||||
|
||||
+36
-11
@@ -96,25 +96,44 @@ class Node(object):
|
||||
return [ self.next ]
|
||||
|
||||
|
||||
def say_menu_with(expression):
|
||||
"""
|
||||
This handles the with clause of a say or menu statement.
|
||||
"""
|
||||
|
||||
if expression is not None:
|
||||
what = renpy.python.py_eval(expression)
|
||||
elif renpy.store.default_transition and renpy.game.preferences.transitions == 2:
|
||||
what = renpy.store.default_transition
|
||||
else:
|
||||
return
|
||||
|
||||
if not what:
|
||||
return
|
||||
|
||||
if renpy.game.preferences.transitions:
|
||||
renpy.game.interface.set_transition(what)
|
||||
|
||||
|
||||
class Say(Node):
|
||||
|
||||
def __init__(self, loc, who, what):
|
||||
def __init__(self, loc, who, what, with):
|
||||
|
||||
super(Say, self).__init__(loc)
|
||||
|
||||
self.who = who
|
||||
self.what = what
|
||||
self.with = with
|
||||
|
||||
def execute(self):
|
||||
|
||||
import renpy.exports as exports
|
||||
|
||||
if self.who is not None:
|
||||
who = renpy.python.py_eval(self.who)
|
||||
else:
|
||||
who = None
|
||||
|
||||
exports.say(who, self.what % renpy.game.store)
|
||||
say_menu_with(self.with)
|
||||
renpy.exports.say(who, self.what % renpy.game.store)
|
||||
|
||||
return self.next
|
||||
|
||||
@@ -358,14 +377,20 @@ class With(Node):
|
||||
super(With, self).__init__(loc)
|
||||
self.expr = expr
|
||||
|
||||
|
||||
def execute(self):
|
||||
trans = renpy.python.py_eval(self.expr)
|
||||
|
||||
# Code copied into exports.with
|
||||
|
||||
if not trans:
|
||||
sls = renpy.game.context().scene_lists
|
||||
sls.replace_old_master()
|
||||
renpy.game.interface.with_none()
|
||||
else:
|
||||
renpy.game.interface.interact(trans)
|
||||
if renpy.game.preferences.transitions:
|
||||
renpy.game.interface.set_transition(trans)
|
||||
renpy.game.interface.interact(show_mouse=False,
|
||||
trans_pause=True,
|
||||
suppress_overlay=True)
|
||||
|
||||
return self.next
|
||||
|
||||
@@ -403,11 +428,12 @@ class Return(Node):
|
||||
|
||||
class Menu(Node):
|
||||
|
||||
def __init__(self, loc, items, set):
|
||||
def __init__(self, loc, items, set, with):
|
||||
super(Menu, self).__init__(loc)
|
||||
|
||||
self.items = items
|
||||
self.set = set
|
||||
self.with = with
|
||||
|
||||
def get_children(self):
|
||||
rv = [ ]
|
||||
@@ -429,8 +455,6 @@ class Menu(Node):
|
||||
|
||||
def execute(self):
|
||||
|
||||
import renpy.exports as exports
|
||||
|
||||
choices = [ ]
|
||||
|
||||
for i, (label, condition, block) in enumerate(self.items):
|
||||
@@ -439,7 +463,8 @@ class Menu(Node):
|
||||
else:
|
||||
choices.append((label, condition, i))
|
||||
|
||||
choice = exports.menu(choices, self.set)
|
||||
say_menu_with(self.with)
|
||||
choice = renpy.exports.menu(choices, self.set)
|
||||
|
||||
if choice is None:
|
||||
return self.next
|
||||
|
||||
+27
-4
@@ -23,11 +23,15 @@ debug = False
|
||||
rollback_enabled = True
|
||||
|
||||
# If the rollback is longer than this, we may trim it.
|
||||
rollback_length = 512
|
||||
rollback_length = 128
|
||||
|
||||
# A list of Displayables that should always be added to the end
|
||||
# of the scene list.
|
||||
overlay = [ ]
|
||||
# The maximum number of steps the user can rollback the game,
|
||||
# interactively.
|
||||
hard_rollback_limit = 10
|
||||
|
||||
# A list of functions returning lists of displayables that will be
|
||||
# added to the end of the display list.
|
||||
overlay_functions = [ ]
|
||||
|
||||
# A list of Displayables that should always be added to the start
|
||||
# of the scene list. (Mostly used for keymaps and the like.)
|
||||
@@ -59,6 +63,25 @@ skip_delay = 100
|
||||
# Archive files that are searched for images.
|
||||
archives = [ ]
|
||||
|
||||
# If True, we will only try loading from archives.
|
||||
# Only useful for debugging Ren'Py, don't document.
|
||||
force_archives = False
|
||||
|
||||
# An image file containing the mouse cursor, if one is defined.
|
||||
mouse = None
|
||||
|
||||
# The distance the keyboard moves the mouse, per 50 ms tick, in pixels.
|
||||
keymouse_distance = 5
|
||||
|
||||
# The default sound playback sample rate.
|
||||
sound_sample_rate = 44100
|
||||
|
||||
# How fast text is displayed on the screen, by default.
|
||||
annoying_text_cps = None
|
||||
|
||||
# The amount of time music is faded out between tracks.
|
||||
fade_music = 0.0
|
||||
|
||||
_globals = globals().copy()
|
||||
|
||||
def reload():
|
||||
|
||||
@@ -43,6 +43,35 @@ class Keymap(renpy.display.layout.Container):
|
||||
def render(self, width, height, st):
|
||||
return None
|
||||
|
||||
class KeymouseBehavior(renpy.display.layout.Null):
|
||||
"""
|
||||
This is a class that causes the keyboard to move the mouse. It's
|
||||
useful on the game and key menus, as well as in imagemaps and the
|
||||
like.
|
||||
"""
|
||||
|
||||
def event(self, ev, x, y):
|
||||
if ev.type == renpy.display.core.DISPLAYTIME:
|
||||
|
||||
pressed = pygame.key.get_pressed()
|
||||
|
||||
x, y = pygame.mouse.get_pos()
|
||||
ox, oy = x, y
|
||||
|
||||
if pressed[K_LEFT]:
|
||||
x -= renpy.config.keymouse_distance
|
||||
if pressed[K_RIGHT]:
|
||||
x += renpy.config.keymouse_distance
|
||||
if pressed[K_UP]:
|
||||
y -= renpy.config.keymouse_distance
|
||||
if pressed[K_DOWN]:
|
||||
y += renpy.config.keymouse_distance
|
||||
|
||||
if (x, y) != (ox, oy):
|
||||
pygame.mouse.set_pos((x, y))
|
||||
|
||||
return None
|
||||
|
||||
class SayBehavior(renpy.display.layout.Null):
|
||||
"""
|
||||
This is a class that implements the say behavior,
|
||||
@@ -62,7 +91,7 @@ class SayBehavior(renpy.display.layout.Null):
|
||||
if ev.type == renpy.display.core.DISPLAYTIME and \
|
||||
self.delay and \
|
||||
ev.duration > self.delay:
|
||||
return True
|
||||
return False
|
||||
|
||||
if ev.type == MOUSEBUTTONDOWN:
|
||||
if ev.button == 1:
|
||||
@@ -108,6 +137,13 @@ class Menu(renpy.display.layout.VBox):
|
||||
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.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))
|
||||
|
||||
@@ -128,14 +164,14 @@ class Menu(renpy.display.layout.VBox):
|
||||
|
||||
# Captions should stay the default text color.
|
||||
if result is None:
|
||||
child.set_style('menu_caption')
|
||||
child.set_style(self.caption_style)
|
||||
continue
|
||||
|
||||
# Actual choices change color if they are selected or not.
|
||||
if i == self.selected:
|
||||
child.set_style('menu_selected')
|
||||
child.set_style(self.selected_style)
|
||||
else:
|
||||
child.set_style('menu_unselected')
|
||||
child.set_style(self.unselected_style)
|
||||
|
||||
|
||||
def event(self, ev, x, y):
|
||||
@@ -164,6 +200,7 @@ class Menu(renpy.display.layout.VBox):
|
||||
return None
|
||||
|
||||
if self.results[target] is not None:
|
||||
renpy.sound.play(self.selected_style.activate_sound)
|
||||
return self.results[target]
|
||||
|
||||
# Change selection based on keypress.
|
||||
@@ -190,13 +227,16 @@ class Menu(renpy.display.layout.VBox):
|
||||
|
||||
# Make selection based on keypress.
|
||||
if ev.type == KEYDOWN and ev.key == K_RETURN:
|
||||
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('menu_selected')
|
||||
self.children[old_selected].set_style('menu_unselected')
|
||||
self.children[self.selected].set_style(self.selected_style)
|
||||
self.children[old_selected].set_style(self.unselected_style)
|
||||
|
||||
renpy.sound.play(self.selected_style.hover_sound)
|
||||
|
||||
renpy.game.interface.redraw(0)
|
||||
|
||||
@@ -241,8 +281,14 @@ class Button(renpy.display.layout.Window):
|
||||
self.old_hover = inside
|
||||
self.set_hover(inside)
|
||||
|
||||
if ev.type == MOUSEBUTTONDOWN and ev.button == 1:
|
||||
if inside:
|
||||
renpy.sound.play(self.style.hover_sound)
|
||||
|
||||
|
||||
if (ev.type == MOUSEBUTTONDOWN and ev.button == 1) or \
|
||||
(ev.type == KEYDOWN and ev.key == K_RETURN):
|
||||
if inside:
|
||||
renpy.sound.play(self.style.activate_sound)
|
||||
return self.clicked()
|
||||
|
||||
return None
|
||||
|
||||
+221
-48
@@ -33,6 +33,18 @@ class Displayable(renpy.object.Object):
|
||||
their fields.
|
||||
"""
|
||||
|
||||
def parameterize(self, name, parameters):
|
||||
"""
|
||||
Called to parameterize this. By default, we don't take any
|
||||
parameters.
|
||||
"""
|
||||
|
||||
if parameters:
|
||||
raise Exception("Image '%s' can't take parameters '%s'. (Perhaps you got the name wrong?)" %
|
||||
(' '.join(name), ' '.join(parameters)))
|
||||
|
||||
return self
|
||||
|
||||
def render(self, width, height, shown_time):
|
||||
"""
|
||||
Called to display this displayable. This is called with width
|
||||
@@ -163,7 +175,6 @@ class SceneLists(object):
|
||||
drawn.
|
||||
|
||||
@ivar master: The current master display list.
|
||||
@ivar old_master: The MDL that was last shown to the user.
|
||||
@ivar transient: The current transient display list.
|
||||
@ivar overlay: The current overlay display list.
|
||||
|
||||
@@ -174,7 +185,6 @@ class SceneLists(object):
|
||||
def __init__(self, oldsl=None):
|
||||
|
||||
if oldsl:
|
||||
self.old_master = oldsl.old_master[:]
|
||||
self.master = oldsl.master[:]
|
||||
self.replace_transient()
|
||||
|
||||
@@ -183,7 +193,6 @@ class SceneLists(object):
|
||||
self.music = oldsl.music
|
||||
|
||||
else:
|
||||
self.old_master = [ ]
|
||||
self.master = [ ]
|
||||
self.transient = [ ]
|
||||
self.overlay = [ ]
|
||||
@@ -196,10 +205,8 @@ class SceneLists(object):
|
||||
"""
|
||||
|
||||
rv = SceneLists()
|
||||
rv.old_master = self.old_master[:]
|
||||
rv.master = self.master[:]
|
||||
rv.transient = self.transient[:]
|
||||
rv.overlay = self.overlay[:]
|
||||
|
||||
rv.music = self.music
|
||||
|
||||
@@ -216,14 +223,6 @@ class SceneLists(object):
|
||||
|
||||
self.transient = [ ]
|
||||
|
||||
def replace_old_master(self):
|
||||
"""
|
||||
Replaces the contents of the old master display list with
|
||||
a copy of the current master display list.
|
||||
"""
|
||||
|
||||
self.old_master = self.master[:]
|
||||
|
||||
def add(self, listname, thing, key=None):
|
||||
"""
|
||||
This is called to add something to a display list. Listname is
|
||||
@@ -291,14 +290,6 @@ class SceneLists(object):
|
||||
|
||||
setattr(self, listname, nl)
|
||||
|
||||
def set_overlay(self, new_overlay):
|
||||
"""
|
||||
This replaces the overlay scene list with the provided overlay
|
||||
scene list.
|
||||
"""
|
||||
|
||||
self.overlay = [ (None, time.time(), i) for i in new_overlay ]
|
||||
|
||||
def render_scene_list(sl, width, height):
|
||||
"""
|
||||
This renders the scene list sl, and returns a rendered
|
||||
@@ -338,11 +329,19 @@ class Display(object):
|
||||
|
||||
@ivar window: The window that is being presented to the user.
|
||||
|
||||
@ivar sample_surface: A sample surface that is optimized for
|
||||
fast blitting to thew window. Used to create other surfaces from.
|
||||
@ivar buffer: A surface that buffers the window.
|
||||
|
||||
@ivar sample_surface: A sample surface that is optimized for fast
|
||||
blitting to the window, with alpha. Used to create other surfaces
|
||||
from.
|
||||
|
||||
@ivar fullscreen: Is the window in fullscreen mode?
|
||||
|
||||
@ivar mouse: The mouse image, if we have one, or None if
|
||||
we do not have one.
|
||||
|
||||
@ivar mouse_location: The mouse location the last time it was
|
||||
drawn, or None if it wasn't drawn the last time around.
|
||||
"""
|
||||
|
||||
|
||||
@@ -350,6 +349,7 @@ class Display(object):
|
||||
|
||||
# It shouldn't matter if pygame is already initialized.
|
||||
pygame.init()
|
||||
renpy.sound.init()
|
||||
|
||||
self.fullscreen = renpy.game.preferences.fullscreen
|
||||
fsflag = 0
|
||||
@@ -357,18 +357,71 @@ class Display(object):
|
||||
if self.fullscreen:
|
||||
fsflag = FULLSCREEN
|
||||
|
||||
|
||||
# The window we display things in.
|
||||
self.window = pygame.display.set_mode((renpy.config.screen_width,
|
||||
renpy.config.screen_height),
|
||||
fsflag)
|
||||
|
||||
pygame.event.set_grab(False)
|
||||
pygame.mouse.set_visible(True)
|
||||
|
||||
pygame.display.set_caption(renpy.config.window_title)
|
||||
# The mouse buffer.
|
||||
if renpy.config.mouse:
|
||||
self.buffer = pygame.Surface((renpy.config.screen_width,
|
||||
renpy.config.screen_height))
|
||||
|
||||
# Sample surface that all surfaces are created based on.
|
||||
self.sample_surface = self.window.convert_alpha()
|
||||
|
||||
pygame.event.set_grab(False)
|
||||
|
||||
# Window title.
|
||||
pygame.display.set_caption(renpy.config.window_title)
|
||||
|
||||
# Load the mouse image, if any.
|
||||
if renpy.config.mouse:
|
||||
self.mouse = renpy.display.image.cache.load_image(renpy.config.mouse)
|
||||
pygame.mouse.set_visible(False)
|
||||
else:
|
||||
self.mouse = None
|
||||
pygame.mouse.set_visible(True)
|
||||
|
||||
self.mouse_location = None
|
||||
|
||||
def draw_mouse(self):
|
||||
"""
|
||||
This draws the mouse to the screen, if necessary. It uses the
|
||||
buffer to minimize the amount of the screen that needs to be
|
||||
drawn, and only redraws if the mouse has actually been moved.
|
||||
"""
|
||||
|
||||
if not self.mouse:
|
||||
return
|
||||
|
||||
mw, mh = self.mouse.get_size()
|
||||
pos = pygame.mouse.get_pos()
|
||||
|
||||
if not pygame.mouse.get_focused():
|
||||
pos = None
|
||||
|
||||
flip = False
|
||||
|
||||
|
||||
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
|
||||
|
||||
if pos and (pos != self.mouse_location):
|
||||
self.window.blit(self.mouse, pos)
|
||||
flip = True
|
||||
|
||||
self.mouse_location = pos
|
||||
|
||||
|
||||
if flip:
|
||||
pygame.display.flip()
|
||||
|
||||
|
||||
|
||||
|
||||
def show(self, transient):
|
||||
"""
|
||||
Draws the current transient screen list to the screen.
|
||||
@@ -383,7 +436,15 @@ class Display(object):
|
||||
|
||||
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):
|
||||
@@ -416,12 +477,26 @@ class Interface(object):
|
||||
|
||||
@ivar display: The display that we used to display the screen.
|
||||
|
||||
@ivar needs_reraw: True if we need a redraw now.
|
||||
@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.
|
||||
|
||||
@ivar transition: If not None, the transition to be applied for the
|
||||
next interaction.
|
||||
|
||||
@ivar supress_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.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
@@ -430,6 +505,10 @@ class Interface(object):
|
||||
self.profile_time = time.time()
|
||||
self.screenshot = None
|
||||
self.redraw_time = 0.0
|
||||
self.old_scene = [ ]
|
||||
self.transition = None
|
||||
self.supress_transition = False
|
||||
self.quick_quit = False
|
||||
|
||||
def take_screenshot(self, scale):
|
||||
"""
|
||||
@@ -470,13 +549,54 @@ class Interface(object):
|
||||
self.needs_redraw = True
|
||||
else:
|
||||
self.redraw_time = min(self.redraw_time, time.time() + delay)
|
||||
|
||||
def interact(self, transition=None):
|
||||
|
||||
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):
|
||||
"""
|
||||
Sets the transition that will be performed as part of the next
|
||||
interaction.
|
||||
"""
|
||||
|
||||
self.transition = transition
|
||||
|
||||
def interact(self, transient=None, 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.
|
||||
|
||||
@param trans_pause: If given, we must have a transition. Should we
|
||||
add a pause behavior during the transition?
|
||||
|
||||
@param suppress_overlay: This suppresses the display of the overlay.
|
||||
@param suppress_underlay: This suppresses the display of the underlay.
|
||||
"""
|
||||
|
||||
## Safety condition, prevents deadlocks.
|
||||
if trans_pause:
|
||||
if not self.transition:
|
||||
return None
|
||||
if self.supress_transition:
|
||||
return None
|
||||
|
||||
|
||||
## Expensive things we want to do before we pick the start_time.
|
||||
|
||||
# Tick time forward.
|
||||
@@ -495,22 +615,52 @@ class Interface(object):
|
||||
start_time = time.time()
|
||||
scene_lists = renpy.game.context().scene_lists
|
||||
|
||||
underlay = [ ( None, 0, i) for i in renpy.config.underlay ]
|
||||
overlay = [ ( None, 0, i ) for i in renpy.config.overlay ]
|
||||
# Compute the overlay, by calling the overlay functions.
|
||||
overlay = [ ]
|
||||
|
||||
if not suppress_overlay:
|
||||
for i in renpy.config.overlay_functions:
|
||||
for j in i():
|
||||
overlay.append((None, 0, j))
|
||||
|
||||
# Perhaps apply transition.
|
||||
master = scene_lists.master
|
||||
if transition:
|
||||
master = [ ( None, start_time,
|
||||
transition(scene_lists.old_master,
|
||||
scene_lists.master) ) ]
|
||||
if not suppress_underlay:
|
||||
underlay = [ ( None, 0, i) for i in renpy.config.underlay ]
|
||||
else:
|
||||
underlay = [ ]
|
||||
|
||||
transient = underlay + master + scene_lists.transient + scene_lists.overlay + overlay
|
||||
# Set up the transient scene list.
|
||||
if transient:
|
||||
transient = [ (None, start_time, i) for i in transient ]
|
||||
else:
|
||||
transient = scene_lists.transient
|
||||
|
||||
|
||||
# Figure out the display list.
|
||||
current_scene = scene_lists.master + transient + overlay
|
||||
|
||||
if self.transition and not self.supress_transition:
|
||||
trans = self.transition(self.old_scene, current_scene)
|
||||
|
||||
transition_scene = [ (None, start_time, trans) ]
|
||||
|
||||
if trans_pause:
|
||||
sb = renpy.display.behavior.SayBehavior(delay=trans.delay)
|
||||
transition_scene.append((None, start_time, sb))
|
||||
else:
|
||||
transition_scene = current_scene
|
||||
|
||||
self.transition = None
|
||||
self.supress_transition = False
|
||||
|
||||
# The list of things to be displayed.
|
||||
display_list = underlay + transition_scene
|
||||
|
||||
# This list of things recieving events.
|
||||
event_list = display_list[:]
|
||||
|
||||
# Compute the reversed list, which is in the right order
|
||||
# for handling events on.
|
||||
rev_transient = transient[:]
|
||||
rev_transient.reverse()
|
||||
event_list.reverse()
|
||||
|
||||
# Redraw the screen during every interaction.
|
||||
self.needs_redraw = True
|
||||
@@ -520,6 +670,10 @@ class Interface(object):
|
||||
# (The same order as rev_transient.)
|
||||
offsets = [ ]
|
||||
|
||||
# Post an event that moves us to the current mouse position.
|
||||
pygame.event.post(pygame.event.Event(MOUSEMOTION,
|
||||
pos=pygame.mouse.get_pos()))
|
||||
|
||||
rv = None
|
||||
|
||||
while rv is None:
|
||||
@@ -534,7 +688,7 @@ class Interface(object):
|
||||
draw_start = time.time()
|
||||
self.redraw_time = draw_start + 365.25 * 86400.0
|
||||
|
||||
offsets = self.display.show(transient)
|
||||
offsets = self.display.show(display_list)
|
||||
offsets.reverse()
|
||||
|
||||
# If profiling is enabled, report the profile time.
|
||||
@@ -543,6 +697,10 @@ class Interface(object):
|
||||
print "Profile: Redraw took %f seconds." % (new_time - draw_start)
|
||||
print "Profile: %f seconds between event and display." % (new_time - self.profile_time)
|
||||
|
||||
# Draw the mouse, if it needs drawing.
|
||||
if show_mouse:
|
||||
self.display.draw_mouse()
|
||||
|
||||
# Update the playing music, if necessary.
|
||||
renpy.music.restore()
|
||||
|
||||
@@ -575,7 +733,12 @@ class Interface(object):
|
||||
|
||||
# Handle quit specially for now.
|
||||
if ev.type == QUIT:
|
||||
raise renpy.game.QuitException()
|
||||
if renpy.game.script.has_label("_confirm_quit") and not self.quick_quit:
|
||||
self.quick_quit = True
|
||||
renpy.game.call_in_new_context("_confirm_quit")
|
||||
self.quick_quit = False
|
||||
else:
|
||||
raise renpy.game.QuitException()
|
||||
|
||||
# Merge mousemotion events.
|
||||
if ev.type == MOUSEMOTION:
|
||||
@@ -584,9 +747,11 @@ class Interface(object):
|
||||
if len(evs):
|
||||
ev = evs[-1]
|
||||
|
||||
x, y = getattr(ev, 'pos', (0, 0))
|
||||
# x, y = getattr(ev, 'pos', (0, 0))
|
||||
|
||||
for (k, t, d), (xo, yo) in zip(rev_transient, offsets):
|
||||
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)
|
||||
|
||||
if rv is not None:
|
||||
@@ -598,9 +763,17 @@ class Interface(object):
|
||||
if time.time() > self.redraw_time:
|
||||
self.needs_redraw = True
|
||||
|
||||
pygame.event.clear()
|
||||
|
||||
pygame.time.set_timer(KEYREPEATEVENT, 0)
|
||||
pygame.event.clear()
|
||||
scene_lists.replace_transient()
|
||||
scene_lists.replace_old_master()
|
||||
|
||||
# Clear up the transitions.
|
||||
self.old_scene = current_scene
|
||||
self.transition = None
|
||||
self.supress_transition = False
|
||||
|
||||
# Redraw the old scene, if any.
|
||||
self.redraw(0)
|
||||
|
||||
return rv
|
||||
|
||||
+125
-10
@@ -1,5 +1,6 @@
|
||||
import renpy
|
||||
import pygame
|
||||
from pygame.constants import *
|
||||
|
||||
class ImageCache(object):
|
||||
|
||||
@@ -35,7 +36,7 @@ class ImageCache(object):
|
||||
im = pygame.image.load(renpy.loader.load(fn), fn)
|
||||
im = im.convert_alpha()
|
||||
|
||||
iw, ih = im.get_size()
|
||||
# iw, ih = im.get_size()
|
||||
|
||||
# surf = renpy.display.surface.Surface(iw, ih)
|
||||
# surf.blit(im, (0, 0))
|
||||
@@ -133,18 +134,23 @@ class Image(renpy.display.core.Displayable):
|
||||
on disk.
|
||||
"""
|
||||
|
||||
def __init__(self, filename):
|
||||
def __init__(self, filename, style='image_placement', **properties):
|
||||
"""
|
||||
@param filename: The filename that the image is loaded from. Many common file formats are supported.
|
||||
"""
|
||||
|
||||
self.filename = filename
|
||||
self.style = renpy.style.Style(style, properties)
|
||||
|
||||
def render(self, w, h, st):
|
||||
return cache.load_image(self.filename)
|
||||
im = cache.load_image(self.filename)
|
||||
w, h = im.get_size()
|
||||
rv = renpy.display.surface.Surface(w, h)
|
||||
rv.blit(im, (0, 0))
|
||||
return rv
|
||||
|
||||
def get_placement(self):
|
||||
return renpy.game.style.image_placement
|
||||
return self.style
|
||||
|
||||
def predict(self, callback):
|
||||
callback(self.filename)
|
||||
@@ -154,14 +160,17 @@ class UncachedImage(renpy.display.core.Displayable):
|
||||
An image that is loaded immediately and not cached.
|
||||
"""
|
||||
|
||||
def __init__(self, file, hint=None, scale=None):
|
||||
def __init__(self, file, hint=None, scale=None, style='image_placement',
|
||||
**properties):
|
||||
self.surf = pygame.image.load(file, hint)
|
||||
|
||||
if scale:
|
||||
self.surf = pygame.transform.scale(self.surf, scale)
|
||||
|
||||
self.style = renpy.style.Style(style, properties)
|
||||
|
||||
def get_placement(self):
|
||||
return renpy.game.style.image_placement
|
||||
return self.style
|
||||
|
||||
def render(self, w, h, st):
|
||||
sw, sh = self.surf.get_size()
|
||||
@@ -194,10 +203,38 @@ class ImageReference(renpy.display.core.Displayable):
|
||||
def find_target(self):
|
||||
import renpy.exports as exports
|
||||
|
||||
if self.name in exports.images:
|
||||
self.target = exports.images[self.name]
|
||||
else:
|
||||
self.target = renpy.display.text.Text("Image %s not found." % repr(self.name), color=(255, 0, 0, 255))
|
||||
name = self.name
|
||||
parameters = [ ]
|
||||
|
||||
def error(msg):
|
||||
self.target = renpy.display.text.Text(msg,
|
||||
color=(255, 0, 0, 255))
|
||||
|
||||
if renpy.config.debug:
|
||||
raise Exception(msg)
|
||||
|
||||
|
||||
# Scan through, searching for an image (defined with an
|
||||
# input statement) that is a prefix of the given name.
|
||||
while name:
|
||||
if name in exports.images:
|
||||
target = exports.images[name]
|
||||
|
||||
try:
|
||||
self.target = target.parameterize(name, parameters)
|
||||
except Exception, e:
|
||||
if renpy.config.debug:
|
||||
raise
|
||||
|
||||
error(str(e))
|
||||
|
||||
return
|
||||
|
||||
else:
|
||||
parameters.insert(0, name[-1])
|
||||
name = name[:-1]
|
||||
|
||||
error("Image '%s' not found." % ' '.join(self.name))
|
||||
|
||||
|
||||
def render(self, width, height, st):
|
||||
@@ -208,8 +245,12 @@ class ImageReference(renpy.display.core.Displayable):
|
||||
return self.target.render(width, height, st)
|
||||
|
||||
def get_placement(self):
|
||||
if not hasattr(self, 'target'):
|
||||
self.find_target()
|
||||
|
||||
return self.target.get_placement()
|
||||
|
||||
|
||||
class Solid(renpy.display.core.Displayable):
|
||||
"""
|
||||
Returns a Displayable that is solid, and filled with a single
|
||||
@@ -403,3 +444,77 @@ class Animation(renpy.display.core.Displayable):
|
||||
def get_placement(self):
|
||||
return renpy.game.style.image_placement
|
||||
|
||||
class ImageMap(renpy.display.core.Displayable):
|
||||
"""
|
||||
The displayable that implements renpy.imagemap.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, ground, selected, hotspots,
|
||||
style='imagemap', **properties):
|
||||
|
||||
self.ground = ground
|
||||
self.selected = selected
|
||||
self.hotspots = hotspots
|
||||
self.active = None
|
||||
|
||||
self.style = renpy.style.Style(style, properties)
|
||||
|
||||
def get_placement(self):
|
||||
return self.style
|
||||
|
||||
def predict(self, callback):
|
||||
callback(i.ground)
|
||||
callback(i.selected)
|
||||
|
||||
def render(self, width, height, st):
|
||||
|
||||
ground = cache.load_image(self.ground)
|
||||
selected = cache.load_image(self.selected)
|
||||
|
||||
width, height = ground.get_size()
|
||||
rv = renpy.display.surface.Surface(width, height)
|
||||
rv.blit(ground, (0, 0))
|
||||
|
||||
if self.active is not None:
|
||||
x0, y0, x1, y1, result = self.hotspots[self.active]
|
||||
|
||||
subsurface = selected.subsurface((x0, y0, x1-x0, y1-y0))
|
||||
rv.blit(subsurface, (x0, y0))
|
||||
|
||||
return rv
|
||||
|
||||
def event(self, ev, x, y):
|
||||
|
||||
old_active = self.active
|
||||
active = None
|
||||
|
||||
for i, (x0, y0, x1, y1, result) in enumerate(self.hotspots):
|
||||
if x >= x0 and x <= x1 and y >= y0 and y <= y1:
|
||||
active = i
|
||||
break
|
||||
|
||||
# result stays set.
|
||||
|
||||
if old_active != active:
|
||||
self.active = active
|
||||
renpy.game.interface.redraw(0)
|
||||
|
||||
if active is not None:
|
||||
renpy.sound.play(self.style.hover_sound)
|
||||
|
||||
|
||||
if active is None:
|
||||
return None
|
||||
|
||||
if (ev.type == MOUSEBUTTONDOWN and ev.button == 1) or \
|
||||
(ev.type == KEYDOWN and ev.key == K_RETURN):
|
||||
|
||||
renpy.sound.play(self.style.activate_sound)
|
||||
|
||||
return result
|
||||
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -379,3 +379,137 @@ class Window(Container):
|
||||
self.window_size = width, height
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
class Pan(Container):
|
||||
"""
|
||||
This is used to pan over a child displayable, which is almost
|
||||
always an image. It works by interpolating the placement of the
|
||||
upper-left corner of the image, over time. It's only really
|
||||
suitable for use with images that are larger than the screen, as
|
||||
we don't do any cropping on the image.
|
||||
"""
|
||||
|
||||
def __init__(self, startpos, endpos, time, child,
|
||||
style='image_placement', **properties):
|
||||
"""
|
||||
@param child: The child displayable.
|
||||
|
||||
@param startpos: The initial coordinates of the upper-left
|
||||
corner of the screen, relative to the image.
|
||||
|
||||
@param endpos: The coordinates of the upper-left corner of the
|
||||
screen, relative to the image, after time has elapsed.
|
||||
|
||||
@param time: The time it takes to pan from startpos to endpos.
|
||||
"""
|
||||
|
||||
super(Pan, self).__init__()
|
||||
self.add(child)
|
||||
|
||||
self.startpos = startpos
|
||||
self.endpos = endpos
|
||||
self.time = time
|
||||
self.style = renpy.style.Style(style, properties)
|
||||
|
||||
def get_placement(self):
|
||||
return self.style
|
||||
|
||||
def render(self, width, height, st):
|
||||
|
||||
surf = self.child.render(width, height, st)
|
||||
self.sizes = [ surf.get_size() ]
|
||||
|
||||
x0, y0 = self.startpos
|
||||
x1, y1 = self.endpos
|
||||
|
||||
if self.time > 0:
|
||||
tfrac = (st / self.time)
|
||||
else:
|
||||
tfrac = 1.0
|
||||
|
||||
if tfrac > 1.0:
|
||||
tfrac = 1.0
|
||||
|
||||
xo = int(x0 * (1.0 - tfrac) + x1 * tfrac)
|
||||
yo = int(y0 * (1.0 - tfrac) + y1 * tfrac)
|
||||
|
||||
|
||||
self.offsets = [ (-xo, -yo) ]
|
||||
|
||||
rv = renpy.display.surface.Surface(width, height)
|
||||
|
||||
# print surf
|
||||
|
||||
subsurf = surf.subsurface((xo, yo, width, height))
|
||||
rv.blit(subsurf, (0, 0))
|
||||
|
||||
# rv.blit(surf, (-xo, -yo))
|
||||
|
||||
if st < self.time:
|
||||
renpy.game.interface.redraw(0)
|
||||
|
||||
return rv
|
||||
|
||||
class Move(Container):
|
||||
"""
|
||||
This moves a child relative to the thing containing it. This
|
||||
motion is done by manipulating the xpos and ypos properties in a
|
||||
placement style.
|
||||
"""
|
||||
|
||||
def __init__(self, startpos, endpos, time, child,
|
||||
style='default', **properties):
|
||||
|
||||
super(Move, self).__init__()
|
||||
self.add(child)
|
||||
|
||||
self.startpos = startpos
|
||||
self.endpos = endpos
|
||||
self.time = time
|
||||
|
||||
self.st = 0.0
|
||||
|
||||
self.style = renpy.style.Style(style, properties)
|
||||
|
||||
def get_placement(self):
|
||||
st = self.st
|
||||
|
||||
x0, y0 = self.startpos
|
||||
x1, y1 = self.endpos
|
||||
|
||||
if self.time > 0:
|
||||
tfrac = (st / self.time)
|
||||
else:
|
||||
tfrac = 1.0
|
||||
|
||||
if tfrac > 1.0:
|
||||
tfrac = 1.0
|
||||
|
||||
xo = x0 * (1.0 - tfrac) + x1 * tfrac
|
||||
yo = y0 * (1.0 - tfrac) + y1 * tfrac
|
||||
|
||||
if isinstance(x1, int):
|
||||
xo = int(xo)
|
||||
|
||||
if isinstance(y1, int):
|
||||
yo = int(yo)
|
||||
|
||||
self.style.xpos = xo
|
||||
self.style.ypos = yo
|
||||
|
||||
return self.style
|
||||
|
||||
def render(self, width, height, st):
|
||||
self.st = st
|
||||
rv = self.child.render(width, height, st)
|
||||
|
||||
self.sizes = [ rv.get_size() ]
|
||||
self.offsets = [ (0, 0) ]
|
||||
|
||||
if st < self.time:
|
||||
renpy.game.interface.redraw(0)
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
|
||||
@@ -100,3 +100,57 @@ class Surface(object):
|
||||
self.blit_to(rv, 0, 0)
|
||||
|
||||
return rv
|
||||
|
||||
def subsurface(self, (x, y, width, height)):
|
||||
"""
|
||||
Returns the subsurface of this surface, similar to
|
||||
pygame.Surface.subsurface
|
||||
"""
|
||||
|
||||
if x > self.width or y > self.height:
|
||||
return Surface(0, 0)
|
||||
|
||||
width = min(self.width - x, width)
|
||||
height = min(self.height - y, height)
|
||||
|
||||
rv = Surface(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()
|
||||
|
||||
if sw - ox <= 0:
|
||||
continue
|
||||
if sh - oy <= 0:
|
||||
continue
|
||||
|
||||
sw = min(sw - sx - ox, width)
|
||||
sh = min(sh - sy - oy, height)
|
||||
|
||||
rv.blit(source.subsurface((sx, sy, sw, sh)),
|
||||
(ox, oy))
|
||||
|
||||
return rv
|
||||
|
||||
+52
-29
@@ -1,4 +1,6 @@
|
||||
import pygame
|
||||
from pygame.constants import *
|
||||
|
||||
import renpy
|
||||
|
||||
_font_cache = { }
|
||||
@@ -37,10 +39,12 @@ class Text(renpy.display.core.Displayable):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, text, style='default', **properties):
|
||||
def __init__(self, text, slow=False, style='default', **properties):
|
||||
"""
|
||||
@param text: The text that will be displayed on the screen.
|
||||
|
||||
@param slow: If True, the text will be slowly typed onto the screen.
|
||||
|
||||
@param style: A style that will be applied to the text.
|
||||
|
||||
@param properties: Additional properties that are applied to the text.
|
||||
@@ -49,6 +53,7 @@ class Text(renpy.display.core.Displayable):
|
||||
|
||||
self.text = text
|
||||
self.style = renpy.style.Style(style, properties)
|
||||
self.slow = slow
|
||||
|
||||
def get_placement(self):
|
||||
return self.style
|
||||
@@ -61,12 +66,12 @@ class Text(renpy.display.core.Displayable):
|
||||
self.text = new_text
|
||||
self._update()
|
||||
|
||||
def set_style(self, style, **properties):
|
||||
def set_style(self, style):
|
||||
"""
|
||||
Changes the style assocated with this object.
|
||||
"""
|
||||
|
||||
self.style = renpy.style.Style(style, properties)
|
||||
self.style = style
|
||||
self._update()
|
||||
|
||||
def _update(self):
|
||||
@@ -143,13 +148,29 @@ class Text(renpy.display.core.Displayable):
|
||||
surf = renpy.display.surface.Surface(self.width + dsxo, self.height + dsyo)
|
||||
font = get_font(self.style.font, self.style.size)
|
||||
|
||||
lines = self.laidout.split('\n')
|
||||
laidout = self.laidout
|
||||
|
||||
# Annoying text hack.
|
||||
if self.slow and renpy.config.annoying_text_cps and not renpy.game.preferences.fast_text:
|
||||
chars = int(st * renpy.config.annoying_text_cps)
|
||||
if chars < len(laidout):
|
||||
laidout = laidout[:chars]
|
||||
renpy.game.interface.redraw(0)
|
||||
else:
|
||||
self.slow = False
|
||||
else:
|
||||
self.slow = False
|
||||
|
||||
|
||||
lines = laidout.split('\n')
|
||||
|
||||
# Common rendering code.
|
||||
def render_lines(x, y, color):
|
||||
for l in lines:
|
||||
ls = font.render(l, True, color)
|
||||
surf.blit(ls, (x, y + font.get_descent()))
|
||||
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
|
||||
@@ -162,37 +183,39 @@ class Text(renpy.display.core.Displayable):
|
||||
render_lines(0, 0 + fudge, self.style.color)
|
||||
|
||||
return surf
|
||||
|
||||
class ExpressionText(Text):
|
||||
"""
|
||||
This is a Displayable that displays the result of an expression on
|
||||
the screen, as text.
|
||||
"""
|
||||
|
||||
def __init__(self, expression, style='default', **properties):
|
||||
def event(self, ev, x, y):
|
||||
"""
|
||||
@param expression: An expression that is evaluated to get the
|
||||
text that will be shown on the screen.
|
||||
|
||||
@param style: A style that will be applied to the text.
|
||||
|
||||
@param properties: Additional properties that are applied to
|
||||
the text.
|
||||
Space, Enter, or Click ends slow, if it's enabled.
|
||||
"""
|
||||
|
||||
super(ExpressionText, self).__init__('', **properties)
|
||||
if not self.slow:
|
||||
return None
|
||||
|
||||
self.old_value = ''
|
||||
self.expression = expression
|
||||
if ( ev.type == MOUSEBUTTONDOWN and ev.button == 1) or \
|
||||
( ev.type == KEYDOWN and (ev.key == K_RETURN or ev.key == K_SPACE)):
|
||||
|
||||
def render(self, width, height, st):
|
||||
self.slow = False
|
||||
raise renpy.display.core.IgnoreEvent()
|
||||
|
||||
value = renpy.python.py_eval(self.expression)
|
||||
value = str(value)
|
||||
class ParameterizedText(object):
|
||||
"""
|
||||
This can be used as an image. When used, this image is expected to
|
||||
have a single parameter, a string which is rendered as the image.
|
||||
"""
|
||||
|
||||
if value != self.old_value:
|
||||
self.old_value = value
|
||||
self.set_text(value)
|
||||
def __init__(self, style='default', **properties):
|
||||
self.style = style
|
||||
self.properties = properties
|
||||
|
||||
return super(ExpressionText, self).render(width, height, st)
|
||||
def parameterize(self, name, parameters):
|
||||
|
||||
if len(parameters) != 1:
|
||||
raise Exception("'%s' takes a single string parameter." %
|
||||
' '.join(name))
|
||||
|
||||
param = parameters[0]
|
||||
string = renpy.python.py_eval(param)
|
||||
|
||||
return Text(string, style=self.style, **self.properties)
|
||||
|
||||
|
||||
@@ -10,11 +10,22 @@ class Transition(renpy.display.core.Displayable):
|
||||
"""
|
||||
|
||||
def __init__(self, delay):
|
||||
self.saybehavior = renpy.display.behavior.SayBehavior(delay=delay)
|
||||
|
||||
|
||||
self.delay = delay
|
||||
self.offsets = [ ]
|
||||
|
||||
def event(self, ev, x, y):
|
||||
return self.saybehavior.event(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
|
||||
|
||||
class Fade(Transition):
|
||||
"""
|
||||
@@ -46,6 +57,8 @@ class Fade(Transition):
|
||||
|
||||
rv = renpy.display.surface.Surface(width, height)
|
||||
|
||||
events = False
|
||||
|
||||
if st < self.out_time:
|
||||
scene_list = self.old_scene_list
|
||||
alpha = int(255 * (st / self.out_time))
|
||||
@@ -57,6 +70,7 @@ class Fade(Transition):
|
||||
else:
|
||||
scene_list = self.new_scene_list
|
||||
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,
|
||||
@@ -64,6 +78,9 @@ class Fade(Transition):
|
||||
height)
|
||||
rv.blit(surf, (0, 0))
|
||||
|
||||
if events:
|
||||
self.offsets = offsets
|
||||
|
||||
# Just to be sure.
|
||||
if alpha < 0:
|
||||
alpha = 0
|
||||
@@ -73,7 +90,8 @@ class Fade(Transition):
|
||||
|
||||
rv.fill(self.color[:3] + (alpha,))
|
||||
|
||||
renpy.game.interface.redraw(0)
|
||||
if st < self.in_time + self.hold_time + self.out_time:
|
||||
renpy.game.interface.redraw(0)
|
||||
|
||||
return rv
|
||||
|
||||
@@ -91,7 +109,7 @@ class Dissolve(Transition):
|
||||
rsl = renpy.display.core.render_scene_list
|
||||
|
||||
rv, offsets = rsl(self.old_scene_list, width, height)
|
||||
surftree, offsets = rsl(self.new_scene_list, width, height)
|
||||
surftree, self.offsets = rsl(self.new_scene_list, width, height)
|
||||
surf = surftree.pygame_surface(False)
|
||||
|
||||
alpha = min(255, int(255 * st / self.time))
|
||||
@@ -99,7 +117,8 @@ class Dissolve(Transition):
|
||||
surf.set_alpha(alpha)
|
||||
rv.blit(surf, (0, 0))
|
||||
|
||||
renpy.game.interface.redraw(0)
|
||||
if st < self.time:
|
||||
renpy.game.interface.redraw(0)
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
+104
-42
@@ -12,6 +12,7 @@ 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.loadsave import *
|
||||
|
||||
import time
|
||||
@@ -29,12 +30,8 @@ def checkpoint():
|
||||
|
||||
renpy.game.log.checkpoint()
|
||||
|
||||
def interact(*widgets):
|
||||
|
||||
for i in widgets:
|
||||
scene_list_add('transient', i)
|
||||
|
||||
return renpy.game.interface.interact()
|
||||
def interact(*widgets, **kwargs):
|
||||
return renpy.game.interface.interact(transient=widgets, **kwargs)
|
||||
|
||||
def scene_lists(index=-1):
|
||||
"""
|
||||
@@ -96,33 +93,21 @@ def scene():
|
||||
sls.clear('master')
|
||||
sls.clear('transient')
|
||||
|
||||
def set_overlay(overlay_list):
|
||||
"""
|
||||
Sets the overlay list that will be used to display things at the
|
||||
top of the screen. Clearing out the scene lists will not change
|
||||
what's displayed in the overlay.
|
||||
|
||||
@param overlay_list: A list of displayables that will be displayed
|
||||
above the current scene.
|
||||
"""
|
||||
|
||||
scene_lists().set_overlay(overlay_list)
|
||||
|
||||
def watch(expression):
|
||||
def watch(expression, style='default', **properties):
|
||||
"""
|
||||
This watches the given python expression, by displaying it in the
|
||||
upper-right corner of the screen. The expression should always be
|
||||
upper-right corner of the screen (although position properties
|
||||
can change that). The expression should always be
|
||||
defined, never throwing an exception.
|
||||
|
||||
This will replace any overlay defined by the program.
|
||||
A watch will not persist through a save or restart.
|
||||
"""
|
||||
|
||||
text = renpy.display.text.ExpressionText(expression)
|
||||
pos = renpy.display.layout.Position(text,
|
||||
xpos=1.0, xanchor='right',
|
||||
ypos=0.0, yanchor='top')
|
||||
def overlay_func():
|
||||
return [ renpy.display.text.Text(renpy.python.py_eval(expression),
|
||||
style=style, **properties) ]
|
||||
|
||||
set_overlay([ pos ])
|
||||
renpy.config.overlay_functions.append(overlay_func)
|
||||
|
||||
def input(prompt, default='', length=None):
|
||||
"""
|
||||
@@ -192,9 +177,7 @@ def display_menu(items, window_style='menu_window'):
|
||||
menu = Menu(items)
|
||||
win = Window(menu, style=window_style)
|
||||
|
||||
scene_list_add('transient', win)
|
||||
|
||||
rv = interact()
|
||||
rv = interact(win)
|
||||
checkpoint()
|
||||
|
||||
return rv
|
||||
@@ -232,39 +215,107 @@ def display_say(who, what, who_style='say_label',
|
||||
import renpy.display.text as text
|
||||
import renpy.display.behavior as behavior
|
||||
|
||||
if who is None:
|
||||
who = ""
|
||||
else:
|
||||
|
||||
if who is not None:
|
||||
who = who + ": "
|
||||
|
||||
vbox = layout.VBox(padding=10)
|
||||
|
||||
label = text.Text(who, style=who_style, **properties)
|
||||
vbox.add(label)
|
||||
if who is not None:
|
||||
label = text.Text(who, style=who_style, **properties)
|
||||
vbox.add(label)
|
||||
|
||||
line = text.Text(what, style=what_style)
|
||||
line = text.Text(what, style=what_style, slow=True)
|
||||
vbox.add(line)
|
||||
|
||||
window = layout.Window(vbox, style=window_style)
|
||||
saybehavior = behavior.SayBehavior()
|
||||
|
||||
scene_list_add('transient', window)
|
||||
scene_list_add('transient', saybehavior)
|
||||
|
||||
interact()
|
||||
interact(saybehavior, window)
|
||||
checkpoint()
|
||||
|
||||
def pause(delay=None):
|
||||
def imagemap(ground, selected, hotspots, overlays=False,
|
||||
style='imagemap', **properties):
|
||||
"""
|
||||
Displays an imagemap. An image map consists of two images and a
|
||||
list of hotspots that are defined on that image. When the user
|
||||
clicks on a hotspot, the value associated with that hotspot is
|
||||
returned.
|
||||
|
||||
@param ground: The name of the file containing the ground
|
||||
image. The ground image is displayed in hotspots that the mouse is
|
||||
not over, and for areas that are not part of any hotspots.
|
||||
|
||||
@param selected: The name of the file containing the selected
|
||||
image. This image is displayed in hotspots when the mouse is over
|
||||
them.
|
||||
|
||||
@param hotspots: A list of tuples defining the hotspots in this
|
||||
image map. Each tuple has the format (x0, y0, x1, y1, result).
|
||||
(x0, y0) gives the coordinates of the upper-left corner of the
|
||||
hotspot, (x1, y1) gives the lower-right corner, and result gives
|
||||
the value returned from this function if the mouse is clicked in
|
||||
the hotspot.
|
||||
|
||||
@param overlay: If True, overlays are displayed when this imagemap
|
||||
is active. If False, the overlays are suppressed.
|
||||
"""
|
||||
|
||||
imagemap = ImageMap(ground, selected, hotspots, style=style, **properties)
|
||||
keymouse = KeymouseBehavior()
|
||||
|
||||
rv = interact(keymouse, imagemap)
|
||||
checkpoint()
|
||||
return rv
|
||||
|
||||
|
||||
def pause(delay=None, music=None):
|
||||
"""
|
||||
When called, this pauses and waits for the user to click before
|
||||
advancing the script.
|
||||
|
||||
@param delay: The number of seconds to delay.
|
||||
|
||||
@param music: If supplied, this gives the number of seconds into
|
||||
the background music that we will delay until. If music is
|
||||
playing, this takes precedence, otherwise the delay parameter
|
||||
take precedence.
|
||||
|
||||
Returns True if the pause was interrupted by the user hitting a key
|
||||
or clicking a mouse, or False if the pause was ended by the appointed
|
||||
time being reached.
|
||||
"""
|
||||
|
||||
if music is not None:
|
||||
newdelay = renpy.music.music_delay(music)
|
||||
|
||||
if newdelay is not None:
|
||||
delay = newdelay
|
||||
|
||||
sayb = renpy.display.behavior.SayBehavior(delay=delay)
|
||||
scene_list_add('transient', sayb)
|
||||
|
||||
interact()
|
||||
|
||||
return interact()
|
||||
|
||||
def with(trans):
|
||||
"""
|
||||
Behaves identically to a with statement. The only reason to use this
|
||||
over a Ren'Py with statement is to get at the return code, which is
|
||||
True if the transition was interrupted, or False otherwise.
|
||||
"""
|
||||
|
||||
# Code basically copied from ast.With.execute.
|
||||
if not trans:
|
||||
renpy.game.interface.with_none()
|
||||
return False
|
||||
else:
|
||||
if renpy.game.preferences.transitions:
|
||||
renpy.game.interface.set_transition(trans)
|
||||
return renpy.game.interface.interact(show_mouse=False,
|
||||
trans_pause=True,
|
||||
suppress_overlay=True)
|
||||
else:
|
||||
return False
|
||||
|
||||
def rollback():
|
||||
"""
|
||||
@@ -346,6 +397,17 @@ def windows():
|
||||
import sys
|
||||
return hasattr(sys, 'winver')
|
||||
|
||||
def transition(trans):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
if trans is None:
|
||||
renpy.game.interface.with_none()
|
||||
else:
|
||||
renpy.game.interface.set_transition(trans)
|
||||
|
||||
call_in_new_context = renpy.game.call_in_new_context
|
||||
curried_call_in_new_context = renpy.curry.curry(renpy.game.call_in_new_context)
|
||||
|
||||
+38
-5
@@ -11,10 +11,13 @@
|
||||
|
||||
import renpy
|
||||
|
||||
# The basepath. Everything that is loaded is relative to this, at least
|
||||
# until we get around to implementing archive files.
|
||||
# The basepath.
|
||||
basepath = None
|
||||
|
||||
# A list of paths that we search to load things. This is searched for
|
||||
# everything that can be loaded, before archives are used.
|
||||
searchpath = [ ]
|
||||
|
||||
# A Script object, giving the script of the currently executing game.
|
||||
script = None
|
||||
|
||||
@@ -54,17 +57,47 @@ seen_session = { }
|
||||
# The set of statements we've ever seen.
|
||||
seen_ever = { }
|
||||
|
||||
# The class that's used to hold the persistent data.
|
||||
class Persistent(object):
|
||||
|
||||
def __setstate__(self, data):
|
||||
vars(self).update(data)
|
||||
|
||||
def __getstate__(self):
|
||||
return vars(self)
|
||||
|
||||
# Undefined attributes return None.
|
||||
def __getattr__(self, attr):
|
||||
return None
|
||||
|
||||
# The persistent data that's kept from session to session
|
||||
persistent = None
|
||||
|
||||
class Preferences(object):
|
||||
"""
|
||||
Stores preferences that will one day be persisted.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def reinit(self):
|
||||
self.fullscreen = False
|
||||
self.sound = True
|
||||
self.music = True
|
||||
self.skip_unseen = False
|
||||
self.fast_text = False
|
||||
|
||||
preferences = Preferences()
|
||||
# 2 - All transitions.
|
||||
# 1 - Only non-default transitions.
|
||||
# 0 - No transitions.
|
||||
self.transitions = 2
|
||||
|
||||
def __setstate__(self, state):
|
||||
self.reinit()
|
||||
vars(self).update(state)
|
||||
|
||||
def __init__(self):
|
||||
self.reinit()
|
||||
|
||||
# The current preferences.
|
||||
preferences = None
|
||||
|
||||
class RestartException(Exception):
|
||||
"""
|
||||
|
||||
+32
-13
@@ -14,12 +14,14 @@ def index_archives():
|
||||
archives = [ ]
|
||||
|
||||
for prefix in renpy.config.archives:
|
||||
fn = transfn(prefix + ".rpi")
|
||||
index = loads(file(fn, "rb").read().decode("zlib"))
|
||||
|
||||
print index.keys()
|
||||
|
||||
archives.append((prefix, index))
|
||||
try:
|
||||
fn = transfn(prefix + ".rpi")
|
||||
index = loads(file(fn, "rb").read().decode("zlib"))
|
||||
archives.append((prefix, index))
|
||||
except:
|
||||
if renpy.config.debug:
|
||||
raise
|
||||
|
||||
def load(name):
|
||||
"""
|
||||
@@ -27,20 +29,28 @@ def load(name):
|
||||
"""
|
||||
|
||||
# Look for the file directly.
|
||||
fn = transfn(name)
|
||||
if os.path.exists(fn):
|
||||
return file(fn, "rb")
|
||||
if not renpy.config.force_archives:
|
||||
|
||||
try:
|
||||
fn = transfn(name)
|
||||
return file(fn, "rb")
|
||||
except:
|
||||
pass
|
||||
|
||||
# Look for it in archive files.
|
||||
for prefix, index in archives:
|
||||
if not name in index:
|
||||
continue
|
||||
|
||||
offset, dlen = index[name]
|
||||
|
||||
f = file(transfn(prefix + ".rpa"), "rb")
|
||||
f.seek(offset)
|
||||
rv = StringIO(f.read(dlen))
|
||||
|
||||
data = [ ]
|
||||
|
||||
for offset, dlen in index[name]:
|
||||
f.seek(offset)
|
||||
data.append(f.read(dlen))
|
||||
|
||||
rv = StringIO(''.join(data))
|
||||
f.close()
|
||||
|
||||
return rv
|
||||
@@ -48,4 +58,13 @@ def load(name):
|
||||
raise Exception("Couldn't find file '%s'." % name)
|
||||
|
||||
def transfn(name):
|
||||
return renpy.game.basepath + "/" + name
|
||||
"""
|
||||
Tries to translate the name to a file that exists in one of the
|
||||
searched directories.
|
||||
"""
|
||||
|
||||
for d in renpy.game.searchpath:
|
||||
if os.path.exists(d + "/" + name):
|
||||
return d + "/" + name
|
||||
|
||||
raise Exception("Couldn't find file '%s'." % name)
|
||||
|
||||
+28
-15
@@ -12,7 +12,7 @@
|
||||
import renpy
|
||||
import renpy.game as game
|
||||
import os
|
||||
from cPickle import load, dumps, HIGHEST_PROTOCOL
|
||||
from cPickle import loads, dumps, HIGHEST_PROTOCOL
|
||||
|
||||
def run():
|
||||
"""
|
||||
@@ -20,6 +20,9 @@ def run():
|
||||
will cause this to change.
|
||||
"""
|
||||
|
||||
# Initialize the log.
|
||||
game.log = renpy.python.RollbackLog()
|
||||
|
||||
# Reload some things, in case this is a restart.
|
||||
renpy.store.reload()
|
||||
renpy.config.reload()
|
||||
@@ -32,31 +35,40 @@ def run():
|
||||
except:
|
||||
pass
|
||||
|
||||
# Unserialize the set of seen statements.
|
||||
# Unserialize the persistent data.
|
||||
try:
|
||||
game.seen_ever = load(file(renpy.config.savedir + "/seen"))
|
||||
f = file(renpy.config.savedir + "/persistent", "r")
|
||||
s = f.read().decode("zlib")
|
||||
f.close()
|
||||
game.persistent = loads(s)
|
||||
except:
|
||||
print "Couldn't load seen statements."
|
||||
game.seen_ever = { }
|
||||
|
||||
game.persistent = game.Persistent()
|
||||
|
||||
# Initialize the set of statements seen ever.
|
||||
if not game.persistent._seen_ever:
|
||||
game.persistent._seen_ever = { }
|
||||
|
||||
game.seen_ever = game.persistent._seen_ever
|
||||
|
||||
# Clear the list of seen statements in this game.
|
||||
game.seen_session = { }
|
||||
|
||||
# Initialize the preferences.
|
||||
if not game.persistent._preferences:
|
||||
game.persistent._preferences = game.Preferences()
|
||||
|
||||
game.preferences = game.persistent._preferences
|
||||
|
||||
# Initialize the store.
|
||||
renpy.store.store = renpy.store
|
||||
game.store = vars(renpy.store)
|
||||
|
||||
renpy.store.preferences = game.preferences
|
||||
renpy.store.persistent = game.persistent
|
||||
renpy.store._preferences = game.preferences
|
||||
|
||||
# Set up styles.
|
||||
game.style = renpy.style.StyleManager()
|
||||
renpy.store.style = game.style
|
||||
|
||||
# Initialize the log.
|
||||
game.log = renpy.python.RollbackLog()
|
||||
|
||||
# Run init code in its own context. (Don't log.)
|
||||
game.contexts = [ renpy.execution.Context(False) ]
|
||||
|
||||
@@ -107,16 +119,17 @@ def run():
|
||||
except game.QuitException, e:
|
||||
break
|
||||
finally:
|
||||
f = file(renpy.config.savedir + "/seen", "wb")
|
||||
f.write(dumps(game.seen_ever))
|
||||
f = file(renpy.config.savedir + "/persistent", "wb")
|
||||
f.write(dumps(game.persistent).encode("zlib"))
|
||||
f.close()
|
||||
|
||||
|
||||
# And, we're done.
|
||||
|
||||
def main(basepath_):
|
||||
def main(basepath):
|
||||
|
||||
game.basepath = basepath_
|
||||
game.basepath = basepath
|
||||
game.searchpath = [ "common", basepath ]
|
||||
|
||||
# Load the script.
|
||||
game.script = renpy.script.load_script(game.basepath)
|
||||
|
||||
+26
-9
@@ -6,6 +6,23 @@ import renpy
|
||||
# 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,
|
||||
@@ -53,17 +70,17 @@ def restore():
|
||||
if current_music == new_music:
|
||||
return
|
||||
|
||||
|
||||
current_music = new_music
|
||||
|
||||
# Usually, ignore errors.
|
||||
try:
|
||||
if not new_music:
|
||||
pygame.mixer.music.stop()
|
||||
try:
|
||||
if current_music != new_music and current_music:
|
||||
current_music = None
|
||||
pygame.mixer.music.fadeout(int(renpy.config.fade_music * 1000))
|
||||
else:
|
||||
fn, loops, startpos = new_music
|
||||
pygame.mixer.music.load(renpy.game.basepath + "/" + fn)
|
||||
pygame.mixer.music.play(loops, startpos)
|
||||
if not pygame.mixer.music.get_busy():
|
||||
fn, loops, startpos = new_music
|
||||
pygame.mixer.music.load(renpy.game.basepath + "/" + fn)
|
||||
pygame.mixer.music.play(loops, startpos)
|
||||
current_music = new_music
|
||||
|
||||
except pygame.error, e:
|
||||
if renpy.config.debug:
|
||||
|
||||
+64
-12
@@ -9,8 +9,16 @@ import renpy.ast as ast
|
||||
|
||||
class ParseError(Exception):
|
||||
|
||||
def __init__(self, filename, number, msg):
|
||||
Exception.__init__(self, "On line %d of %s: %s" % (number, filename, msg))
|
||||
def __init__(self, filename, number, msg, line=None, pos=None):
|
||||
message = "On line %d of %s: %s" % (number, filename, msg)
|
||||
|
||||
if line is not None:
|
||||
message += "\n\n" + line
|
||||
|
||||
if pos is not None:
|
||||
message += "\n" + " " * pos + "^"
|
||||
|
||||
Exception.__init__(self, message)
|
||||
|
||||
|
||||
def list_logical_lines(filename):
|
||||
@@ -209,7 +217,20 @@ class Lexer(object):
|
||||
# there is a huge chance of confusion.
|
||||
keywords = [
|
||||
'at',
|
||||
'call',
|
||||
'hide',
|
||||
'if',
|
||||
'image',
|
||||
'init',
|
||||
'jump',
|
||||
'menu',
|
||||
'python',
|
||||
'return',
|
||||
'scene',
|
||||
'set',
|
||||
'show',
|
||||
'with',
|
||||
'while',
|
||||
]
|
||||
|
||||
|
||||
@@ -302,7 +323,7 @@ class Lexer(object):
|
||||
location.
|
||||
"""
|
||||
|
||||
raise ParseError(self.filename, self.number, msg)
|
||||
raise ParseError(self.filename, self.number, msg, self.text, self.pos)
|
||||
|
||||
def eol(self):
|
||||
"""
|
||||
@@ -429,6 +450,15 @@ class Lexer(object):
|
||||
|
||||
c = self.text[self.pos]
|
||||
|
||||
# Allow unicode strings.
|
||||
if c == 'u':
|
||||
self.pos += 1
|
||||
|
||||
if self.eol():
|
||||
return False
|
||||
|
||||
c = self.text[self.pos]
|
||||
|
||||
if c not in ('"', "'"):
|
||||
return False
|
||||
|
||||
@@ -555,16 +585,20 @@ class Lexer(object):
|
||||
|
||||
def simple_expression(self):
|
||||
"""
|
||||
Tries to parse a simple_expression. Returns true if it can, or
|
||||
false if it cannot.
|
||||
Tries to parse a simple_expression. Returns the text if it can, or
|
||||
None if it cannot.
|
||||
"""
|
||||
|
||||
self.skip_whitespace()
|
||||
if self.eol():
|
||||
return None
|
||||
|
||||
start = self.pos
|
||||
|
||||
# We start with either a name, a python_string, or parenthesized
|
||||
# python
|
||||
if (not self.name() and
|
||||
not self.python_string() and
|
||||
if (not self.python_string() and
|
||||
not self.name() and
|
||||
not self.parenthesised_python()):
|
||||
|
||||
return None
|
||||
@@ -677,11 +711,11 @@ def parse_image_name(l):
|
||||
rv = [ l.require(l.name) ]
|
||||
|
||||
while True:
|
||||
n = l.name()
|
||||
n = l.simple_expression()
|
||||
if not n:
|
||||
break
|
||||
|
||||
rv.append(n)
|
||||
rv.append(n.strip())
|
||||
|
||||
return tuple(rv)
|
||||
|
||||
@@ -752,6 +786,7 @@ def parse_with(l, node):
|
||||
|
||||
def parse_menu(l, loc):
|
||||
|
||||
with = None
|
||||
set = None
|
||||
|
||||
# Tuples of (label, condition, block)
|
||||
@@ -761,6 +796,12 @@ def parse_menu(l, loc):
|
||||
|
||||
while not l.eob:
|
||||
|
||||
if l.keyword('with'):
|
||||
with = l.require(l.simple_expression)
|
||||
l.expect_eol()
|
||||
l.expect_noblock('with clause')
|
||||
l.advance()
|
||||
|
||||
if l.keyword('set'):
|
||||
set = l.require(l.simple_expression)
|
||||
l.expect_eol()
|
||||
@@ -798,7 +839,8 @@ def parse_menu(l, loc):
|
||||
items.append((label, condition, block))
|
||||
l.advance()
|
||||
|
||||
return ast.Menu(loc, items, set)
|
||||
return ast.Menu(loc, items, set, with)
|
||||
|
||||
|
||||
def parse_statement(l):
|
||||
"""
|
||||
@@ -1059,11 +1101,16 @@ def parse_statement(l):
|
||||
state = l.checkpoint()
|
||||
what = l.string()
|
||||
|
||||
if l.keyword('with'):
|
||||
with = l.require(l.simple_expression)
|
||||
else:
|
||||
with = None
|
||||
|
||||
if what and l.eol():
|
||||
# We have a one-argument say statement.
|
||||
l.expect_noblock('say statement')
|
||||
l.advance()
|
||||
return ast.Say(loc, None, what)
|
||||
return ast.Say(loc, None, what, with)
|
||||
|
||||
l.revert(state)
|
||||
|
||||
@@ -1071,11 +1118,16 @@ def parse_statement(l):
|
||||
who = l.simple_expression()
|
||||
what = l.string()
|
||||
|
||||
if l.keyword('with'):
|
||||
with = l.require(l.simple_expression)
|
||||
else:
|
||||
with = None
|
||||
|
||||
if who and what is not None:
|
||||
l.expect_eol()
|
||||
l.expect_noblock('say statement')
|
||||
l.advance()
|
||||
return ast.Say(loc, who, what)
|
||||
return ast.Say(loc, who, what, with)
|
||||
|
||||
l.error('expected statement.')
|
||||
|
||||
|
||||
+24
-8
@@ -318,14 +318,6 @@ class RollbackLog(renpy.object.Object):
|
||||
@ivar current: The current rollback object. (Equivalent to
|
||||
log[-1])
|
||||
|
||||
Not serialized:
|
||||
|
||||
@ivar old_store: A copy of the store as it was when begin was
|
||||
last called.
|
||||
|
||||
@ivar mutated: A dictionary that maps object ids to a tuple of
|
||||
(weakref to object, information needed to rollback that object)
|
||||
|
||||
@ivar ever_been_changed: A dictionary containing a key for each
|
||||
variable in the store that has ever been changed. (These variables
|
||||
become the roots of what is changed or rolled-back.)
|
||||
@@ -333,6 +325,16 @@ class RollbackLog(renpy.object.Object):
|
||||
@ivar frozen_roots: A frozen copy of the roots. When freeze is called,
|
||||
this holds a copy of roots. It's None at other times.
|
||||
|
||||
@ivar rollback_limit: The number of steps left that we can
|
||||
interactively rollback.
|
||||
|
||||
Not serialized:
|
||||
|
||||
@ivar old_store: A copy of the store as it was when begin was
|
||||
last called.
|
||||
|
||||
@ivar mutated: A dictionary that maps object ids to a tuple of
|
||||
(weakref to object, information needed to rollback that object)
|
||||
"""
|
||||
|
||||
nosave = [ 'old_store', 'mutated' ]
|
||||
@@ -343,6 +345,7 @@ class RollbackLog(renpy.object.Object):
|
||||
self.mutated = { }
|
||||
self.ever_been_changed = { }
|
||||
self.frozen_roots = None
|
||||
self.rollback_limit = 0
|
||||
|
||||
def after_setstate(self):
|
||||
self.mutated = { }
|
||||
@@ -453,6 +456,9 @@ class RollbackLog(renpy.object.Object):
|
||||
that the user may want to rollback to just before this
|
||||
node.
|
||||
"""
|
||||
|
||||
if self.rollback_limit < renpy.config.hard_rollback_limit:
|
||||
self.rollback_limit += 1
|
||||
|
||||
self.current.checkpoint = True
|
||||
|
||||
@@ -469,6 +475,13 @@ class RollbackLog(renpy.object.Object):
|
||||
effect.
|
||||
"""
|
||||
|
||||
# If we have exceeded the rollback limit, and don't have force,
|
||||
# give up.
|
||||
if not self.rollback_limit > 0 and not force:
|
||||
return
|
||||
|
||||
self.rollback_limit -= 1
|
||||
|
||||
self.purge_unreachable(self.get_roots())
|
||||
|
||||
revlog = [ ]
|
||||
@@ -499,6 +512,9 @@ class RollbackLog(renpy.object.Object):
|
||||
for rb in revlog:
|
||||
rb.rollback()
|
||||
|
||||
# Disable the next transition, as it's pointless.
|
||||
renpy.game.interface.supress_transition = True
|
||||
|
||||
# Restart the game with the new state.
|
||||
raise renpy.game.RestartException()
|
||||
|
||||
|
||||
+32
-8
@@ -8,6 +8,9 @@ import os
|
||||
|
||||
from cPickle import loads, dumps
|
||||
|
||||
# The version of the dumped script.
|
||||
script_version = 2
|
||||
|
||||
class ScriptError(Exception):
|
||||
"""
|
||||
Exception that is raised if the script is somehow inconsistent,
|
||||
@@ -41,8 +44,13 @@ class Script(object):
|
||||
self.namemap = { }
|
||||
self.initcode = [ ]
|
||||
|
||||
# Find the script files to load.
|
||||
dirlist = list(os.listdir(dir))
|
||||
|
||||
# A list of all files in the search directories.
|
||||
dirlist = [ ]
|
||||
|
||||
for dirname in renpy.game.searchpath:
|
||||
for fn in os.listdir(dirname):
|
||||
dirlist.append(dirname + "/" + fn)
|
||||
|
||||
# Files to ensure (because they are the alts of files that
|
||||
# have been processed.)
|
||||
@@ -53,8 +61,6 @@ class Script(object):
|
||||
if not (fn.endswith('.rpyc') or fn.endswith('.rpy')):
|
||||
continue
|
||||
|
||||
fn = dir + '/' + fn
|
||||
|
||||
if fn in ignore:
|
||||
continue
|
||||
|
||||
@@ -74,7 +80,15 @@ class Script(object):
|
||||
|
||||
# print "Loading", fn
|
||||
|
||||
self.load_file(fn)
|
||||
if self.load_file(fn):
|
||||
continue
|
||||
|
||||
print "Couldn't load %s, trying %s instead." % (fn, alt)
|
||||
|
||||
if self.load_file(alt):
|
||||
continue
|
||||
|
||||
raise Exception("Could not load %s or %s." % (fn, alt))
|
||||
|
||||
|
||||
self.initcode.sort()
|
||||
@@ -86,14 +100,22 @@ class Script(object):
|
||||
if fn.endswith(".rpy"):
|
||||
stmts = renpy.parser.parse(fn)
|
||||
f = file(fn + "c", "wb")
|
||||
f.write(dumps(stmts).encode('zlib'))
|
||||
f.write(dumps((script_version, stmts)).encode('zlib'))
|
||||
f.close()
|
||||
elif fn.endswith(".rpyc"):
|
||||
f = file(fn, "rb")
|
||||
stmts = loads(f.read().decode('zlib'))
|
||||
|
||||
try:
|
||||
version, stmts = loads(f.read().decode('zlib'))
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
if version != script_version:
|
||||
return False
|
||||
|
||||
f.close()
|
||||
else:
|
||||
assert False
|
||||
return False
|
||||
|
||||
# All of the statements found in file, regardless of nesting
|
||||
# depth.
|
||||
@@ -132,6 +154,8 @@ class Script(object):
|
||||
if init:
|
||||
self.initcode.append(init)
|
||||
|
||||
return True
|
||||
|
||||
def lookup(self, label):
|
||||
"""
|
||||
Looks up the given label in the game. If the label is not found,
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# Plays sounds.
|
||||
|
||||
import renpy
|
||||
|
||||
import pygame
|
||||
from pygame.constants import *
|
||||
|
||||
def init():
|
||||
pygame.mixer.init(renpy.config.sound_sample_rate)
|
||||
|
||||
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
|
||||
|
||||
try:
|
||||
sound = pygame.mixer.Sound(renpy.loader.load(fn))
|
||||
sound.play()
|
||||
except:
|
||||
if renpy.config.debug:
|
||||
raise
|
||||
|
||||
+7
-3
@@ -21,7 +21,8 @@ Solid = renpy.display.image.Solid
|
||||
Frame = renpy.display.image.Frame
|
||||
Animation = renpy.display.image.Animation
|
||||
Position = renpy.curry.curry(renpy.display.layout.Position)
|
||||
# Resize = renpy.curry.curry(renpy.display.layout.Resize)
|
||||
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)
|
||||
@@ -54,7 +55,7 @@ class Character(object):
|
||||
def __init__(self, name,
|
||||
who_style='say_label',
|
||||
what_style='say_dialogue',
|
||||
window_style='window_say',
|
||||
window_style='say_window',
|
||||
**properties):
|
||||
"""
|
||||
@param name: The name of the character, as shown to the user.
|
||||
@@ -83,12 +84,15 @@ class Character(object):
|
||||
renpy.display_say(self.name, what,
|
||||
who_style=self.who_style,
|
||||
what_style=self.what_style,
|
||||
window_style=self.window_style,
|
||||
**self.properties)
|
||||
|
||||
|
||||
# Conveniently get rid of all the packages we had imported before.
|
||||
import renpy.exports as renpy
|
||||
|
||||
# The default transition.
|
||||
default_transition = None
|
||||
|
||||
_globals = globals().copy()
|
||||
|
||||
def reload():
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import renpy
|
||||
|
||||
|
||||
# The version of Ren'Py in use.
|
||||
version = 'Renpy 4.0'
|
||||
version = 'Renpy 4.2'
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
# This file consists of renpy functions that aren't expected to be
|
||||
# touched by the user too much. We reserve the _ prefix for names
|
||||
# defined in the library.
|
||||
|
||||
# It's strongly reccomended that you don't edit this file, as future
|
||||
# releases of Ren'Py will probably change this file to include more
|
||||
# functionality.
|
||||
|
||||
# It's also strongly recommended that you leave this file in the
|
||||
# game directory, so its functionality is included in your game.
|
||||
|
||||
label _enable_overlay:
|
||||
|
||||
# Set up the default keymap.
|
||||
python hide:
|
||||
config.underlay = [ ]
|
||||
config.overlay = [ ]
|
||||
|
||||
# The default keymap.
|
||||
km = renpy.Keymap(
|
||||
K_PAGEUP = renpy.rollback,
|
||||
f = renpy.toggle_fullscreen,
|
||||
)
|
||||
|
||||
config.underlay.append(km)
|
||||
|
||||
|
||||
# If the user gives an enable_overlay function, call that.
|
||||
if renpy.has_label('enable_overlay'):
|
||||
call enable_overlay
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# This is the true starting point of the program. Sssh... Don't
|
||||
# tell anyone.
|
||||
label _start:
|
||||
|
||||
call _enable_overlay
|
||||
jump start
|
||||
@@ -2,14 +2,24 @@ init:
|
||||
# Set up the size of the screen.
|
||||
$ config.screen_width = 800
|
||||
$ config.screen_height = 600
|
||||
$ config.mouse = "mouse.png"
|
||||
$ config.background = (0, 0, 0, 255)
|
||||
|
||||
# Positions of things on the screen.
|
||||
$ left = Position(xpos=0.0, xanchor='left')
|
||||
$ right = Position(xpos=1.0, xanchor='right')
|
||||
$ center = Position()
|
||||
|
||||
# Styles
|
||||
$ style.window.background = Solid((0, 0, 128, 128))
|
||||
|
||||
# Transition
|
||||
$ fade = Fade(0.5, 0, 0.5)
|
||||
$ dissolve = Dissolve(0.5)
|
||||
|
||||
# Backgrounds.
|
||||
image whitehouse = Image("whitehouse.jpg")
|
||||
image marspan = Image("marspan.jpg")
|
||||
|
||||
# Character pictures.
|
||||
image eileen happy = Image("9a_happy.png")
|
||||
@@ -140,6 +150,45 @@ label after_sub:
|
||||
|
||||
show eileen happy at center
|
||||
|
||||
e "Testing transitions. If transitions are None, then nothing will
|
||||
happen."
|
||||
|
||||
e "First, normal with statements. This box will fade out, and the
|
||||
next will pop in."
|
||||
|
||||
with fade
|
||||
|
||||
e "This will pop out, a fade will happen, and the next will pop
|
||||
in."
|
||||
|
||||
with None
|
||||
with fade
|
||||
|
||||
e "Testing default transitions. The next three things will
|
||||
dissolve in. If transitions is Some, nothing will
|
||||
happen."
|
||||
|
||||
$ default_transition = dissolve
|
||||
|
||||
e "One"
|
||||
e "Two"
|
||||
e "Three"
|
||||
|
||||
$ default_transition = None
|
||||
|
||||
|
||||
scene marspan at Pan((0,0), (1600,0), 15.0)
|
||||
show eileen happy at center
|
||||
|
||||
e "Testing pan."
|
||||
|
||||
e "Still testing pan."
|
||||
|
||||
e "Wait for it to stop, and make sure it does."
|
||||
|
||||
scene marspan at Pan((0,0), (1600,0), 0)
|
||||
show eileen happy at center
|
||||
e "This should be a static pan."
|
||||
|
||||
init:
|
||||
python:
|
||||
|
||||
Reference in New Issue
Block a user