Compare commits
91 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ff139808ff | |||
| 07136f9be8 | |||
| 35fc2295e9 | |||
| 2b262ccca3 | |||
| d42aab3dff | |||
| 612c347498 | |||
| 3c4540131a | |||
| 43317f6e02 | |||
| 523742d8de | |||
| 9516ee910e | |||
| 3ea31a3862 | |||
| 649411a4ef | |||
| a0e60e5453 | |||
| e3f1f40a78 | |||
| d0177f4c52 | |||
| 84a3a55a03 | |||
| 048075b47a | |||
| fab097c92a | |||
| 9d26bd8c04 | |||
| 664d243c23 | |||
| 9a0906655d | |||
| 13d60f2741 | |||
| 7d4de64d0b | |||
| 9ada2d9884 | |||
| a5f1951cfb | |||
| 98850e4c5c | |||
| 709e1f925d | |||
| b11ae331a5 | |||
| 3ba4b26b99 | |||
| b864077017 | |||
| d411dff1c0 | |||
| 7fc7453450 | |||
| 1799971885 | |||
| b999911292 | |||
| 29e0f9ad21 | |||
| d821b7eb8b | |||
| 272a214304 | |||
| 7f15b351ee | |||
| 5a6a12a14a | |||
| 01493c7063 | |||
| 244055fcb7 | |||
| 0415af5f31 | |||
| f73372c924 | |||
| 65cdc24e37 | |||
| 1b2d439651 | |||
| b383942c69 | |||
| 73bd109abb | |||
| 33ffd5dedb | |||
| 7422fe43b8 | |||
| 879c45a5b3 | |||
| db0d205374 | |||
| f20594a2b5 | |||
| ee017f4bc6 | |||
| 4d089f9a0e | |||
| e7f9c5c2ac | |||
| 2df8d38c2f | |||
| 17ac44d610 | |||
| 3c52c2ae3f | |||
| 5a5115e29f | |||
| 5c7ff1c97b | |||
| cc820a3595 | |||
| 48fff48d28 | |||
| 1684243e57 | |||
| 45e14781eb | |||
| 5ceea7b4a5 | |||
| 17e716b4c3 | |||
| adfdbf3246 | |||
| 03b757d60e | |||
| 12c6d77990 | |||
| 8a7373f412 | |||
| df28ff5bdc | |||
| 2afc2beb5d | |||
| 3896bf3aad | |||
| 744ace5ab6 | |||
| 4bf0b74b6c | |||
| 5c6e624ff7 | |||
| 6c0d5a7a10 | |||
| b34cb21f0c | |||
| b77eb7ed0b | |||
| 34193c510e | |||
| 55f1eaf5da | |||
| 9b74b5a367 | |||
| 008922edc6 | |||
| 82edca785f | |||
| b271baecd5 | |||
| 9b48cd2a35 | |||
| 183f6ace88 | |||
| b5987cd6ea | |||
| 6e153a5070 | |||
| 56e59d91dd | |||
| fd91c6811f |
@@ -0,0 +1,31 @@
|
||||
build
|
||||
dist
|
||||
dists
|
||||
renpy.app
|
||||
**/*.rpyc
|
||||
**/*.rpyb
|
||||
**/*.rpymc
|
||||
_renpy.c
|
||||
nativemidi.c
|
||||
sound.c
|
||||
winmixer.c
|
||||
jedit
|
||||
lint.txt
|
||||
renpy.code
|
||||
traceback.txt
|
||||
doc/common.css
|
||||
doc/images
|
||||
doc/index.html
|
||||
doc/monobook.css
|
||||
doc/monobook2.css
|
||||
doc/reference
|
||||
doc/shared.css
|
||||
doc/tutorials
|
||||
doc/www.renpy.org
|
||||
testing
|
||||
translations.rpy
|
||||
launchertrans.rpy
|
||||
jcc
|
||||
scite/scite.zip
|
||||
screenshot*
|
||||
.ropeproject
|
||||
@@ -1,3 +1,293 @@
|
||||
New in Ren'Py 6.9.0
|
||||
-------------------
|
||||
|
||||
This release features a rewritten rendering system. The rendering
|
||||
subsystem is what manages drawing to the screen. There have been
|
||||
several major improvements:
|
||||
|
||||
* The render system has been simplified. Although still complex, it's
|
||||
now easier to understand than the old system, while also being
|
||||
naturally a bit faster.
|
||||
|
||||
* That being said, the render system now focuses more on framerate
|
||||
consistency than raw speed.
|
||||
|
||||
* The render system now supports subpixel drawing, drawing images at
|
||||
locations that are not integer pixel boundaries. This is enabled by
|
||||
setting the subpixel style property to True for a displayable. (It
|
||||
makes the most sense to do this for Move and Pan, and less sense to
|
||||
do this for non-moving displayables.)
|
||||
|
||||
* The render system supports specifying subpixel placement of
|
||||
displayables. This is done using the new absolute type. (A
|
||||
floating-point position is still interpreted as a fraction of the
|
||||
area in which the displayable is placed.)
|
||||
|
||||
* The render system supports a rotate-zoom-alpha mode, in which allows
|
||||
a displayable to be rotated, zoomed, and have its alpha
|
||||
changed. When this mode is used, the cost remains constant, even
|
||||
when multiple transformations are composed. Currently, this is only
|
||||
used by Transform and ui.transform.
|
||||
|
||||
* The render system supports rotating and scaling focusable objects.
|
||||
|
||||
Improved the transform routine used by RotoZoom and Transform. It's
|
||||
now both more accurate and faster. On Intel computers, it is now
|
||||
accelerated using MMX instructions.
|
||||
|
||||
|
||||
The image statement now implicitly places itself inside an init block
|
||||
if it's not inside an init block to begin with. The code:
|
||||
|
||||
image bg world = "world.jpg"
|
||||
|
||||
is now equivalent to:
|
||||
|
||||
init 990:
|
||||
image bg world = "world.jpg"
|
||||
|
||||
|
||||
When compiling python blocks containing strings with a unicode
|
||||
character in them, Ren'Py will automatically prepend a u to any string
|
||||
that's missing it. This should make life easier for users of non-ASCII
|
||||
languages.
|
||||
|
||||
|
||||
The new ui.autobar function makes it easy to make a bar that
|
||||
varies between two values over time.
|
||||
|
||||
|
||||
The new renpy.Container class can be subclassed to create a user-defined
|
||||
displayable thatr contains other displayables, like Transforms.
|
||||
|
||||
|
||||
The new im.FactorScale image manipulator allows images to be scaled by
|
||||
a specified factor at preload time. This is generally faster than doing the
|
||||
equivalent scaling at display time with FactorZoom.
|
||||
|
||||
|
||||
The new renpy.sound.set_pan function allows sound and music to be
|
||||
smoothly panned over time.
|
||||
|
||||
|
||||
The new config.label_callback variable allows a function to be called
|
||||
whenever control reaches a label. It can distinguish between jumps to
|
||||
that label and control falling through.
|
||||
|
||||
|
||||
There have been a couple of places where properly documented functions
|
||||
have superseded semi-documented calls into the Ren'Py internals:
|
||||
|
||||
The renpy.game_menu and ui.gamemenus functions are now the preferred ways
|
||||
of getting access to the game menu.
|
||||
|
||||
The ui.jumps function now takes a transition argument, allowing it to subsume
|
||||
the undocumented _intra_jumps.
|
||||
|
||||
|
||||
Ren'Py now looks for the file environment.txt in the directory
|
||||
containing Ren'Py itself. If it finds it, it loads the file as a
|
||||
python file, and uses it to set the default values of environment
|
||||
variables. This allows things like the editor or the scale factor to
|
||||
be set even on platforms that don't make it easy to change environment
|
||||
variables globally.
|
||||
|
||||
|
||||
The reporting of parse errors involving missing quotes has been
|
||||
improved.
|
||||
|
||||
|
||||
Changed the behavior of overlays during transitions. Now, both the old
|
||||
and new screens will use the new overlay. Previously, the overlay was
|
||||
cleared out during a transition, which didn't work well, as (for
|
||||
example) a dissolve would cause the overlay to blink out and then
|
||||
dissolve back in again.
|
||||
|
||||
|
||||
Calling theme.roundrect() no longer implies layout.button_menu().
|
||||
|
||||
|
||||
Ren'Py now autosaves when entering the quit prompt, and will not actually
|
||||
quit until this autosave is complete.
|
||||
|
||||
|
||||
Fixed a divide by zero error that occured when Alpha was used for 0
|
||||
seconds.
|
||||
|
||||
|
||||
The sound libraries have been upgraded:
|
||||
|
||||
* libvorbis is now at 1.2.0
|
||||
* SDL_sound is now a development version, which includes the mpg123
|
||||
decoder. According to the release announcment: ". Unlike mpglib, this
|
||||
decoder seems to play mp3s that were recorded after 1999, too. :-)"
|
||||
|
||||
|
||||
To reduce distribution size, OGG Speex support was dropped.
|
||||
|
||||
|
||||
New in Ren'Py 6.8.1
|
||||
-------------------
|
||||
|
||||
Added support for the iRex iLiad ebook reader platform. This required
|
||||
adding the following features, which may prove useful for other
|
||||
platforms:
|
||||
|
||||
Added support for the RENPY_SCALE_WIDTH environment variable, which
|
||||
allows the game to be scaled to a specific width, if it's larger.
|
||||
|
||||
Added support for the RENPY_LESS_MEMORY environment variable, which
|
||||
reduces the amount of memory used by Ren'Py in exchanged for reduced
|
||||
speed.
|
||||
|
||||
Added support for the RENPY_LESS_UPDATES environment variable, which
|
||||
tries to reduce the number of screen updates to one per interaction.
|
||||
|
||||
Added support for the RENPY_LESS_MOUSE environment variable, which
|
||||
prevents the mouse from being shown.
|
||||
|
||||
The iLiad support was sponsored by Hixbooks.
|
||||
|
||||
|
||||
It is now possible to rebuild the distribution of a game by unzipping
|
||||
it underneath the Ren'Py directory, selecting it as a project, and
|
||||
building the distributions. This makes it easy to upgrade the version
|
||||
of Ren'Py used by a game, or to build a version of it that works on
|
||||
your favorite platform.
|
||||
|
||||
|
||||
The new Alpha function can be used to change the alpha level of a
|
||||
displayable. (im.Alpha can only be used with other image
|
||||
manipulators.)
|
||||
|
||||
|
||||
User-defined statements are now documented in the reference manual,
|
||||
making them officially supported.
|
||||
|
||||
A user-defined statement can now be used as the default
|
||||
statement. This allows you to replace the say statement with an
|
||||
alternate of your own devising.
|
||||
|
||||
|
||||
Fixed a bug that occured when space characters were inserted
|
||||
immediately after a newline.
|
||||
|
||||
Fixed a problem displaying a presplash image on the mac platform.
|
||||
|
||||
|
||||
New in Ren'Py 6.8.0
|
||||
-------------------
|
||||
|
||||
Ren'Py now includes its own font-handling library, derived from
|
||||
pygame.font/SDL_ttf, which it uses in place of them when
|
||||
possible. This gives two advantages:
|
||||
|
||||
* Outlined text is now possible. The new outlines style property
|
||||
allows you to specify one or more outlines, including offset
|
||||
ones. This allows for several fairly complicated text effects, similar
|
||||
to what you'd see in modern subtitles.
|
||||
|
||||
* The bug preventing fonts from being archived has probably been
|
||||
fixed. It should now be possible to include a font in an archive, and
|
||||
have that work. The archiver has been modified to allow ttf files to
|
||||
be archived again. Please report any bugs/crashes that occur.
|
||||
|
||||
|
||||
There is now a new justify style property, that causes lines of text
|
||||
to have additional whitespace inserted between words so that the left
|
||||
and right margins are even, except on the last line of a paragraph.
|
||||
|
||||
|
||||
Ren'Py now supports the MudgeFont and BMFont image-based font file
|
||||
formats. The new renpy.register_mudgefont function is used to register
|
||||
a MudgeFont, and the renpy.register_bmfon function is used to register
|
||||
a BMFont.
|
||||
|
||||
|
||||
Newly-created games will have a Help option on the main and game
|
||||
menus. This is controlled by the config.help variable. By default
|
||||
the help system shows the README.html file in the game's base directory.
|
||||
|
||||
|
||||
If the file icon.ico is present in the base directory, it will be used
|
||||
as the icon of the windows exe when building the windows
|
||||
distribution. Similarly, the file icon.icns will be used as the icon
|
||||
of the Macintosh application. (Note that .icns is a different file format
|
||||
from .ico.)
|
||||
|
||||
|
||||
On Mac OS X, Ren'Py will not change the application icon unless
|
||||
config.window_icon is set. More specifically, it will default to the
|
||||
launcher icon rather than a generic pygame icon.
|
||||
|
||||
|
||||
The Macintosh application will now show the name of the game in the
|
||||
menu bar, rather than "Ren'Py Launcher".
|
||||
|
||||
|
||||
The "Continue Game" option on the main menu has been renamed "Load
|
||||
Game", for consistency with the game menu.
|
||||
|
||||
|
||||
When config.developer is set and an undefined image is used in a show
|
||||
or scene statement, a list of undefined images will appear centered at
|
||||
the top of the screen. A black screen (configurable with
|
||||
config.missing_background) will be shown instead of any missing
|
||||
background. This makes it easier to write a script before all images
|
||||
are present.
|
||||
|
||||
|
||||
The newly-documented Style.set_parent method lets you change the
|
||||
parent of a style. The new style.rebuild function allows styles to be
|
||||
rebuilt even after the game has begun running, with some
|
||||
caveats. (Changes are not persisted, for one thing.)
|
||||
|
||||
|
||||
The new unscrollable style property allows one to choose how
|
||||
unscrollbable scrollbars are displays. None keeps the current
|
||||
behavior, which shows the bar normally. If set to "insensitive", the
|
||||
bar becomes insensitive, while if set to "hide", the bar is not
|
||||
drawn at all.
|
||||
|
||||
|
||||
The new renpy.easy_displayable method can be used by user-defined
|
||||
displayables to convert strings into Solid or Image displayables.
|
||||
|
||||
|
||||
Alt-F4 and Command-Q now trigger the quit event, as if you pressed the
|
||||
quit button on the top of the window. The first press brings up the
|
||||
quit prompt, and a second press forcibly terminates the game.
|
||||
|
||||
Alt-M and Command-M iconify the window.
|
||||
|
||||
|
||||
The new RENPY_SCREENSHOT_PATTERN environment variable lets you
|
||||
configure the name of screenshot files. Ren'Py can now write out
|
||||
screenshots as JPGs, as well as PNGs.
|
||||
|
||||
|
||||
Several of the libraries underlying Ren'Py have been updated. Pygame
|
||||
has been updated to version 1.8.1, while Freetype is now at version
|
||||
2.3.7.
|
||||
|
||||
|
||||
The presplash code now looks for presplash.jpg in the game directory
|
||||
if presplash.png is not found.
|
||||
|
||||
|
||||
On Windows, config.window_icon will be scaled to 32x32 if it is wider
|
||||
than 32 pixels. This allows the same icon to be used across platforms.
|
||||
|
||||
|
||||
Custom mouse cursors should now be properly hidden when the mouse
|
||||
leaves the window. Previously, this failed to work on the Mac OS X
|
||||
platform.
|
||||
|
||||
|
||||
The launcher has been moved into the launcher/ directory. This really
|
||||
shouldn't matter much, but may affect packaging and translation.
|
||||
|
||||
|
||||
New in Ren'Py 6.7.1
|
||||
-------------------
|
||||
|
||||
@@ -48,11 +338,20 @@ what to archive. This makes it easier to break the files up into
|
||||
multiple archives, in a single game.
|
||||
|
||||
|
||||
The new renpy.copy_images function copies images with one prefix to a
|
||||
second prefix.
|
||||
|
||||
|
||||
The launcher will no longer include directories beginning with a dot
|
||||
(like .svn and .bzr) when copying the template directory to create a
|
||||
new project.
|
||||
|
||||
|
||||
This version includes a refresh of the character art included with
|
||||
"The Question". The new art is by Deji, and the full-color version of
|
||||
the art is available from http://wiki.renai.us/.
|
||||
|
||||
|
||||
|
||||
New in Ren'Py 6.7.0
|
||||
-------------------
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright 2004-2007 PyTom <pytom@bishoujo.us>
|
||||
Copyright 2004-2009 PyTom <pytom@bishoujo.us>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
This file contains boilerplate information about running Ren'Py that
|
||||
can be included into the README for your game. It may be necessary to
|
||||
change some of the filenames in this document, to make it specific to
|
||||
your game.
|
||||
|
||||
Feel free to use this in your projects.
|
||||
|
||||
|
||||
|
||||
Running the Game
|
||||
================
|
||||
|
||||
(Windows)
|
||||
|
||||
If this game was installed on Windows using an installer, then you can
|
||||
run it by choosing the shortcut left by the installer. If this game
|
||||
was installed on Windows using the cross-platform zip file, then it
|
||||
can be run by executing the run_game.exe file.
|
||||
|
||||
(Macintosh)
|
||||
|
||||
For information about running Ren'Py games on Mac OS 10.3 or higher,
|
||||
please go to http://www.bishoujo.us/renpy/mac.html .
|
||||
|
||||
(Linux/Unix)
|
||||
|
||||
For information about running Ren'Py games under Linux and Unix,
|
||||
please go to http://www.bishoujo.us/renpy/linux.html . At the very
|
||||
least, you will need to compile the _renpy module. Read
|
||||
the file module/README.txt for details.
|
||||
|
||||
|
||||
Playing the Game
|
||||
================
|
||||
|
||||
By default, the game starts running in full screen mode. On some
|
||||
computers, especially some virtual machines, this can lead to mouse
|
||||
problems. To fix this, press 'f'.
|
||||
|
||||
Some of the more interesting game actions can be performed as follows:
|
||||
|
||||
- The left mouse button is used to advance to the next line of
|
||||
dialogue, or to pick menu options.
|
||||
|
||||
- The right mouse button brings you into a menu screen where you can
|
||||
save the game, load the game, change preferences, return to the main
|
||||
menu, or quit entirely.
|
||||
|
||||
- Scrolling the mouse wheel up or pushing page up returns you to the
|
||||
previous screen.
|
||||
|
||||
- Holding down the CTRL key skips dialogue. By default, it only
|
||||
skips read dialogue, but this can be changed by a preference. Tab
|
||||
toggles skip mode.
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
A version of dissolve that only draws the pixels that are being
|
||||
dissolved, while doing a straight-blit on pixels that remain the same
|
||||
between the two images.
|
||||
|
||||
Figure out some way of having variables updated in a new context saved
|
||||
permanently, even if we return to the outer context because of a
|
||||
rollback or something. Need to be very careful here.
|
||||
|
||||
Dynamic variables, parameterized labels, etc.
|
||||
|
||||
Implement an extension mechanism of some sort, so that users can drop
|
||||
packages of Ren'Py code into the Ren'Py directory, and get stuff like
|
||||
speech support.
|
||||
|
||||
Implement speech control support.
|
||||
|
||||
BPP conversion (perhaps as a package).
|
||||
|
||||
Automatically scale to a fixed size.
|
||||
|
||||
Hide picture-mouse when leaving window, restore it when entering window.
|
||||
@@ -18,7 +18,9 @@ def main():
|
||||
setup(name="RenPy",
|
||||
windows=[ dict(script="renpy.py",
|
||||
dest_base="renpy",
|
||||
icon_resources=[ (1, "newicon.ico") ] ) ],
|
||||
icon_resources=[ (1, "newicon.ico") ],
|
||||
),
|
||||
],
|
||||
|
||||
console=[ dict(script="renpy.py", dest_base="console") ],
|
||||
|
||||
@@ -57,6 +59,9 @@ def main():
|
||||
if fn in seen:
|
||||
continue
|
||||
|
||||
if fn == "SDL_mixer.dll":
|
||||
continue
|
||||
|
||||
seen[fn] = True
|
||||
|
||||
zfnew.writestr(fn, zfold.read(fn))
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
1#!/usr/bin/env python
|
||||
|
||||
import glob
|
||||
import re
|
||||
from sets import Set as set
|
||||
import sys
|
||||
|
||||
seen_files = set()
|
||||
seen_strings = set()
|
||||
|
||||
def tl(fn, s):
|
||||
|
||||
s = eval(s)
|
||||
|
||||
if len(s) < 3:
|
||||
return
|
||||
|
||||
if s in seen_strings:
|
||||
return
|
||||
|
||||
if fn not in seen_files:
|
||||
print >>out
|
||||
print >>out, " # Translatable strings found in", fn
|
||||
print >>out
|
||||
seen_files.add(fn)
|
||||
|
||||
print >>out, " config.translations[%r] = %r" % (s, s)
|
||||
seen_strings.add(s)
|
||||
|
||||
def process(fn):
|
||||
|
||||
data = file(fn).read()
|
||||
|
||||
for m in re.finditer(r'\bu\"(\\"|[^"])+\"', data):
|
||||
tl(fn, m.group(0))
|
||||
|
||||
for m in re.finditer(r"\bu\'(\\'|[^'])+\'", data):
|
||||
tl(fn, m.group(0))
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# out = file("extras/translations.rpy", "w")
|
||||
out = sys.stdout
|
||||
|
||||
print >>out, "# This file contains a list of all of the phrases you can translate"
|
||||
print >>out, "# from the Ren'Py launcher."
|
||||
print >>out, ""
|
||||
print >>out, "init python:"
|
||||
|
||||
for fn in glob.glob("launcher/*.rpy"):
|
||||
process(fn)
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 296 B |
|
Before Width: | Height: | Size: 335 B |
@@ -1,697 +0,0 @@
|
||||
# cardgame.rpy - Cardgame support for Ren'Py
|
||||
# Copyright (C) 2008 PyTom <pytom@bishoujo.us>
|
||||
#
|
||||
# This software may be distributed in modified or unmodified form,
|
||||
# provided:
|
||||
#
|
||||
# (1) This complete license notice is retained.
|
||||
#
|
||||
# (2) This software and all software and data files distributed
|
||||
# alongside this software and intended to be loaded in the same
|
||||
# memory space may be redistributed without requirement for
|
||||
# payment, notification, or other forms of compensation.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Commercial licensing for this software is available, please
|
||||
# contact pytom@bishoujo.us for information.
|
||||
|
||||
init python:
|
||||
|
||||
import pygame
|
||||
|
||||
DRAG_NONE = 0
|
||||
DRAG_CARD = 1
|
||||
DRAG_ABOVE = 2
|
||||
DRAG_STACK = 3
|
||||
DRAG_TOP = 4
|
||||
|
||||
# Returns the overlap of the area between the two
|
||||
# rectangles.
|
||||
def __rect_overlap_area(r1, r2):
|
||||
if r1 is None or r2 is None:
|
||||
return 0
|
||||
|
||||
x1, y1, w1, h1 = r1
|
||||
x2, y2, w2, h2 = r2
|
||||
|
||||
maxleft = max(x1, x2)
|
||||
minright = min(x1 + w1, x2 + w2)
|
||||
maxtop = max(y1, y2)
|
||||
minbottom = min(y1 + h1, y2 + h2)
|
||||
|
||||
if minright < maxleft:
|
||||
return 0
|
||||
|
||||
if minbottom < maxtop:
|
||||
return 0
|
||||
|
||||
return (minright - maxleft) * (minbottom - maxtop)
|
||||
|
||||
def __default_can_drag(table, stack, card):
|
||||
return table.get_faceup(card)
|
||||
|
||||
class Table(renpy.Displayable):
|
||||
|
||||
def __init__(self, back=None, base=None, springback=0.1, rotate=0.1, can_drag=__default_can_drag, doubleclick=.33, **kwargs):
|
||||
|
||||
renpy.Displayable.__init__(self, **kwargs)
|
||||
|
||||
# A map from card value to the card object corresponding to
|
||||
# that value.
|
||||
self.cards = { }
|
||||
|
||||
# A list of the stacks that have been defined.
|
||||
self.stacks = [ ]
|
||||
|
||||
# The back of cards that don't have a more specific back
|
||||
# defined.
|
||||
self.back = renpy.easy.displayable_or_none(back)
|
||||
|
||||
# The base of stacks that don't have a more specific base
|
||||
# defined.
|
||||
self.base = renpy.easy.displayable_or_none(base)
|
||||
|
||||
# The amount of time it takes for cards to springback
|
||||
# into their rightful place.
|
||||
self.springback = springback
|
||||
|
||||
# The amount of time it takes for cards to rotate into
|
||||
# their proper orientation.
|
||||
self.rotate = rotate
|
||||
|
||||
# A function that is called to tell if we can drag a
|
||||
# particular card.
|
||||
self.can_drag = can_drag
|
||||
|
||||
# The time between clicks for the click to be considered a
|
||||
# double-click.
|
||||
self.doubleclick = doubleclick
|
||||
|
||||
# Are we sensitive to input? [doc]
|
||||
self.sensitive = True
|
||||
|
||||
# The last click event.
|
||||
self.last_event = CardEvent()
|
||||
|
||||
# The card that has been clicked.
|
||||
self.click_card = None
|
||||
|
||||
# The stack that has been clicked.
|
||||
self.click_stack = None
|
||||
|
||||
# The list of cards that are being dragged.
|
||||
self.drag_cards = [ ]
|
||||
|
||||
# Are we dragging the cards?
|
||||
self.dragging = False
|
||||
|
||||
# The position where we clicked.
|
||||
self.click_x = 0
|
||||
self.click_y = 0
|
||||
|
||||
# The amount of time we've been shown for.
|
||||
self.st = 0
|
||||
|
||||
# This shows the table on the given layer.
|
||||
def show(self, layer='master'):
|
||||
|
||||
for v in self.cards.itervalues():
|
||||
v.offset = __Fixed(0, 0)
|
||||
|
||||
ui.layer(layer)
|
||||
ui.add(self)
|
||||
ui.close()
|
||||
|
||||
# This hides the table.
|
||||
def hide(self, layer='master'):
|
||||
ui.layer(layer)
|
||||
ui.remove(self)
|
||||
ui.close()
|
||||
|
||||
# This controls sensitivity.
|
||||
def set_sensitive(self, value):
|
||||
self.sensitive = value
|
||||
|
||||
def get_card(self, value):
|
||||
if value not in self.cards:
|
||||
raise Exception("No card has the value %r." % value)
|
||||
|
||||
return self.cards[value]
|
||||
|
||||
# This sets the faceup status of a card.
|
||||
def set_faceup(self, card, faceup=True):
|
||||
self.get_card(card).faceup = faceup
|
||||
renpy.redraw(self, 0)
|
||||
|
||||
def get_faceup(self, card):
|
||||
return self.get_card(card).faceup
|
||||
|
||||
# This sets the rotation of a card.
|
||||
def set_rotate(self, card, rotation):
|
||||
__Rotate(self.get_card(card), rotation)
|
||||
renpy.redraw(self, 0)
|
||||
|
||||
def get_rotate(self, card):
|
||||
return self.get_card(card).rotate.rotate_limit()
|
||||
|
||||
def add_marker(self, card, marker):
|
||||
self.get_card(card).markers.append(marker)
|
||||
renpy.redraw(self, 0)
|
||||
|
||||
def remove_marker(self, card, marker):
|
||||
self.get_card(card).markers.remove(marker)
|
||||
renpy.redraw(self, 0)
|
||||
|
||||
# Called to create a new card.
|
||||
def card(self, value, face, back=None):
|
||||
self.cards[value] = __Card(self, value, face, back)
|
||||
|
||||
# Called to create a new stack.
|
||||
def stack(self, x, y, xoff=0, yoff=0, show=1024, base=None,
|
||||
click=False, drag=DRAG_NONE, drop=False, hidden=False):
|
||||
|
||||
rv = __Stack(self, x, y, xoff, yoff, show, base, click, drag, drop, hidden)
|
||||
|
||||
self.stacks.append(rv)
|
||||
return rv
|
||||
|
||||
# Force a redraw on each interaction.
|
||||
def per_interact(self):
|
||||
renpy.redraw(self, 0)
|
||||
|
||||
|
||||
def render(self, width, height, st, at):
|
||||
|
||||
self.st = st
|
||||
|
||||
rv = renpy.Render(width, height)
|
||||
|
||||
for s in self.stacks:
|
||||
|
||||
if s.hidden:
|
||||
s.rect = None
|
||||
for c in s.cards:
|
||||
c.rect = None
|
||||
continue
|
||||
|
||||
s.render_to(rv, width, height, st, at)
|
||||
|
||||
for c in s.cards:
|
||||
c.render_to(rv, width, height, st, at)
|
||||
|
||||
return rv
|
||||
|
||||
def event(self, ev, x, y, st):
|
||||
|
||||
self.st = st
|
||||
|
||||
if not self.sensitive:
|
||||
return
|
||||
|
||||
grabbed = renpy.display.focus.get_grab()
|
||||
|
||||
if (grabbed is not None) and (grabbed is not self):
|
||||
return
|
||||
|
||||
if ev.type == pygame.MOUSEBUTTONDOWN and ev.button == 1:
|
||||
|
||||
if self.click_stack:
|
||||
return
|
||||
|
||||
stack = None
|
||||
card = None
|
||||
|
||||
for s in self.stacks:
|
||||
|
||||
sx, sy, sw, sh = s.rect
|
||||
if sx <= x and sy <= y and sx + sw > x and sy + sh > y:
|
||||
stack = s
|
||||
|
||||
|
||||
for c in s.cards[-s.show:]:
|
||||
if c.rect is None:
|
||||
continue
|
||||
|
||||
cx, cy, cw, ch = c.rect
|
||||
if cx <= x and cy <= y and cx + cw > x and cy + ch > y:
|
||||
card = c
|
||||
stack = c.stack
|
||||
|
||||
if stack is None:
|
||||
return
|
||||
|
||||
# Grab the display.
|
||||
renpy.display.focus.set_grab(self)
|
||||
|
||||
# Don't let the user grab a moving card.
|
||||
if card is not None:
|
||||
xoffset, yoffset = card.offset.offset()
|
||||
if xoffset or yoffset:
|
||||
return
|
||||
|
||||
# Move the stack containing the card to the front.
|
||||
self.stacks.remove(stack)
|
||||
self.stacks.append(stack)
|
||||
|
||||
if stack.click or stack.drag:
|
||||
self.click_card = card
|
||||
self.click_stack = stack
|
||||
|
||||
if card is None or not self.can_drag(self, card.stack, card.value):
|
||||
self.drag_cards = [ ]
|
||||
elif card.stack.drag == DRAG_CARD:
|
||||
self.drag_cards = [ card ]
|
||||
elif card.stack.drag == DRAG_ABOVE:
|
||||
self.drag_cards = [ ]
|
||||
for c in card.stack.cards:
|
||||
if c is card or self.drag_cards:
|
||||
self.drag_cards.append(c)
|
||||
elif card.stack.drag == DRAG_STACK:
|
||||
self.drag_cards = list(card.stack.cards)
|
||||
elif card.stack.drag == DRAG_TOP:
|
||||
if card.stack.cards[-1] is card:
|
||||
self.drag_cards = [ card ]
|
||||
else:
|
||||
self.drag_cards = [ ]
|
||||
|
||||
for c in self.drag_cards:
|
||||
c.offset = __Fixed(0, 0)
|
||||
|
||||
self.click_x = x
|
||||
self.click_y = y
|
||||
self.dragging = False
|
||||
|
||||
renpy.redraw(self, 0)
|
||||
|
||||
raise renpy.IgnoreEvent()
|
||||
|
||||
if ev.type == pygame.MOUSEMOTION or (ev.type == pygame.MOUSEBUTTONUP and ev.button == 1):
|
||||
|
||||
if abs(x - self.click_x) > 7 or abs(y - self.click_y) > 7:
|
||||
self.dragging = True
|
||||
|
||||
dx = x - self.click_x
|
||||
dy = y - self.click_y
|
||||
|
||||
for c in self.drag_cards:
|
||||
xoffset, yoffset = c.offset.offset()
|
||||
|
||||
cdx = dx - xoffset
|
||||
cdy = dy - yoffset
|
||||
|
||||
c.offset = __Fixed(dx, dy)
|
||||
|
||||
if c.rect:
|
||||
cx, cy, cw, ch = c.rect
|
||||
cx += cdx
|
||||
cy += cdy
|
||||
c.rect = (cx, cy, cw, ch)
|
||||
|
||||
area = 0
|
||||
dststack = None
|
||||
dstcard = None
|
||||
|
||||
for s in self.stacks:
|
||||
if not s.drop:
|
||||
continue
|
||||
|
||||
for c in self.drag_cards:
|
||||
|
||||
if c.stack == s:
|
||||
continue
|
||||
a = __rect_overlap_area(c.rect, s.rect)
|
||||
if a >= area:
|
||||
dststack = s
|
||||
dstcard = None
|
||||
area = a
|
||||
|
||||
for c1 in s.cards:
|
||||
a = __rect_overlap_area(c.rect, c1.rect)
|
||||
if a >= area:
|
||||
dststack = s
|
||||
dstcard = c1
|
||||
area = a
|
||||
|
||||
if area == 0:
|
||||
dststack = None
|
||||
dstcard = None
|
||||
|
||||
renpy.redraw(self, 0)
|
||||
|
||||
if ev.type == pygame.MOUSEMOTION:
|
||||
raise renpy.IgnoreEvent()
|
||||
|
||||
if ev.type == pygame.MOUSEBUTTONUP and ev.button == 1:
|
||||
|
||||
# Ungrab the display.
|
||||
renpy.display.focus.set_grab(None)
|
||||
|
||||
evt = None
|
||||
|
||||
if self.dragging:
|
||||
if dststack is not None and self.drag_cards:
|
||||
|
||||
evt = CardEvent()
|
||||
evt.type = "drag"
|
||||
evt.table = self
|
||||
evt.stack = self.click_stack
|
||||
evt.card = self.click_card.value
|
||||
evt.drag_cards = [c.value for c in self.drag_cards]
|
||||
evt.drop_stack = dststack
|
||||
if dstcard:
|
||||
evt.drop_card = dstcard.value
|
||||
evt.time = st
|
||||
|
||||
else:
|
||||
|
||||
if self.click_stack.click:
|
||||
|
||||
evt = CardEvent()
|
||||
evt.type = "click"
|
||||
evt.table = self
|
||||
evt.stack = self.click_stack
|
||||
if self.click_card:
|
||||
evt.card = self.click_card.value
|
||||
else:
|
||||
evt.card = None
|
||||
|
||||
evt.time = st
|
||||
|
||||
if (evt.type == self.last_event.type
|
||||
and evt.stack == self.last_event.stack
|
||||
and evt.card == self.last_event.card
|
||||
and evt.time < self.last_event.time + self.doubleclick):
|
||||
|
||||
evt.type = "doubleclick"
|
||||
|
||||
if evt is not None:
|
||||
self.last_event = evt
|
||||
|
||||
for c in self.drag_cards:
|
||||
c.springback()
|
||||
|
||||
self.click_card = None
|
||||
self.click_stack = None
|
||||
self.drag_cards = [ ]
|
||||
|
||||
if evt is not None:
|
||||
return evt
|
||||
else:
|
||||
raise renpy.IgnoreEvent()
|
||||
|
||||
|
||||
class CardEvent(object):
|
||||
|
||||
def __init__(self):
|
||||
self.type = None
|
||||
self.stack = None
|
||||
self.card = None
|
||||
self.drag_cards = None
|
||||
self.drop_stack = None
|
||||
self.drop_card = None
|
||||
self.time = 0
|
||||
|
||||
# Represents a stack of one or more cards, which can be placed on the
|
||||
# table.
|
||||
class __Stack(object):
|
||||
|
||||
def __init__(
|
||||
self, table,
|
||||
x, y,
|
||||
xoff, yoff,
|
||||
show, base,
|
||||
click, drag, drop,
|
||||
hidden):
|
||||
|
||||
|
||||
# The table this stack belongs to.
|
||||
self.table = table
|
||||
|
||||
# The x and y coordinates of the center of the top card of
|
||||
# this stack.
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
# The offset in the x and y directions of each successive
|
||||
# card.
|
||||
self.xoff = xoff
|
||||
self.yoff = yoff
|
||||
|
||||
# The number of cards to show from this stack. (We show the
|
||||
# last show cards if this is less than the numebr of cards
|
||||
# in the stack.)
|
||||
self.show = show
|
||||
|
||||
# The image that is shown behind the stack. If None, the
|
||||
# background is taken from the table.
|
||||
self.base = base
|
||||
|
||||
# Should we report click events on this stack?
|
||||
self.click = click
|
||||
|
||||
# Should we allow dragging this stack? If so, how?
|
||||
self.drag = drag
|
||||
|
||||
# Should we allow dropping to this stack?
|
||||
self.drop = drop
|
||||
|
||||
# Is this stack hidden?
|
||||
self.hidden = hidden
|
||||
|
||||
# The list of cards in this stack.
|
||||
self.cards = [ ]
|
||||
|
||||
# The rectangle for the background of this effect.
|
||||
self.rect = None
|
||||
|
||||
def insert(self, index, card):
|
||||
card = self.table.get_card(card)
|
||||
|
||||
if card.stack:
|
||||
card.stack.cards.remove(card)
|
||||
|
||||
card.stack = self
|
||||
self.cards.insert(index, card)
|
||||
|
||||
self.table.stacks.remove(self)
|
||||
self.table.stacks.append(self)
|
||||
|
||||
card.springback()
|
||||
|
||||
def append(self, card):
|
||||
if card in self.cards:
|
||||
self.insert(len(self.cards) - 1, card)
|
||||
else:
|
||||
self.insert(len(self.cards), card)
|
||||
|
||||
def remove(self, card):
|
||||
card = self.table.get_card(card)
|
||||
self.cards.remove(card)
|
||||
card.stack = None
|
||||
card.rect = None
|
||||
|
||||
def deal(self):
|
||||
if not self.cards:
|
||||
return None
|
||||
|
||||
card = self.cards[-1]
|
||||
self.remove(card.value)
|
||||
return card.value
|
||||
|
||||
def shuffle(self):
|
||||
renpy.random.shuffle(self.cards)
|
||||
renpy.redraw(self.table, 0)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.cards)
|
||||
|
||||
def __getitem__(self, idx):
|
||||
return self.cards[idx].value
|
||||
|
||||
def __iter__(self):
|
||||
for i in self.cards:
|
||||
yield i.value
|
||||
|
||||
def __contains__(self, item):
|
||||
return self.table.get_card(card) in self.cards
|
||||
|
||||
def render_to(self, rv, width, height, st, at):
|
||||
|
||||
base = self.base or self.table.base
|
||||
|
||||
if base is None:
|
||||
return
|
||||
|
||||
surf = renpy.render(base, width, height, st, at)
|
||||
cw, ch = surf.get_size()
|
||||
|
||||
cx = self.x - cw / 2
|
||||
cy = self.y - ch / 2
|
||||
|
||||
self.rect = (cx, cy, cw, ch)
|
||||
rv.blit(surf, (cx, cy))
|
||||
|
||||
class __Card(object):
|
||||
|
||||
def __init__(self, table, value, face, back):
|
||||
|
||||
# The table this card belongs to.
|
||||
self.table = table
|
||||
|
||||
# The value of this card.
|
||||
self.value = value
|
||||
|
||||
# The face of this card.
|
||||
self.face = renpy.easy.displayable(face)
|
||||
|
||||
# The back of this card. If None, then the back is taken from
|
||||
# the table the card belongs to.
|
||||
self.back = renpy.easy.displayable_or_none(back)
|
||||
|
||||
# Is this card faceup (or face down?)
|
||||
self.faceup = True
|
||||
|
||||
# Object that's called to decide how rotated this card should
|
||||
# be.
|
||||
self.rotate = None
|
||||
|
||||
# A series of highlights that should be drawn over this card.
|
||||
self.highlights = [ ]
|
||||
|
||||
# The stack this card is in.
|
||||
self.stack = None
|
||||
|
||||
# An object that gives the offset of this card relative to
|
||||
# where it would normally be placed.
|
||||
self.offset = __Fixed(0, 0)
|
||||
|
||||
# The rectangle where this card was last drawn to the screen
|
||||
# at.
|
||||
self.rect = None
|
||||
|
||||
__Rotate(self, 0)
|
||||
|
||||
# Returns the base x and y placement of this card.
|
||||
def place(self):
|
||||
s = self.stack
|
||||
offset = max(len(s.cards) - s.show, 0)
|
||||
index = max(s.cards.index(self) - offset, 0)
|
||||
|
||||
return (s.x + s.xoff * index, s.y + s.yoff * index)
|
||||
|
||||
def springback(self):
|
||||
if self.rect is None:
|
||||
self.offset = __Fixed(0, 0)
|
||||
else:
|
||||
self.offset = __Springback(self)
|
||||
|
||||
def render_to(self, rv, width, height, st, at):
|
||||
|
||||
x, y = self.place()
|
||||
xoffset, yoffset = self.offset.offset()
|
||||
x += xoffset
|
||||
y += yoffset
|
||||
|
||||
if self.faceup:
|
||||
d = self.face
|
||||
else:
|
||||
d = self.back or self.table.back
|
||||
|
||||
# TODO: Figure out if we can reuse some of this.
|
||||
|
||||
if self.highlights:
|
||||
d = Fixed(* ([d] + [renpy.easy.displayable(i) for i in self.highlights]))
|
||||
|
||||
r = self.rotate.rotate()
|
||||
if r:
|
||||
d = RotoZoom(r, r, 0, 1, 1, 0)(d)
|
||||
|
||||
surf = renpy.render(d, width, height, st, at)
|
||||
w, h = surf.get_size()
|
||||
|
||||
x -= w / 2
|
||||
y -= h / 2
|
||||
|
||||
self.rect = (x, y, w, h)
|
||||
|
||||
rv.blit(surf, (x, y))
|
||||
|
||||
def __repr__(self):
|
||||
return "<__Card %r>" % self.value
|
||||
|
||||
|
||||
class __Springback(object):
|
||||
|
||||
def __init__(self, card):
|
||||
self.card = card
|
||||
self.table = table = card.table
|
||||
|
||||
self.start = table.st
|
||||
|
||||
cx, cy, cw, ch = self.card.rect
|
||||
x = cx + cw / 2
|
||||
y = cy + ch / 2
|
||||
|
||||
self.startx = x
|
||||
self.starty = y
|
||||
|
||||
def offset(self):
|
||||
|
||||
t = (self.table.st - self.start) / self.table.springback
|
||||
t = min(t, 1.0)
|
||||
|
||||
if t < 1.0:
|
||||
renpy.redraw(self.table, 0)
|
||||
|
||||
px, py = self.card.place()
|
||||
|
||||
return int((self.startx - px) * (1.0 - t)), int((self.starty - py) * (1.0 - t))
|
||||
|
||||
|
||||
class __Fixed(object):
|
||||
def __init__(self, x, y):
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
def offset(self):
|
||||
return self.x, self.y
|
||||
|
||||
|
||||
class __Rotate(object):
|
||||
def __init__(self, card, amount):
|
||||
|
||||
self.table = table = card.table
|
||||
self.start = table.st
|
||||
|
||||
if card.rotate is None:
|
||||
self.start_rotate = amount
|
||||
else:
|
||||
self.start_rotate = card.rotate.rotate()
|
||||
|
||||
self.end_rotate = amount
|
||||
|
||||
card.rotate = self
|
||||
|
||||
|
||||
def rotate(self):
|
||||
|
||||
if self.start_rotate == self.end_rotate:
|
||||
return self.start_rotate
|
||||
|
||||
t = (self.table.st - self.start) / self.table.springback
|
||||
t = min(t, 1.0)
|
||||
|
||||
if t < 1.0:
|
||||
renpy.redraw(self.table, 0)
|
||||
|
||||
return self.start_rotate + (self.end_rotate - self.start_rotate) * t
|
||||
|
||||
def rotate_limit(self):
|
||||
return self.end_rotate
|
||||
|
Before Width: | Height: | Size: 140 KiB |
@@ -1,336 +0,0 @@
|
||||
# klondike.rpy - Klondike Solitaire
|
||||
# Copyright (C) 2008 PyTom <pytom@bishoujo.us>
|
||||
#
|
||||
# This software may be distributed in modified or unmodified form,
|
||||
# provided:
|
||||
#
|
||||
# (1) This complete license notice is retained.
|
||||
#
|
||||
# (2) This software and all software and data files distributed
|
||||
# alongside this software and intended to be loaded in the same
|
||||
# memory space may be redistributed without requirement for
|
||||
# payment, notification, or other forms of compensation.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Commercial licensing for this software is available, please
|
||||
# contact pytom@bishoujo.us for information.
|
||||
|
||||
init python:
|
||||
|
||||
class Klondike(object):
|
||||
|
||||
# We represent a card as a (suit, rank) tuple. The suit is one of the
|
||||
# following four constants, while the rank is 1 for ace, 2 for 2,
|
||||
# ..., 10 for 10, 11 for jack, 12 for queen, 13 for king.
|
||||
CLUB = 0
|
||||
SPADE = 1
|
||||
HEART = 2
|
||||
DIAMOND = 3
|
||||
|
||||
def __init__(self, deal=3):
|
||||
|
||||
# Constants that let us easily change where the game is
|
||||
# located.
|
||||
LEFT=140
|
||||
TOP=58
|
||||
COL_SPACING = 90
|
||||
ROW_SPACING = 120
|
||||
CARD_XSPACING = 20
|
||||
CARD_YSPACING = 30
|
||||
|
||||
# Store the parameters.
|
||||
self.deal = deal
|
||||
|
||||
# Create the table, stock, and waste.
|
||||
self.table = t = Table(base="card/base.png", back="card/back.png")
|
||||
self.stock = t.stack(LEFT, TOP, xoff=0, yoff=0, click=True)
|
||||
self.waste = t.stack(LEFT + COL_SPACING, TOP, xoff=CARD_XSPACING, drag=DRAG_TOP, show=self.deal, click=True)
|
||||
|
||||
# The 4 foundation stacks.
|
||||
self.foundations = [ ]
|
||||
for i in range(0, 4):
|
||||
s = t.stack(LEFT + COL_SPACING * (i + 3), TOP, xoff=0, yoff=0, drag=DRAG_TOP, drop=True)
|
||||
self.foundations.append(s)
|
||||
|
||||
# The 7 tableau stacks.
|
||||
self.tableau = [ ]
|
||||
for i in range(0, 7):
|
||||
s = t.stack(LEFT + COL_SPACING * i, TOP + ROW_SPACING, xoff=0, yoff=CARD_YSPACING, drag=DRAG_ABOVE, click=True, drop=True)
|
||||
self.tableau.append(s)
|
||||
|
||||
# Create the stock and shuffle it.
|
||||
for rank in range(1, 14):
|
||||
for suit in range(0, 4):
|
||||
value = (suit, rank)
|
||||
t.card(value, "card/%d.png" % self.card_num(suit, rank))
|
||||
t.set_faceup(value, False)
|
||||
self.stock.append(value)
|
||||
|
||||
self.stock.shuffle()
|
||||
|
||||
# Deal out the initial tableau.
|
||||
for i in range(0, 7):
|
||||
for j in range(i, 7):
|
||||
c = self.stock.deal()
|
||||
self.tableau[j].append(c)
|
||||
|
||||
# Ensure that the bottom of each tableau is faceup.
|
||||
for i in range(0, 7):
|
||||
if self.tableau[i]:
|
||||
self.table.set_faceup(self.tableau[i][-1], True)
|
||||
|
||||
|
||||
# This figures out the image filename for a given suit and rank.
|
||||
def card_num(self, suit, rank):
|
||||
ranks = [ None, 1, 49, 45, 41, 37, 33, 29, 25, 21, 17, 13, 9, 5 ]
|
||||
return suit + ranks[rank]
|
||||
|
||||
def show(self):
|
||||
self.table.show()
|
||||
|
||||
def hide(self):
|
||||
self.table.hide()
|
||||
|
||||
def tableau_drag(self, evt):
|
||||
|
||||
card = evt.drag_cards[0]
|
||||
cards = evt.drag_cards
|
||||
stack = evt.drop_stack
|
||||
|
||||
csuit, crank = card
|
||||
|
||||
# If the stack is empty, allow a king to be dragged to it.
|
||||
if not stack:
|
||||
if crank == 13:
|
||||
for i in cards:
|
||||
stack.append(i)
|
||||
|
||||
return "tableau_drag"
|
||||
|
||||
# Otherwise, the stack has a bottom card.
|
||||
bottom = stack[-1]
|
||||
bsuit, brank = bottom
|
||||
|
||||
# Figure out which of the stacks are black.
|
||||
cblack = (csuit == self.SPADE) or (csuit == self.CLUB)
|
||||
bblack = (bsuit == self.SPADE) or (bsuit == self.CLUB)
|
||||
|
||||
# Can we legally place the cards?
|
||||
if (bblack != cblack) and (crank == brank - 1):
|
||||
|
||||
# Place the cards:
|
||||
for i in cards:
|
||||
stack.append(i)
|
||||
|
||||
return "tableau_drag"
|
||||
|
||||
return False
|
||||
|
||||
def foundation_drag(self, evt):
|
||||
|
||||
# We can only drag one card at a time to a foundation.
|
||||
if len(evt.drag_cards) != 1:
|
||||
return False
|
||||
|
||||
suit, rank = evt.drag_cards[0]
|
||||
|
||||
# If there is a card on the foundation already, then
|
||||
# check to see if we're dropping then next one in
|
||||
# sequence.
|
||||
if len(evt.drop_stack):
|
||||
dsuit, drank = evt.drop_stack[-1]
|
||||
if suit == dsuit and rank == drank + 1:
|
||||
evt.drop_stack.append(evt.drag_cards[0])
|
||||
return "foundation_drag"
|
||||
|
||||
# Otherwise, make sure we're dropping an ace.
|
||||
else:
|
||||
if rank == 1:
|
||||
evt.drop_stack.append(evt.drag_cards[0])
|
||||
return "foundation_drag"
|
||||
|
||||
return False
|
||||
|
||||
def tableau_doubleclick(self, evt):
|
||||
|
||||
# Make sure that there's at least one card in the stack.
|
||||
if not evt.stack:
|
||||
return False
|
||||
|
||||
# The bottom card in the stack.
|
||||
card = evt.stack[-1]
|
||||
suit, rank = card
|
||||
|
||||
# If the card is an ace, find an open foundation and put it
|
||||
# there.
|
||||
if rank == 1:
|
||||
for i in self.foundations:
|
||||
if not i:
|
||||
i.append(card)
|
||||
break
|
||||
return "foundation_drag"
|
||||
|
||||
# Otherwise, see if there's a foundation where we can put
|
||||
# the card.
|
||||
for i in self.foundations:
|
||||
if not i:
|
||||
continue
|
||||
|
||||
fsuit, frank = i[-1]
|
||||
if suit == fsuit and rank == frank + 1:
|
||||
i.append(card)
|
||||
return "foundation_drag"
|
||||
|
||||
return False
|
||||
|
||||
def stock_click(self, evt):
|
||||
|
||||
# If there are cards in the stock, dispense up to three3
|
||||
# of them.
|
||||
if self.stock:
|
||||
for i in range(0, self.deal):
|
||||
if self.stock:
|
||||
c = self.stock[-1]
|
||||
self.table.set_faceup(c, True)
|
||||
self.waste.append(c)
|
||||
|
||||
return "stock_click"
|
||||
|
||||
# Otherwise, move the contents of the waste to the stock.
|
||||
else:
|
||||
while self.waste:
|
||||
c = self.waste[-1]
|
||||
self.table.set_faceup(c, False)
|
||||
self.stock.append(c)
|
||||
|
||||
return "stock_click"
|
||||
|
||||
|
||||
def interact(self):
|
||||
|
||||
evt = ui.interact()
|
||||
rv = False
|
||||
|
||||
# Check the various events, and dispatch them to the methods
|
||||
# that handle them.
|
||||
if evt.type == "drag":
|
||||
if evt.drop_stack in self.tableau:
|
||||
rv = self.tableau_drag(evt)
|
||||
|
||||
elif evt.drop_stack in self.foundations:
|
||||
rv = self.foundation_drag(evt)
|
||||
|
||||
elif evt.type == "click":
|
||||
if evt.stack == self.stock:
|
||||
rv = self.stock_click(evt)
|
||||
|
||||
elif evt.type == "doubleclick":
|
||||
if (evt.stack in self.tableau) or (evt.stack is self.waste):
|
||||
rv = self.tableau_doubleclick(evt)
|
||||
|
||||
# Ensure that the bottom card in each tableau is faceup.
|
||||
for i in range(0, 7):
|
||||
if self.tableau[i]:
|
||||
self.table.set_faceup(self.tableau[i][-1], True)
|
||||
|
||||
# Check to see if any of the foundations has less than
|
||||
# 13 cards in it. If it does, return False. Otherwise,
|
||||
# return True.
|
||||
for i in self.foundations:
|
||||
if len(i) != 13:
|
||||
return rv
|
||||
|
||||
return "win"
|
||||
|
||||
# Sets things as sensitive (or not).
|
||||
def set_sensitive(self, value):
|
||||
self.table.set_sensitive(value)
|
||||
|
||||
# Utility functions.
|
||||
|
||||
# Is it okay to drag the over card onto under, where under is
|
||||
# part of a tableau.
|
||||
def can_hint(self, under, over):
|
||||
usuit, urank = under
|
||||
osuit, orank = over
|
||||
|
||||
if orank == 1:
|
||||
return False
|
||||
|
||||
ublack = (usuit == self.SPADE) or (usuit == self.CLUB)
|
||||
oblack = (osuit == self.SPADE) or (osuit == self.CLUB)
|
||||
|
||||
if (oblack != ublack) and (orank == urank - 1):
|
||||
return True
|
||||
|
||||
# Returns the first faceup card in the stack.
|
||||
def first_faceup(self, s):
|
||||
for c in s:
|
||||
if self.table.get_faceup(c):
|
||||
return c
|
||||
|
||||
# This tries to find a reasonable hint, and returns it as a
|
||||
# pair of cardnames.
|
||||
def hint(self):
|
||||
|
||||
for i in self.tableau:
|
||||
if not i:
|
||||
continue
|
||||
|
||||
over = self.first_faceup(i)
|
||||
|
||||
for j in self.tableau:
|
||||
if not j or i is j:
|
||||
continue
|
||||
|
||||
under = j[-1]
|
||||
|
||||
if self.can_hint(under, over):
|
||||
return (under, over)
|
||||
|
||||
if self.waste:
|
||||
|
||||
over = self.waste[-1]
|
||||
|
||||
for j in self.tableau:
|
||||
if not j:
|
||||
continue
|
||||
|
||||
under = j[-1]
|
||||
|
||||
if self.can_hint(under, over):
|
||||
return (under, over)
|
||||
|
||||
return None, None
|
||||
|
||||
def card_name(self, c):
|
||||
suit, rank = c
|
||||
|
||||
return [
|
||||
"INVALID",
|
||||
"Ace",
|
||||
"Two",
|
||||
"Three",
|
||||
"four",
|
||||
"Five",
|
||||
"Six",
|
||||
"Seven",
|
||||
"Eight",
|
||||
"Nine",
|
||||
"Ten",
|
||||
"Jack",
|
||||
"Queen",
|
||||
"King" ][rank] + " of " + [
|
||||
"Clubs",
|
||||
"Spades",
|
||||
"Hearts",
|
||||
"Diamonds" ][suit]
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 138 KiB |
@@ -1,247 +0,0 @@
|
||||
## This file contains some of the options that can be changed to customize
|
||||
## your Ren'Py game. It only contains the most common options... there
|
||||
## is quite a bit more customization you can do.
|
||||
##
|
||||
## Lines beginning with two '#' marks are comments, and you shouldn't
|
||||
## uncomment them. Lines beginning with a single '#' mark are
|
||||
## commented-out code, and you may want to uncomment them when
|
||||
## appropriate.
|
||||
|
||||
init -1 python hide:
|
||||
|
||||
## Should we enable the use of developer tools? This should be
|
||||
## set to False before the game is released, so the user can't
|
||||
## cheat using developer tools.
|
||||
|
||||
config.developer = True
|
||||
|
||||
## These control the width and height of the screen.
|
||||
|
||||
config.screen_width = 800
|
||||
config.screen_height = 600
|
||||
|
||||
## This controls the title of the window, when Ren'Py is
|
||||
## running in a window.
|
||||
|
||||
config.window_title = u"Ren'Py Cardgame Demo"
|
||||
|
||||
#########################################
|
||||
# Layouts
|
||||
|
||||
## This enables the use of an in-game menu that is made out of
|
||||
## buttons.
|
||||
|
||||
layout.button_menu()
|
||||
|
||||
#########################################
|
||||
# Themes
|
||||
|
||||
## We then want to call a theme function. themes.roundrect is
|
||||
## a theme that features the use of rounded rectangles. It's
|
||||
## the only theme we currently support.
|
||||
##
|
||||
## The theme function takes a number of parameters that can
|
||||
## customize the color scheme.
|
||||
|
||||
theme.roundrect(
|
||||
|
||||
## The color of an idle widget face.
|
||||
widget = "#7AA27B",
|
||||
|
||||
## The color of a focused widget face.
|
||||
widget_hover = "#A3C7A3",
|
||||
|
||||
## The color of the text in a widget.
|
||||
widget_text = "#CDE0CE",
|
||||
|
||||
## The color of the text in a selected widget. (For
|
||||
## example, the current value of a preference.)
|
||||
widget_selected = "#ffffff",
|
||||
|
||||
## The color of a disabled widget face.
|
||||
disabled = "#426143",
|
||||
|
||||
## The color of disabled widget text.
|
||||
disabled_text = "#819981",
|
||||
|
||||
## The color of informational labels.
|
||||
label = "#ffffff",
|
||||
|
||||
## The color of a frame containing widgets.
|
||||
frame = "#245536",
|
||||
|
||||
## If this is True, the in-game window is rounded. If False,
|
||||
## the in-game window is square.
|
||||
rounded_window = False,
|
||||
|
||||
## The background of the main menu. This can be a color
|
||||
## beginning with '#', or an image filename. The latter
|
||||
## should take up the full height and width of the screen.
|
||||
mm_root = "mainmenu.jpg",
|
||||
|
||||
## The background of the game menu. This can be a color
|
||||
## beginning with '#', or an image filename. The latter
|
||||
## should take up the full height and width of the screen.
|
||||
gm_root = "table.jpg",
|
||||
|
||||
## And we're done with the theme. The theme will customize
|
||||
## various styles, so if we want to change them, we should
|
||||
## do so below.
|
||||
)
|
||||
|
||||
|
||||
#########################################
|
||||
## These settings let you customize the window containing the
|
||||
## dialogue and narration, by replacing it with an image.
|
||||
|
||||
## The background of the window. In a Frame, the two numbers
|
||||
## are the size of the left/right and top/bottom borders,
|
||||
## respectively.
|
||||
|
||||
# style.window.background = Frame("frame.png", 12, 12)
|
||||
|
||||
## Margin is space surrounding the window, where the background
|
||||
## is not drawn.
|
||||
|
||||
# style.window.left_margin = 6
|
||||
# style.window.right_margin = 6
|
||||
# style.window.top_margin = 6
|
||||
# style.window.bottom_margin = 6
|
||||
|
||||
## Padding is space inside the window, where the background is
|
||||
## drawn.
|
||||
|
||||
# style.window.left_padding = 6
|
||||
# style.window.right_padding = 6
|
||||
# style.window.top_padding = 6
|
||||
# style.window.bottom_padding = 6
|
||||
|
||||
## This is the minimum height of the window, including the margins
|
||||
## and padding.
|
||||
|
||||
# style.window.yminimum = 250
|
||||
|
||||
|
||||
#########################################
|
||||
## This lets you change the placement of the main menu.
|
||||
|
||||
## The way placement works is that we find an anchor point
|
||||
## inside a displayable, and a position (pos) point on the
|
||||
## screen. We then place the displayable so the two points are
|
||||
## at the same place.
|
||||
|
||||
## An anchor/pos can be given as an integer or a floating point
|
||||
## number. If an integer, the number is interpreted as a number
|
||||
## of pixels from the upper-left corner. If a floating point,
|
||||
## the number is interpreted as a fraction of the size of the
|
||||
## displayable or screen.
|
||||
|
||||
# style.mm_menu_frame.xpos = 0.5
|
||||
# style.mm_menu_frame.xanchor = 0.5
|
||||
# style.mm_menu_frame.ypos = 0.75
|
||||
# style.mm_menu_frame.yanchor = 0.5
|
||||
|
||||
|
||||
#########################################
|
||||
## These let you customize the default font used for text in Ren'Py.
|
||||
|
||||
## The file containing the default font.
|
||||
|
||||
# style.default.font = "DejaVuSans.ttf"
|
||||
|
||||
## The default size of text.
|
||||
|
||||
# style.default.size = 22
|
||||
|
||||
## Note that these only change the size of some of the text. Other
|
||||
## buttons have their own styles.
|
||||
|
||||
|
||||
#########################################
|
||||
## These settings let you change some of the sounds that are used by
|
||||
## Ren'Py.
|
||||
|
||||
## Set this to False if the game does not have any sound effects.
|
||||
|
||||
config.has_sound = True
|
||||
|
||||
## Set this to False if the game does not have any music.
|
||||
|
||||
config.has_music = True
|
||||
|
||||
## Set this to False if the game does not have voicing.
|
||||
|
||||
config.has_voice = True
|
||||
|
||||
## Sounds that are used when button and imagemaps are clicked.
|
||||
|
||||
# style.button.activate_sound = "click.wav"
|
||||
# style.imagemap.activate_sound = "click.wav"
|
||||
|
||||
## Sounds that are used when entering and exiting the game menu.
|
||||
|
||||
# config.enter_sound = "click.wav"
|
||||
# config.exit_sound = "click.wav"
|
||||
|
||||
## A sample sound that can be played to check the sound volume.
|
||||
|
||||
# config.sample_sound = "click.wav"
|
||||
|
||||
## Music that is played while the user is at the main menu.
|
||||
|
||||
# config.main_menu_music = "main_menu_theme.ogg"
|
||||
|
||||
|
||||
#########################################
|
||||
## Transitions.
|
||||
|
||||
## Used when entering the game menu from the game.
|
||||
|
||||
config.enter_transition = dissolve
|
||||
|
||||
## Used when exiting the game menu to the game.
|
||||
|
||||
config.exit_transition = dissolve
|
||||
|
||||
## Used between screens of the game menu.
|
||||
|
||||
config.intra_transition = dissolve
|
||||
|
||||
## Used when entering the game menu from the main menu.
|
||||
|
||||
config.main_game_transition = dissolve
|
||||
|
||||
## Used when returning to the main menu from the game.
|
||||
|
||||
config.game_main_transition = dissolve
|
||||
|
||||
## Used when entering the main menu from the splashscreen.
|
||||
|
||||
config.end_splash_transition = dissolve
|
||||
|
||||
## Used when entering the main menu after the game has ended.
|
||||
|
||||
config.end_game_transition = fade
|
||||
|
||||
## Used when a game is loaded.
|
||||
|
||||
config.after_load_transition = dissolve
|
||||
|
||||
|
||||
#########################################
|
||||
## Default values of Preferences.
|
||||
|
||||
## Note: These options are only evaluated the first time a
|
||||
## game is run. To have them run a second time, delete
|
||||
## game/saves/persistent
|
||||
|
||||
## Should we start in fullscreen mode?
|
||||
|
||||
config.default_fullscreen = False
|
||||
|
||||
## The default text speed in characters per second. 0 is infinite.
|
||||
|
||||
config.default_text_cps = 0
|
||||
|
||||
config.save_directory = "cardgame-11754"
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
# You can place the script of your game in this file.
|
||||
|
||||
init:
|
||||
$ e = Character('Eileen', color="#c8ffc8")
|
||||
|
||||
image eileen happy = "eileen_happy.png"
|
||||
image bg table = "table.jpg"
|
||||
image dim = "#0008"
|
||||
|
||||
# Some styles for show text.
|
||||
$ style.centered_text.drop_shadow = (2, 2)
|
||||
$ style.centered_text.drop_shadow_color = "#000b"
|
||||
|
||||
label start:
|
||||
|
||||
scene bg table
|
||||
|
||||
python:
|
||||
k = Klondike(1)
|
||||
k.set_sensitive(False)
|
||||
k.show()
|
||||
|
||||
show dim
|
||||
show eileen happy
|
||||
with dissolve
|
||||
|
||||
e "Welcome to the cardgame demo. Let's play some solitaire!"
|
||||
|
||||
e "I might show up from time to time to give you some advice, but it's up to you if you want to take it."
|
||||
|
||||
e "Good luck!"
|
||||
|
||||
label continue:
|
||||
|
||||
hide dim
|
||||
hide eileen
|
||||
with dissolve
|
||||
|
||||
label quick_continue:
|
||||
|
||||
$ hint_count = renpy.random.randint(10, 20)
|
||||
|
||||
while True:
|
||||
|
||||
python:
|
||||
|
||||
ui.textbutton("Give Up", ui.jumps("giveup"), xalign=.02, yalign=.98)
|
||||
k.set_sensitive(True)
|
||||
event = k.interact()
|
||||
|
||||
if event:
|
||||
renpy.checkpoint()
|
||||
|
||||
if event == "win":
|
||||
jump win
|
||||
|
||||
if event == "tableau_drag" or event == "stock_click":
|
||||
$ hint_count -= 1
|
||||
if hint_count <= 0:
|
||||
jump hint
|
||||
|
||||
label win:
|
||||
|
||||
show dim
|
||||
show eileen happy
|
||||
with dissolve
|
||||
|
||||
"Congratulations!"
|
||||
|
||||
jump newgame
|
||||
|
||||
label giveup:
|
||||
|
||||
$ k.set_sensitive(False)
|
||||
|
||||
show dim
|
||||
show eileen happy
|
||||
with dissolve
|
||||
|
||||
menu:
|
||||
e "Are you sure you want to give up?"
|
||||
|
||||
"Yes":
|
||||
|
||||
"Oh well, better luck next time."
|
||||
|
||||
jump newgame
|
||||
|
||||
"No":
|
||||
|
||||
jump continue
|
||||
|
||||
label newgame:
|
||||
|
||||
menu:
|
||||
e "Would you like to try again?"
|
||||
|
||||
"Yes":
|
||||
pass
|
||||
|
||||
"No":
|
||||
e "Well, I hope to see you again soon."
|
||||
return
|
||||
|
||||
e "Okay, here we go!"
|
||||
|
||||
scene bg table
|
||||
|
||||
python:
|
||||
k = Klondike(1)
|
||||
k.sensitive = False
|
||||
k.show()
|
||||
|
||||
jump continue
|
||||
|
||||
|
||||
label hint:
|
||||
|
||||
$ under, over = k.hint()
|
||||
|
||||
$ print under, over
|
||||
|
||||
if under is None:
|
||||
jump quick_continue
|
||||
|
||||
$ under = k.card_name(under)
|
||||
$ over = k.card_name(over)
|
||||
|
||||
$ k.set_sensitive(False)
|
||||
|
||||
show dim
|
||||
show eileen happy
|
||||
with dissolve
|
||||
|
||||
$ hint = renpy.random.randint(0, 2)
|
||||
|
||||
if hint == 0:
|
||||
e "Maybe put the %(over)s on top of the %(under)s."
|
||||
|
||||
elif hint == 1:
|
||||
e "You can try moving the %(over)s to the %(under)s."
|
||||
|
||||
elif hint == 2:
|
||||
e "I think something can go on the %(under)s."
|
||||
|
||||
jump continue
|
||||
|
||||
|
||||
# This has nothing to do with card games.
|
||||
label splashscreen:
|
||||
|
||||
scene bg table
|
||||
$ renpy.pause(1.0)
|
||||
|
||||
show text "According to legend, prospectors in the klondike would carry with them a deck of cards."
|
||||
with dissolve
|
||||
with Pause(5.0)
|
||||
hide text
|
||||
with dissolve
|
||||
with Pause(1.0)
|
||||
|
||||
show text "If they were ever lost, they'd start playing a game of solitaire."
|
||||
with dissolve
|
||||
with Pause(4.0)
|
||||
hide text
|
||||
with dissolve
|
||||
with Pause(1.0)
|
||||
|
||||
show text "Without fail, help would soon arrive..."
|
||||
with dissolve
|
||||
with Pause(3.0)
|
||||
hide text
|
||||
with dissolve
|
||||
with Pause(1.0)
|
||||
|
||||
show text "... saying \"put the Five of Spades on the Six of Hearts.\""
|
||||
|
||||
with dissolve
|
||||
with Pause(4.0)
|
||||
hide text
|
||||
with dissolve
|
||||
with Pause(1.0)
|
||||
|
||||
return
|
||||
|
Before Width: | Height: | Size: 116 KiB |
@@ -196,6 +196,9 @@ label _style_hierarchy:
|
||||
jump _developer_screen
|
||||
|
||||
|
||||
init -1050 python:
|
||||
config.missing_background = "black"
|
||||
|
||||
init 1050 python:
|
||||
|
||||
if renpy.game.options.remote:
|
||||
@@ -214,4 +217,73 @@ init 1050 python:
|
||||
config.underlay.append(Remote())
|
||||
del Remote
|
||||
|
||||
|
||||
|
||||
if config.developer:
|
||||
|
||||
# This is used to indicate that a scene has just occured,
|
||||
# and so if the next thing is a missing_show, we should
|
||||
# blank the screen.
|
||||
__missing_scene = False
|
||||
|
||||
# This returns the __missing dictionary from the current
|
||||
# context object.
|
||||
def __missing():
|
||||
try:
|
||||
return renpy.context().__missing
|
||||
except AttributeError:
|
||||
rv = dict()
|
||||
renpy.context().__missing = rv
|
||||
return rv
|
||||
|
||||
def __missing_show_callback(name, what, layer):
|
||||
if layer != 'master':
|
||||
return False
|
||||
|
||||
global __missing_scene
|
||||
if __missing_scene:
|
||||
renpy.show(name, what=config.missing_background)
|
||||
__missing_scene = False
|
||||
|
||||
what = " ".join(what).replace("{", "{{")
|
||||
__missing()[name[0]] = what
|
||||
return True
|
||||
|
||||
def __missing_hide_callback(name, layer):
|
||||
if layer != 'master':
|
||||
return False
|
||||
|
||||
global __missing_scene
|
||||
__missing_scene = False
|
||||
|
||||
__missing().pop(name[0], None)
|
||||
return True
|
||||
|
||||
def __missing_scene_callback(layer):
|
||||
if layer != 'master':
|
||||
return False
|
||||
|
||||
global __missing_scene
|
||||
__missing_scene = True
|
||||
__missing().clear()
|
||||
|
||||
return True
|
||||
|
||||
def __missing_overlay():
|
||||
missing = __missing()
|
||||
|
||||
if not missing:
|
||||
return
|
||||
|
||||
ui.vbox(xalign=0.5, yalign=0.0)
|
||||
ui.text(_("Undefined Images"), xalign=0.5)
|
||||
|
||||
for what in sorted(missing.values()):
|
||||
ui.text(what, xalign=0.5)
|
||||
|
||||
ui.close()
|
||||
|
||||
|
||||
config.missing_scene = __missing_scene_callback
|
||||
config.missing_show = __missing_show_callback
|
||||
config.missing_hide = __missing_hide_callback
|
||||
config.overlay_functions.append(__missing_overlay)
|
||||
|
||||
@@ -111,16 +111,35 @@ init -1180 python:
|
||||
renpy.jump("_noisy_return")
|
||||
else:
|
||||
renpy.restart_interaction()
|
||||
|
||||
config.help = None
|
||||
|
||||
def _help():
|
||||
if not config.help:
|
||||
return
|
||||
|
||||
if renpy.has_label(config.help):
|
||||
renpy.call_in_new_context(config.help)
|
||||
return
|
||||
|
||||
_preferences.fullscreen = False
|
||||
|
||||
import webbrowser
|
||||
webbrowser.open_new("file:///" + config.basedir + "/" + config.help)
|
||||
|
||||
|
||||
init -1180 python hide:
|
||||
|
||||
# Called to make a screenshot happen.
|
||||
def screenshot():
|
||||
import os.path
|
||||
import os
|
||||
|
||||
pattern = os.environ.get("RENPY_SCREENSHOT_PATTERN", "screenshot%04d.png")
|
||||
|
||||
i = 1
|
||||
while True:
|
||||
fn = "screenshot%04d.png" % i
|
||||
fn = pattern % i
|
||||
if not os.path.exists(fn):
|
||||
break
|
||||
i += 1
|
||||
@@ -143,7 +162,6 @@ init -1180 python hide:
|
||||
|
||||
toggle_skipping()
|
||||
|
||||
|
||||
def fast_skip():
|
||||
if config.fast_skipping or config.developer:
|
||||
config.skipping = "fast"
|
||||
@@ -163,8 +181,9 @@ init -1180 python hide:
|
||||
|
||||
filename, line = renpy.get_filename_line()
|
||||
renpy.launch_editor([ filename ], line)
|
||||
|
||||
# The default keymap.
|
||||
|
||||
# The default keymap. We might also want to put some of this into
|
||||
# the launcher.
|
||||
km = renpy.Keymap(
|
||||
rollback = renpy.rollback,
|
||||
screenshot = screenshot,
|
||||
@@ -178,6 +197,9 @@ init -1180 python hide:
|
||||
dump_styles = dump_styles,
|
||||
reload_game = reload_game,
|
||||
developer = renpy.curried_call_in_new_context("_developer"),
|
||||
quit = renpy.quit_event,
|
||||
iconify = renpy.iconify,
|
||||
help = _help,
|
||||
)
|
||||
|
||||
config.underlay = [ km ]
|
||||
|
||||
@@ -12,9 +12,10 @@ init -1150 python:
|
||||
|
||||
config.main_menu = [
|
||||
(u"Start Game", "start", "True"),
|
||||
(u"Continue Game", _intra_jumps("load_screen", "main_game_transition"), "True"),
|
||||
(u"Load Game", _intra_jumps("load_screen", "main_game_transition"), "True"),
|
||||
(u"Preferences", _intra_jumps("preferences_screen", "main_game_transition"), "True"),
|
||||
(u"Quit", ui.jumps("_quit"), "True")
|
||||
(u"Help", _help, "True", "config.help"),
|
||||
(u"Quit", ui.jumps("_quit"), "True"),
|
||||
]
|
||||
|
||||
config.game_menu = [
|
||||
@@ -23,10 +24,13 @@ init -1150 python:
|
||||
( "save", u"Save Game", _intra_jumps("save_screen", "intra_transition"), 'not main_menu' ),
|
||||
( "load", u"Load Game", _intra_jumps("load_screen", "intra_transition"), 'True'),
|
||||
( None, u"Main Menu", ui.callsinnewcontext("_main_menu_prompt"), 'not main_menu' ),
|
||||
( "help", u"Help", _help, "True", "config.help"),
|
||||
( None, u"Quit", ui.callsinnewcontext("_quit_prompt"), 'True' ),
|
||||
]
|
||||
|
||||
label _quit_prompt:
|
||||
$ renpy.loadsave.force_autosave()
|
||||
|
||||
if layout.yesno_prompt(None, u"Are you sure you want to quit?"):
|
||||
jump _quit
|
||||
else:
|
||||
|
||||
@@ -41,6 +41,7 @@ init 1130:
|
||||
else:
|
||||
for i, m in enumerate(mixers):
|
||||
renpy.sound.set_mixer(i, m, default=True)
|
||||
renpy.sound.set_pan(0, 0, channel=i)
|
||||
if m == 'music':
|
||||
renpy.music.set_music(i, True, default=True)
|
||||
else:
|
||||
|
||||
@@ -68,6 +68,7 @@ init -1200 python hide:
|
||||
style.hyperlink_text = Style(style.default, help='hyperlinked text')
|
||||
|
||||
style.viewport = Style(style.default, help='default style of viewports')
|
||||
style.transform = Style(style.default, help='default style of transforms')
|
||||
|
||||
style.list = Style(style.default)
|
||||
style.list_box = Style(style.vbox)
|
||||
@@ -132,8 +133,10 @@ init -1090 python:
|
||||
style.default.strikethrough = False
|
||||
style.default.drop_shadow = None
|
||||
style.default.drop_shadow_color = (0, 0, 0, 255)
|
||||
style.default.outlines = [ ]
|
||||
style.default.minwidth = 0
|
||||
style.default.textalign = 0
|
||||
style.default.text_align = 0
|
||||
style.default.justify = False
|
||||
style.default.text_y_fudge = 0
|
||||
style.default.first_indent = 0
|
||||
style.default.rest_indent = 0
|
||||
@@ -164,7 +167,8 @@ init -1090 python:
|
||||
style.default.ymaximum = None
|
||||
style.default.xoffset = 0
|
||||
style.default.yoffset = 0
|
||||
|
||||
style.default.subpixel = False
|
||||
|
||||
# Sound properties.
|
||||
style.default.sound = None
|
||||
|
||||
@@ -185,6 +189,7 @@ init -1090 python:
|
||||
style.default.left_gutter = 0
|
||||
style.default.right_gutter = 0
|
||||
style.default.thumb_offset = 0
|
||||
style.default.unscrollable = None
|
||||
|
||||
# Misc.
|
||||
style.default.activate_sound = None
|
||||
@@ -260,6 +265,9 @@ init -1090 python:
|
||||
# Viewport
|
||||
style.viewport.clipping = True
|
||||
|
||||
# Transform
|
||||
style.transform.subpixel = True
|
||||
|
||||
# Menu windows.
|
||||
style.mm_root.background = "#000"
|
||||
style.mm_root.xfill = True
|
||||
|
||||
@@ -298,13 +298,19 @@ init -1110 python hide:
|
||||
|
||||
# Compat for the old roundrect theme.
|
||||
window = None,
|
||||
button_menu = True,
|
||||
button_menu = None,
|
||||
rounded_window = True,
|
||||
outline_bars = False,
|
||||
|
||||
mm_root = None,
|
||||
gm_root = None):
|
||||
|
||||
if button_menu is None:
|
||||
if config.script_version < (6, 9, 0):
|
||||
button_menu = True
|
||||
else:
|
||||
button_menu = False
|
||||
|
||||
layout.defaults()
|
||||
|
||||
if config.screen_width <= 640:
|
||||
@@ -480,14 +486,6 @@ init -1110 python hide:
|
||||
style.gm_root.background = "#e9d8bc"
|
||||
|
||||
|
||||
theme.outline_drop_shadow = [
|
||||
(0, -2),
|
||||
(-1, -1), (0, -1), (1, -1),
|
||||
(-2, 0), (-1, 0), (1, 0), (2, 0),
|
||||
(-1, 1), (0, 1), (1, 1),
|
||||
(0, 2)
|
||||
]
|
||||
|
||||
@theme
|
||||
def outline_frames():
|
||||
theme.clear_frames()
|
||||
@@ -507,12 +505,11 @@ init -1110 python hide:
|
||||
|
||||
style.button_text.size = text_size
|
||||
style.button_text.color = inside
|
||||
style.button_text.drop_shadow = theme.outline_drop_shadow
|
||||
style.button_text.drop_shadow_color = idle
|
||||
style.button_text.hover_drop_shadow_color = hover
|
||||
style.button_text.selected_drop_shadow_color = selected
|
||||
style.button_text.selected_hover_drop_shadow_color = hover
|
||||
style.button_text.insensitive_drop_shadow_color = insensitive
|
||||
style.button_text.outlines = [ (2, idle) ]
|
||||
style.button_text.hover_outlines = [ (2, hover) ]
|
||||
style.button_text.selected_outlines = [ (2, selected) ]
|
||||
style.button_text.selected_hover_outlines = [ (2, hover) ]
|
||||
style.button_text.insensitive_outlines = [ (2, insensitive) ]
|
||||
|
||||
|
||||
@theme
|
||||
@@ -534,12 +531,12 @@ init -1110 python hide:
|
||||
|
||||
style.large_button_text.size = text_size
|
||||
style.large_button_text.color = inside
|
||||
style.large_button_text.drop_shadow = theme.outline_drop_shadow
|
||||
style.large_button_text.drop_shadow_color = idle
|
||||
style.large_button_text.hover_drop_shadow_color = hover
|
||||
style.large_button_text.selected_drop_shadow_color = selected
|
||||
style.large_button_text.selected_hover_drop_shadow_color = hover
|
||||
style.large_button_text.insensitive_drop_shadow_color = insensitive
|
||||
|
||||
style.large_button_text.outlines = [ (2, idle) ]
|
||||
style.large_button_text.hover_outlines = [ (2, hover) ]
|
||||
style.large_button_text.selected_outlines = [ (2, selected) ]
|
||||
style.large_button_text.selected_hover_outlines = [ (2, hover) ]
|
||||
style.large_button_text.insensitive_outlines = [ (2, insensitive) ]
|
||||
|
||||
@theme
|
||||
def outline_prompts(
|
||||
@@ -550,8 +547,7 @@ init -1110 python hide:
|
||||
theme.clear_prompts()
|
||||
|
||||
style.prompt_text.color = inside
|
||||
style.prompt_text.drop_shadow = theme.outline_drop_shadow
|
||||
style.prompt_text.drop_shadow_color = prompt
|
||||
style.prompt_text.outlines = [ (2, prompt) ]
|
||||
style.prompt_text.size = text_size
|
||||
|
||||
@theme
|
||||
@@ -563,8 +559,7 @@ init -1110 python hide:
|
||||
theme.clear_labels()
|
||||
|
||||
style.label_text.color = inside
|
||||
style.label_text.drop_shadow = theme.outline_drop_shadow
|
||||
style.label_text.drop_shadow_color = label
|
||||
style.label_text.outlines = [ (2, label) ]
|
||||
style.label_text.size = text_size
|
||||
|
||||
@theme
|
||||
@@ -622,8 +617,6 @@ init -1110 python hide:
|
||||
style.vscrollbar.hover_bottom_bar = Frame(color("vbar", hover), 0, 0)
|
||||
style.vscrollbar.hover_thumb = Frame(color("circle", hover), 0, 7)
|
||||
|
||||
|
||||
|
||||
@theme
|
||||
def outline(
|
||||
inside="#fff",
|
||||
|
||||
@@ -22,7 +22,7 @@ init python:
|
||||
style.prefs_column.box_spacing = 5
|
||||
style.prefs_frame.top_margin = 5
|
||||
|
||||
style.prefs_left.xpos = 100
|
||||
style.prefs_left.xpos = 110
|
||||
style.prefs_center.xpos = 320
|
||||
style.prefs_right.xpos = 530
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
dist/console.exe
|
||||
@@ -1,3 +0,0 @@
|
||||
init -50:
|
||||
$ config.script_version = (6, 7, 1)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
../help.html
|
||||
|
Before Width: | Height: | Size: 19 KiB |
@@ -62,6 +62,15 @@ label demo_movement:
|
||||
|
||||
e "Move can repeat a movement, and even have it bounce back and forth, like I'm doing now."
|
||||
|
||||
|
||||
show eileen happy at Move((.25, 1.0, 0.0, 1.0),
|
||||
(.75, 1.0, 1.0, 1.0),
|
||||
10.0, repeat=True, bounce=True, subpixel=True)
|
||||
|
||||
with dissolve
|
||||
|
||||
e "Our subpixel mode allows slow moves, of less than a few pixels a second, to be very accurate."
|
||||
|
||||
scene bg onememorial at Pan((0, 800), (0, 0), 10.0)
|
||||
with dissolve
|
||||
|
||||
|
||||
@@ -2,41 +2,41 @@
|
||||
# capabilities of Ren'Py.
|
||||
|
||||
init:
|
||||
python:
|
||||
# Register an sfont.
|
||||
$ renpy.register_sfont('new_sfont', 22,
|
||||
filename="new_sfont.png",
|
||||
spacewidth=6)
|
||||
|
||||
# This imports in the asfont22 module, which contains a list of
|
||||
# kerning pairs.
|
||||
import asfont22
|
||||
|
||||
# Register the sfont contained in asfont22 as "subfont".
|
||||
renpy.register_sfont('subfont', 22,
|
||||
filename="asfont22.png",
|
||||
kerns=asfont22.kerns,
|
||||
spacewidth=4)
|
||||
|
||||
# Declare some characters that speak in the subfont.
|
||||
|
||||
# Just use the subfont.
|
||||
$ esub = Character("Eileen",
|
||||
color="#c8ffc8",
|
||||
what_font="subfont",
|
||||
what_black_color="#282")
|
||||
# Declare a character that uses the sfont.
|
||||
$ esfont = Character("Eileen",
|
||||
color="#c8ffc8",
|
||||
what_font="new_sfont")
|
||||
|
||||
# Slow text.
|
||||
$ eslow = Character("Eileen",
|
||||
color="#c8ffc8",
|
||||
what_slow_cps=20)
|
||||
|
||||
|
||||
# Outlined text.
|
||||
$ eoutline = Character("Eileen",
|
||||
color="#c8ffc8",
|
||||
what_outlines=[ (1, "#282") ])
|
||||
|
||||
# Use it in subtitle mode.
|
||||
$ esubtitle = Character(None,
|
||||
what_font="subfont",
|
||||
what_black_color="#282",
|
||||
what_size=28,
|
||||
what_outlines=[(3, "#0008", 2, 2), (3, "#282", 0, 0)],
|
||||
what_layout="subtitle",
|
||||
what_xalign=0.5,
|
||||
what_text_align=0.5,
|
||||
window_background=None,
|
||||
window_yminimum=0,
|
||||
show_say_vbox_properties=dict(xalign=0.5))
|
||||
window_xfill=False,
|
||||
window_xalign=0.5)
|
||||
|
||||
|
||||
# This is used to show the defintion text, by the hyperlink demostration
|
||||
# code.
|
||||
$ definition = Character(None,
|
||||
window_yfill=True,
|
||||
window_xmargin=20,
|
||||
@@ -48,7 +48,7 @@ init:
|
||||
$ style.pink.color = "#ffc0c0"
|
||||
|
||||
label demo_text:
|
||||
|
||||
|
||||
e "Ren'Py gives you quite a bit of control over how text appears."
|
||||
|
||||
e "Text tags let us control the appearance of text that is shown to the user."
|
||||
@@ -86,13 +86,23 @@ label demo_text:
|
||||
|
||||
e "And we want to give you all the power you need."
|
||||
|
||||
esub "For even more control, Ren'Py supports SFonts, image files containing font information."
|
||||
e "There are a couple of text adjustements that don't corrrespond to text tags."
|
||||
|
||||
eoutline "The outlines setting lets you put outlines around the text."
|
||||
|
||||
eoutline "You can have more than one outline, and each has its own color and offset."
|
||||
|
||||
esubtitle "Here, we have two outlines around the white text."
|
||||
|
||||
esubtitle "The bottom one is a translucent black that's offset a little, while the top one is green."
|
||||
|
||||
esub "SFonts let you use fonts you otherwise couldn't, and apply special effects to fonts... like I'm doing now, applying an outline to the font."
|
||||
esubtitle "By hiding the window and adjusting the layout method, we are able to create reasonable subtitles."
|
||||
|
||||
esubtitle "Finally, you can adjust the line breaking algorithm. The subtitle line-breaking algorithm tries to make lines even in length."
|
||||
|
||||
esubtitle "Along with sfonts and the ability to change the text window, this lets us render reasonable subtitles."
|
||||
esubtitle "This might be an interesting look for a game."
|
||||
|
||||
esfont "For even more control, Ren'Py supports SFonts, image files containing font information."
|
||||
|
||||
esfont "SFonts let you use fonts you otherwise couldn't, and apply special effects to fonts using your favorite image editor."
|
||||
|
||||
e "Well, that's it for fonts and text tags."
|
||||
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
# This is the code for the logo example.
|
||||
init python:
|
||||
|
||||
# This spins the logo, while at the same time zooming it and decreasing the
|
||||
# alpha.
|
||||
def logo_transform(t, st, at):
|
||||
|
||||
# Repeat every 10 seconds.
|
||||
st = st % 7.0
|
||||
|
||||
# The move takes 5 seconds.
|
||||
done = min(st / 5.0, 1.0)
|
||||
|
||||
t.xpos = done
|
||||
t.xanchor = 1.0 - done
|
||||
t.ypos = .5
|
||||
t.yanchor = .5
|
||||
t.rotate = 360 * done
|
||||
t.alpha = 1.0 - done
|
||||
t.zoom = 1.0 + done
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
# This is the code for the balls example. It's a bit complicated, but most of
|
||||
# this is the code for ball movement and so on. Only a very little bit of this
|
||||
# actually deals with Ren'Py.
|
||||
init python:
|
||||
import math
|
||||
|
||||
class Ball(object):
|
||||
def __init__(self, filename, x, y, function=None):
|
||||
|
||||
self.transform = Transform(child=filename, xpos=x, ypos=y, xanchor=0.5, yanchor=0.5, rotate=0, function=function)
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
MAX_SPEED = 150
|
||||
|
||||
self.dx = renpy.random.uniform(-MAX_SPEED, MAX_SPEED)
|
||||
self.dy = renpy.random.uniform(-MAX_SPEED, MAX_SPEED)
|
||||
|
||||
# Rotation speed.
|
||||
self.drotate = renpy.random.uniform(0, 180)
|
||||
|
||||
# This is called
|
||||
def balls_collide(p1, p2):
|
||||
"""
|
||||
Check to see if any of the balls are colliding. If they are,
|
||||
then handle the collision.
|
||||
"""
|
||||
|
||||
DOUBLE_RADIUS = 75
|
||||
|
||||
x21 = p2.x - p1.x
|
||||
y21 = p2.y - p1.y
|
||||
|
||||
d = math.hypot(x21, y21)
|
||||
|
||||
# Return if too far.
|
||||
if d > DOUBLE_RADIUS:
|
||||
return
|
||||
|
||||
vx21 = p2.dx - p1.dx
|
||||
vy21 = p2.dy - p1.dy
|
||||
|
||||
# Return if not approaching.
|
||||
if (vx21 * x21 + vy21 * y21) > 0:
|
||||
return
|
||||
|
||||
# Fix divide by zero.
|
||||
if x21 == 0:
|
||||
x21 = .00001
|
||||
|
||||
# Compute the collision.
|
||||
a = y21 / x21
|
||||
dvx2 = -(vx21 + a * vy21) / (1 + a * a)
|
||||
|
||||
p2.dx += dvx2
|
||||
p2.dy += a * dvx2
|
||||
|
||||
p1.dx -= dvx2
|
||||
p2.dy -= a * dvx2
|
||||
|
||||
# This is called by the first transform. It updates all of the
|
||||
# transforms.
|
||||
def balls_update(pilot, st, at):
|
||||
|
||||
global last_time
|
||||
|
||||
RADIUS = 75 / 2
|
||||
LEFT = RADIUS
|
||||
RIGHT = 800 - RADIUS
|
||||
TOP = RADIUS
|
||||
BOTTOM = 600 - RADIUS
|
||||
|
||||
# The pilot is the first ball in our list, and he's the one
|
||||
# that gets last_time updated.
|
||||
if last_time is None:
|
||||
dt = 0
|
||||
else:
|
||||
dt = st - last_time
|
||||
|
||||
last_time = st
|
||||
|
||||
# Handle current collisions.
|
||||
for i in xrange(0, len(balls)):
|
||||
for j in xrange(i + 1, len(balls)):
|
||||
balls_collide(balls[i], balls[j])
|
||||
|
||||
# Basic movement, and bouncing off the walls.
|
||||
for i in balls:
|
||||
|
||||
i.x += i.dx * dt
|
||||
i.y += i.dy * dt
|
||||
|
||||
if i.x < LEFT:
|
||||
i.x = LEFT
|
||||
i.dx = abs(i.dx)
|
||||
|
||||
if i.x > RIGHT:
|
||||
i.x = RIGHT
|
||||
i.dx = -abs(i.dx)
|
||||
|
||||
if i.y < TOP:
|
||||
i.y = TOP
|
||||
i.dy = abs(i.dy)
|
||||
|
||||
if i.y > BOTTOM:
|
||||
i.y = BOTTOM
|
||||
i.dy = -abs(i.dy)
|
||||
|
||||
|
||||
# Update the transforms.
|
||||
for i in balls:
|
||||
|
||||
# This is the code that deals with Ren'Py to update the
|
||||
# various transforms. Note that we use absolute coordinates
|
||||
# to position ourselves with subpixel accuracy.
|
||||
i.transform.xpos = absolute(i.x)
|
||||
i.transform.ypos = absolute(i.y)
|
||||
i.transform.rotate = (i.drotate * st) % 360.0
|
||||
i.transform.update()
|
||||
|
||||
return 0
|
||||
|
||||
# These are used in the button example:
|
||||
init python:
|
||||
def button_transform(t, st, at):
|
||||
t.rotate = (90 * st) % 360.0
|
||||
return 0
|
||||
|
||||
label demo_transform:
|
||||
|
||||
e "The Transform function allows you to rotate, zoom, move, and adjust the alpha of a displayable."
|
||||
|
||||
e "It does this under the control of a Python function, making it incredibly flexible at the cost of some complexity."
|
||||
|
||||
hide eileen
|
||||
with dissolve
|
||||
|
||||
show logo base at Transform(function=logo_transform)
|
||||
|
||||
e "Here's a simple example, showing how we can change an image as it moves around the screen."
|
||||
|
||||
e "A nice thing about Transform is that it's \"one price\"."
|
||||
|
||||
e "If you use it to do a rotation, you can zoom or adjust alpha at no additional cost."
|
||||
|
||||
hide logo base
|
||||
with dissolve
|
||||
|
||||
python:
|
||||
|
||||
last_time = None
|
||||
|
||||
# Define a list of ball objects.
|
||||
balls = [
|
||||
Ball("eileen_orb.png", 200, 150, function=balls_update),
|
||||
Ball("lucy_orb.png", 400, 150),
|
||||
Ball("eileen_orb.png", 600, 150),
|
||||
|
||||
Ball("lucy_orb.png", 200, 300),
|
||||
Ball("lucy_orb.png", 600, 300),
|
||||
|
||||
Ball("eileen_orb.png", 200, 450),
|
||||
Ball("lucy_orb.png", 400, 450),
|
||||
Ball("eileen_orb.png", 600, 450),
|
||||
]
|
||||
|
||||
# Add each ball's transform to the screen.
|
||||
for i, b in enumerate(balls):
|
||||
renpy.show("ball%d" % i, what=b.transform)
|
||||
|
||||
with dissolve
|
||||
|
||||
e "As the python functions get more complicated, more advanced behavior is possible."
|
||||
|
||||
e "This can include coordinating more than one Transform."
|
||||
|
||||
python:
|
||||
for i, b in enumerate(balls):
|
||||
renpy.hide("ball%d" % i)
|
||||
|
||||
|
||||
with dissolve
|
||||
|
||||
python hide:
|
||||
ui.transform(function=button_transform, xalign=0.5, yalign=0.5)
|
||||
ui.textbutton("A Working Button", clicked=ui.returns(True))
|
||||
|
||||
e "Finally, transforms can be applied to buttons, and work even while the button is zoomed."
|
||||
|
||||
show eileen happy
|
||||
with dissolve
|
||||
|
||||
e "With a little Python code, transforms let you do a lot of things."
|
||||
|
||||
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -8,14 +8,14 @@ init:
|
||||
("demo_basics", "Basic Scripting", "5.6.3"),
|
||||
("demo_experience", "User Experience", "5.6.3"),
|
||||
("demo_transitions", "Transitions", "5.6.6"),
|
||||
("demo_movement", "Positions and Movement", "6.7.0"),
|
||||
("demo_movement", "Positions and Movement", "6.9.0"),
|
||||
("demo_animation", "Animation", "6.2.0"),
|
||||
("demo_multimedia", "Music and Sound", "5.6.3"),
|
||||
("demo_imageops", "Image Operations", "6.5.0"),
|
||||
("demo_ui", "User Interaction", "6.5.0"),
|
||||
("demo_text", "Fonts and Text Tags", "6.6.2"),
|
||||
("demo_text", "Fonts and Text Tags", "6.8.0"),
|
||||
("demo_character", "Character Objects", "6.2.0"),
|
||||
("demo_advanced", "Advanced Features", "6.7.0"),
|
||||
("demo_advanced", "Advanced Features", "6.9.0"),
|
||||
]
|
||||
|
||||
# Update above with this!
|
||||
@@ -25,6 +25,7 @@ init:
|
||||
("demo_dynamic", "Dynamic Displayables", "5.6.3"),
|
||||
("demo_minigame", "Minigames", "6.3.2"),
|
||||
("demo_persistent", "Persistent Data", "6.7.0"),
|
||||
("demo_transform", "Transform", "6.9.0"),
|
||||
]
|
||||
|
||||
def demos_show(demos_info, nevermind):
|
||||
|
||||
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 33 KiB |
@@ -194,7 +194,20 @@ init -1 python hide:
|
||||
|
||||
# config.main_menu_music = "main_menu_theme.ogg"
|
||||
|
||||
|
||||
#########################################
|
||||
## Help.
|
||||
|
||||
## This lets you configure the help option on the Ren'Py menus.
|
||||
## It may be:
|
||||
## - A label in the script, in which case that label is called to
|
||||
## show help to the user.
|
||||
## - A file name relative to the base directory, which is opened in a
|
||||
## web browser.
|
||||
## - None, to disable help.
|
||||
config.help = "README.html"
|
||||
|
||||
|
||||
#########################################
|
||||
## Transitions.
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ def main():
|
||||
os.chmod("./renpy.app/Contents/MacOS/Ren'Py Launcher", 0755)
|
||||
|
||||
# Compile the various games
|
||||
for i in [ 'demo/game', 'data', 'template/game', 'the_question/game' ]:
|
||||
for i in [ 'demo/game', 'launcher', 'template/game', 'the_question/game' ]:
|
||||
os.system("./renpy.sh --compile --game " + i)
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ def main():
|
||||
files.append("LICENSE.txt")
|
||||
files.extend(tree("common"))
|
||||
more_files.append("console.exe")
|
||||
files.extend(tree("data"))
|
||||
files.extend(tree("launcher"))
|
||||
files.extend(tree("demo"))
|
||||
# files.extend(tree("dse"))
|
||||
files.extend(tree("the_question"))
|
||||
@@ -150,6 +150,7 @@ def main():
|
||||
|
||||
# files.extend(tree("extras"))
|
||||
more_files.extend(tree("lib"))
|
||||
more_files.extend(tree("lib/linux-x86"))
|
||||
|
||||
module_files = [
|
||||
"lib/pysdlsound/linmixer.py",
|
||||
@@ -177,6 +178,9 @@ def main():
|
||||
"winmixer.pyx",
|
||||
"IMG_savepng.c",
|
||||
"IMG_savepng.h",
|
||||
"renpy_font.c",
|
||||
"renpy_ttf.c",
|
||||
"renpy_ttf.h",
|
||||
]
|
||||
|
||||
for i in module_files:
|
||||
@@ -231,7 +235,7 @@ def main():
|
||||
|
||||
print
|
||||
print "Did you remember to rebuild the exe after the last change?"
|
||||
print "Did you run me with renpython -O?"
|
||||
print "Did you run me with renpython -OO?"
|
||||
print "Was ming using the right crt?"
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -63,7 +63,7 @@ init -100 python:
|
||||
if name:
|
||||
layout.label(s.name, "stat")
|
||||
else:
|
||||
ui.nul()
|
||||
ui.null()
|
||||
|
||||
if value and max:
|
||||
layout.label("%d/%d" % (v, s.max), "stat_value")
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>README</title>
|
||||
<style>
|
||||
HTML {
|
||||
background: #eed;
|
||||
}
|
||||
|
||||
BODY {
|
||||
background: #fff;
|
||||
margin: 0px 10% 0px 10%;
|
||||
border-left: 1px solid #888;
|
||||
border-right: 1px solid #888;
|
||||
padding-left: 4em;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
DT {
|
||||
font-weight: bold;
|
||||
margin-top: .33em;
|
||||
}
|
||||
|
||||
H3 {
|
||||
color: #600;
|
||||
margin-left: -2.5em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="renpy_help"></div>
|
||||
<p><a name="Basic_Help" id="Basic_Help"></a></p>
|
||||
<h3><span class="mw-headline">Basic Help</span></h3>
|
||||
<p>To advance through the game, <tt>left-click</tt> or press the <tt>space</tt> or <tt>enter</tt> keys. When at a menu, <tt>left-click</tt> to make a choice, or use the arrow keys to select a choice and <tt>enter</tt> to activate it.</p>
|
||||
<p><a name="Game_Menu" id="Game_Menu"></a></p>
|
||||
<h3><span class="mw-headline">Game Menu</span></h3>
|
||||
<p>When playing a game, <tt>right-click</tt> or press the <tt>escape</tt> key to enter the game menu. The game menu gives the following choices:</p>
|
||||
<dl>
|
||||
<dt>Return</dt>
|
||||
<dd>Returns to the game.</dd>
|
||||
<dt>Save Game</dt>
|
||||
<dd>Allows you to save a game by clicking on a save slot.</dd>
|
||||
<dt>Load Game</dt>
|
||||
<dd>Allows you to load a game by clicking on a save slot. Clicking on "Auto" accesses the automatic save slots.</dd>
|
||||
<dt>Preferences</dt>
|
||||
<dd>Changes the game preferences (options/configuration):
|
||||
<dl>
|
||||
<dt>Display</dt>
|
||||
<dd>Switches between fullscreen and windowed mode.</dd>
|
||||
<dt>Transitions</dt>
|
||||
<dd>Controls the display of transitions between game screens.</dd>
|
||||
<dt>Text Speed</dt>
|
||||
<dd>Controls the rate at which text displays. The further to the right this slider is, the faster the text will display. All the way to the right causes text to be shown instantly.</dd>
|
||||
<dt>Joystick</dt>
|
||||
<dd>Lets you control the game using a joystick.</dd>
|
||||
<dt>Skip</dt>
|
||||
<dd>Chooses between skipping messages that have been already seen (in any play through the game), and skipping all messages.</dd>
|
||||
<dt>Begin Skipping</dt>
|
||||
<dd>Returns to the game, while skipping.</dd>
|
||||
<dt>After Choices</dt>
|
||||
<dd>Controls if skipping stops upon reaching a menu.</dd>
|
||||
<dt>Auto-Forward Time</dt>
|
||||
<dd>Controls automatic advance. The further to the left this slider is, the shorter the amount of time before the game advances. All the way to the right means text will never auto-forward.</dd>
|
||||
<dt>Music, Sound, and Voice Volume</dt>
|
||||
<dd>Controls the volume of the Music, Sound effect, and Voice channels, respectively. The further to the right these are, the louder the volume.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt>Main Menu</dt>
|
||||
<dd>Returns to the main menu, ending the current game.</dd>
|
||||
<dt>Help</dt>
|
||||
<dd>Shows this help screen.</dd>
|
||||
<dt>Quit</dt>
|
||||
<dd>Exits the game; the game will be closed and ended.</dd>
|
||||
</dl>
|
||||
<p><a name="Key_and_Mouse_Bindings" id="Key_and_Mouse_Bindings"></a></p>
|
||||
<h3><span class="mw-headline">Key and Mouse Bindings</span></h3>
|
||||
<dl>
|
||||
<dt>Left-click, Enter</dt>
|
||||
<dd>Advances through the game, activates menu choices, buttons, and sliders.</dd>
|
||||
<dt>Space</dt>
|
||||
<dd>Advances through the game, but does not activate choices.</dd>
|
||||
<dt>Arrow Keys</dt>
|
||||
<dd>Selects menu choices, buttons, and sliders.</dd>
|
||||
<dt>Ctrl</dt>
|
||||
<dd>Causes skipping to occur while the ctrl key is held down.</dd>
|
||||
<dt>Tab</dt>
|
||||
<dd>Toggles skipping, causing it to occur until tab is pressed again.</dd>
|
||||
<dt>Mousewheel-Up, PageUp</dt>
|
||||
<dd>Causes rollback to occur. Rollback reverses the game back in time, showing prior text and even allowing menu choices to be changed.</dd>
|
||||
<dt>Mousewheel-Down, PageDown</dt>
|
||||
<dd>Causes rollforward to occur, cancelling out a previous rollback.</dd>
|
||||
<dt>Right-click, Escape</dt>
|
||||
<dd>Enters the game menu. When in the game menu, returns to the game.</dd>
|
||||
<dt>Middle-click, H</dt>
|
||||
<dd>Hides the text window and other transient displays.</dd>
|
||||
<dt>F</dt>
|
||||
<dd>Toggles fullscreen mode</dd>
|
||||
<dt>S</dt>
|
||||
<dd>Takes a screenshot, saving it in a file named screenshotxxxx.png, where xxxx is a serial number.</dd>
|
||||
<dt>Alt-H, Command-H</dt>
|
||||
<dd>Hides (iconifies) the window.</dd>
|
||||
<dt>Alt-F4, Command-Q</dt>
|
||||
<dd>Quits the game.</dd>
|
||||
</dl>
|
||||
</body>
|
||||
</html>
|
||||
@@ -9,16 +9,34 @@ init python:
|
||||
import sys
|
||||
import zlib
|
||||
|
||||
import pefile
|
||||
|
||||
# Returns true if a file or directory should not be included in
|
||||
# the distribution
|
||||
|
||||
|
||||
ignored_files = ("thumbs.db",
|
||||
"launcherinfo.py",
|
||||
"traceback.txt",
|
||||
"errors.txt",
|
||||
"completion.lua",)
|
||||
"icon.ico",
|
||||
"icon.icns"
|
||||
)
|
||||
|
||||
def ignored(fn):
|
||||
|
||||
root_ignored_files = (
|
||||
"common",
|
||||
"renpy",
|
||||
"renpy.code",
|
||||
"python23.dll",
|
||||
"python24.dll",
|
||||
"python25.dll",
|
||||
"msvcr71.dll",
|
||||
"lib",
|
||||
"iliad-icon.png",
|
||||
"manifest.xml"
|
||||
)
|
||||
|
||||
def ignored(fn, dir, dest, root):
|
||||
if fn[0] == ".":
|
||||
return True
|
||||
|
||||
@@ -35,13 +53,21 @@ init python:
|
||||
if fn == "archived":
|
||||
return True
|
||||
|
||||
if root and dir == dest:
|
||||
if fn.endswith(".py") or fn.endswith(".sh") or fn.endswith(".app"):
|
||||
return True
|
||||
|
||||
if fn in root_ignored_files:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def tree(src, dest,
|
||||
exclude_suffix=[ ".pyc", "~", ".bak" ],
|
||||
exclude_prefix=[ ".", '#' ],
|
||||
exclude=ignored_files,
|
||||
exclude_func=None,
|
||||
exclude_func=ignored,
|
||||
root=False,
|
||||
):
|
||||
|
||||
# Get rid of trailing slashes.
|
||||
@@ -52,7 +78,7 @@ init python:
|
||||
dest = dest[:-1]
|
||||
|
||||
# What should we include?
|
||||
def include(fn):
|
||||
def include(fn, destdir, dest):
|
||||
for i in exclude_suffix:
|
||||
if fn.endswith(i):
|
||||
return False
|
||||
@@ -65,7 +91,7 @@ init python:
|
||||
if i == fn.lower():
|
||||
return False
|
||||
|
||||
if exclude_func and exclude_func(fn):
|
||||
if exclude_func and exclude_func(fn, destdir, dest, root):
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -86,7 +112,7 @@ init python:
|
||||
|
||||
for fn in files:
|
||||
|
||||
if not include(fn):
|
||||
if not include(fn, destdir, dest):
|
||||
continue
|
||||
|
||||
sfn = srcdir + "/" + fn
|
||||
@@ -95,10 +121,12 @@ init python:
|
||||
dfn = destdir + "/" + fn
|
||||
else:
|
||||
dfn = fn
|
||||
|
||||
dfn = dfn.replace("\\", "/")
|
||||
|
||||
rv.append((sfn, dfn))
|
||||
|
||||
dirs[:] = [ i for i in dirs if include(i) ]
|
||||
dirs[:] = [ i for i in dirs if include(i, destdir, dest) ]
|
||||
|
||||
return rv
|
||||
|
||||
@@ -135,12 +163,17 @@ label distribute:
|
||||
else:
|
||||
linux = False
|
||||
|
||||
if os.path.exists(config.renpy_base + "/lib/linux-iliad"):
|
||||
iliad = True
|
||||
else:
|
||||
iliad = False
|
||||
|
||||
if os.path.exists(config.renpy_base + "/renpy.app"):
|
||||
mac = True
|
||||
else:
|
||||
mac = False
|
||||
|
||||
if not (windows or mac or linux):
|
||||
|
||||
if not (windows or mac or linux or iliad):
|
||||
store.error(u"Can't Distribute", u"Ren'Py is missing files required for distribution. Please download the full package from {a=http://www.renpy.org/}www.renpy.org{/a}.", "tools_menu")
|
||||
|
||||
lint()
|
||||
@@ -173,11 +206,14 @@ label distribute:
|
||||
spacer()
|
||||
|
||||
if windows:
|
||||
text(u"Windows 98+", style='launcher_input')
|
||||
text(u"Windows 2000+", style='launcher_input')
|
||||
|
||||
if linux:
|
||||
text(u"Linux x86", style='launcher_input')
|
||||
|
||||
if iliad:
|
||||
text(u"iLiad", style='launcher_input')
|
||||
|
||||
if mac:
|
||||
text(u"Mac OS X 10.4+", style='launcher_input')
|
||||
|
||||
@@ -197,9 +233,7 @@ label distribute:
|
||||
if not interact():
|
||||
renpy.jump("tools")
|
||||
|
||||
default_name = project.name
|
||||
if persistent.build_project == project.name:
|
||||
default_name = persistent.build_name
|
||||
default_name = project.info.get('distribution_base', project.name)
|
||||
|
||||
name = prompt(u"Building Distributions",
|
||||
u"Please enter a base name for the directories making up this distribution.",
|
||||
@@ -217,14 +251,14 @@ label distribute:
|
||||
except:
|
||||
store.error(u"Error", u"Distribution names must be ASCII. This is because archive file formats do not support non-ASCII characters in a uniform way.", "tools_menu")
|
||||
|
||||
persistent.build_project = project.name
|
||||
persistent.build_name = name
|
||||
project.info['distribution_base'] = name
|
||||
|
||||
ignore_extensions = persistent.ignore_extensions or "~ .bak"
|
||||
ignore_extensions = project.info.get('ignore_extensions', "~ .bak")
|
||||
ignore_extensions = prompt(u"Building Distributions", u"Please enter a space separated list of the file extensions you do not want included in the distribution.", "tools", ignore_extensions)
|
||||
persistent.ignore_extensions = ignore_extensions
|
||||
store.ignore_extensions = [ i.strip() for i in ignore_extensions.strip().split() ]
|
||||
project.info['ignore_extensions'] = ignore_extensions
|
||||
|
||||
project.save()
|
||||
|
||||
# Figure out the files that will make up the distribution.
|
||||
|
||||
@@ -244,24 +278,26 @@ label distribute:
|
||||
exclude_suffix = [ ],
|
||||
exclude_prefix = [ ],
|
||||
exclude=[ ],
|
||||
exclude_func = project.info["ignored"]))
|
||||
root=True))
|
||||
|
||||
shortgamedir = project.gamedir[len(project.path)+1:]
|
||||
|
||||
for i in store.ignore_extensions:
|
||||
if "script_version.rpy".endswith(i):
|
||||
break
|
||||
else:
|
||||
multi.append((config.gamedir + "/script_version.rpy",
|
||||
shortgamedir + "/script_version.rpy"))
|
||||
|
||||
for i in store.ignore_extensions:
|
||||
if "script_version.rpyc".endswith(i):
|
||||
break
|
||||
else:
|
||||
multi.append((config.gamedir + "/script_version.rpyc",
|
||||
shortgamedir + "/script_version.rpyc"))
|
||||
|
||||
def add_script_version(fn, multi=multi, shortgamedir=shortgamedir):
|
||||
|
||||
for a, b in multi:
|
||||
if b == shortgamedir + "/" + fn:
|
||||
return
|
||||
|
||||
for i in store.ignore_extensions:
|
||||
if fn.endswith(i):
|
||||
return
|
||||
|
||||
multi.append((config.gamedir + "/" + fn, shortgamedir + "/" + fn))
|
||||
|
||||
add_script_version("script_version.rpy")
|
||||
add_script_version("script_version.rpyc")
|
||||
|
||||
# renpy.py
|
||||
multi.append((config.renpy_base + "/renpy.py",
|
||||
project.name + ".py"))
|
||||
@@ -275,6 +311,13 @@ label distribute:
|
||||
( config.renpy_base + "/msvcr71.dll", "msvcr71.dll" ),
|
||||
]
|
||||
|
||||
win_data = { }
|
||||
if os.path.exists(project.path + "/icon.ico"):
|
||||
win_data[project.name + ".exe"] = pefile.change_icons(
|
||||
config.renpy_base + "/renpy.exe",
|
||||
project.path + "/icon.ico",
|
||||
)
|
||||
|
||||
zf = zipfile.ZipFile(name + ".zip", "w", zipfile.ZIP_DEFLATED)
|
||||
|
||||
progress_len = len(multi) + len(win_files)
|
||||
@@ -296,6 +339,8 @@ label distribute:
|
||||
zi.external_attr = long(0100666) << 16
|
||||
data = file(fn, "rb").read()
|
||||
|
||||
data = win_data.get(an, data)
|
||||
|
||||
zf.writestr(zi, data)
|
||||
|
||||
|
||||
@@ -306,7 +351,7 @@ label distribute:
|
||||
if linux:
|
||||
|
||||
linux_files = [
|
||||
(config.renpy_base + "/lib", "lib"),
|
||||
(config.renpy_base + "/lib/linux-x86", "lib/linux-x86"),
|
||||
(config.renpy_base + "/renpy.sh", project.name + ".sh"),
|
||||
(config.renpy_base + "/lib/python", "lib/python"),
|
||||
]
|
||||
@@ -314,7 +359,6 @@ label distribute:
|
||||
|
||||
linux_files.extend(tree(config.renpy_base + "/lib/linux-x86", "lib/linux-x86"))
|
||||
|
||||
|
||||
tf = tarfile.open(name + "-linux-x86.tar.bz2", "w:bz2")
|
||||
tf.dereference = True
|
||||
|
||||
@@ -355,11 +399,81 @@ label distribute:
|
||||
|
||||
tf.close()
|
||||
|
||||
|
||||
# iLiad Tar Bz2
|
||||
if iliad:
|
||||
|
||||
iliad_files = [
|
||||
(config.renpy_base + "/lib", "lib"),
|
||||
(config.renpy_base + "/renpy-iliad.sh", project.name + ".sh"),
|
||||
(config.renpy_base + "/lib/python", "lib/python"),
|
||||
(config.renpy_base + "/manifest.xml", "manifest.xml"),
|
||||
(config.renpy_base + "/iliad-icon.png", "iliad-icon.png"),
|
||||
]
|
||||
|
||||
iliad_files.extend(tree(config.renpy_base + "/lib/linux-iliad", "lib/linux-iliad"))
|
||||
|
||||
iliad_data = { }
|
||||
|
||||
manifest = file(config.renpy_base + "/manifest.xml", "r").read()
|
||||
manifest = manifest.replace("GAMENAME", project.name)
|
||||
iliad_data["manifest.xml"] = manifest
|
||||
|
||||
zf = zipfile.ZipFile(name + "-iLiad.zip", "w", zipfile.ZIP_DEFLATED)
|
||||
|
||||
progress_len = len(multi) + len(iliad_files)
|
||||
store.message = u"We thank Hixbooks for sponsoring iLiad support."
|
||||
|
||||
for i, (fn, an) in enumerate(multi + iliad_files):
|
||||
|
||||
progress(u"Building iLiad", i, progress_len)
|
||||
|
||||
if os.path.isdir(fn):
|
||||
continue
|
||||
|
||||
zi = zipfile.ZipInfo(project.name + "/" + an)
|
||||
|
||||
s = os.stat(fn)
|
||||
zi.date_time = time.gmtime(s.st_mtime)[:6]
|
||||
zi.compress_type = zipfile.ZIP_DEFLATED
|
||||
zi.create_system = 3
|
||||
|
||||
for ext in [ ".sh", ".so", "python", "python.real" ]:
|
||||
if fn.endswith(ext):
|
||||
zi.external_attr = long(0100777) << 16
|
||||
data = file(fn, "rb").read()
|
||||
break
|
||||
else:
|
||||
zi.external_attr = long(0100666) << 16
|
||||
data = file(fn, "rb").read()
|
||||
|
||||
data = iliad_data.get(an, data)
|
||||
zf.writestr(zi, data)
|
||||
|
||||
zf.close()
|
||||
|
||||
if mac:
|
||||
|
||||
mac_files = tree(config.renpy_base + "/renpy.app",
|
||||
project.name + ".app")
|
||||
|
||||
mac_files = [ (fn, an.replace("Ren'Py Launcher", project.name)) for (fn, an) in mac_files ]
|
||||
|
||||
# Data replacement for macintosh.
|
||||
mac_data = { }
|
||||
|
||||
quoted_name = project.name.replace("&", "&").replace("<", "<")
|
||||
info_plist = file(config.renpy_base + "/renpy.app/Contents/Info.plist", "rb").read().replace("Ren'Py Launcher", quoted_name)
|
||||
mac_data[project.name + ".app/Contents/Info.plist"] = info_plist
|
||||
|
||||
quoted_name = project.name.replace("\"", "\\\"")
|
||||
launcher_py = file(config.renpy_base + "/renpy.app/Contents/Resources/launcher.py", "rb").read().replace("Ren'Py Launcher", quoted_name)
|
||||
mac_data[project.name + ".app/Contents/Resources/launcher.py"] = launcher_py
|
||||
|
||||
if os.path.exists(project.path + "/icon.icns"):
|
||||
icon_data = file(project.path + "/icon.icns", "rb").read()
|
||||
mac_data[project.name + ".app/Contents/Resources/launcher.icns"] = icon_data
|
||||
|
||||
zf = zipfile.ZipFile(name + "-mac.zip", "w", zipfile.ZIP_DEFLATED)
|
||||
|
||||
progress_len = len(multi) + len(mac_files)
|
||||
@@ -371,7 +485,6 @@ label distribute:
|
||||
|
||||
if os.path.isdir(fn):
|
||||
continue
|
||||
|
||||
|
||||
zi = zipfile.ZipInfo(name + "-mac/" + an)
|
||||
|
||||
@@ -387,6 +500,7 @@ label distribute:
|
||||
zi.external_attr = long(0100666) << 16
|
||||
data = file(fn, "rb").read()
|
||||
|
||||
data = mac_data.get(an, data)
|
||||
zf.writestr(zi, data)
|
||||
|
||||
zf.close()
|
||||
@@ -4,11 +4,12 @@
|
||||
init:
|
||||
python hide:
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
if not config.editor:
|
||||
|
||||
editor = config.renpy_base + "/jedit/jedit.jar"
|
||||
editor = os.path.normpath(config.renpy_base + "/jedit/jedit.jar")
|
||||
editor = renpy.shell_escape(editor)
|
||||
|
||||
if sys.platform == 'win32':
|
||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
@@ -36,6 +36,13 @@ init:
|
||||
|
||||
style.hyperlink_text.color = "#0ff"
|
||||
|
||||
config.underlay = [
|
||||
renpy.Keymap(
|
||||
quit = renpy.quit_event,
|
||||
iconify = renpy.iconify,
|
||||
)
|
||||
]
|
||||
|
||||
def quote(c):
|
||||
n = ord(c)
|
||||
if 0x20 <= n <= 0x7f:
|
||||
@@ -58,13 +65,14 @@ init:
|
||||
ui.add(DynamicDisplayable("Text(_(store.message), size=14, color='#fff', xpos=100, ypos=375, xmaximum=240)"))
|
||||
ui.text(renpy.version(), xalign=0, yalign=1.0, size=12, color='#000')
|
||||
|
||||
return ui.interact(suppress_underlay=True)
|
||||
return ui.interact()
|
||||
|
||||
def title(name):
|
||||
ui.window(style='mm_root')
|
||||
ui.null()
|
||||
|
||||
ui.frame(xminimum=350, yminimum=370)
|
||||
ui.viewport(child_size=(2000, None), draggable=True, ymaximum=34)
|
||||
_label_factory(name, "launcher_title", size=28)
|
||||
|
||||
# ui.text(name, xpos=4, ypos=4, size=30, color="#559")
|
||||
@@ -93,7 +101,7 @@ init:
|
||||
|
||||
# ui.button(clicked=clicked, hovered=hovered)
|
||||
# ui.text(text, style="button_text")
|
||||
_button_factory(text, 'launcher', clicked=clicked, hovered=hovered, selected=selected)
|
||||
_button_factory(text, 'launcher', clicked=clicked, hovered=hovered, selected=selected, xmaximum=200)
|
||||
|
||||
|
||||
def prompt(name, message, cancel, default='', hint=''):
|
||||
@@ -151,6 +159,8 @@ init:
|
||||
mid(focus="paged_menu")
|
||||
|
||||
for name, desc, ret, hovered, selected in choices[page * per_page:(page + 1) * per_page]:
|
||||
if len(name) > 19:
|
||||
name = name[:18] + u"\u2026"
|
||||
button(name, desc, clicked=ui.returns(("return", ret)), hovered=hovered, selected=selected)
|
||||
|
||||
ui.close()
|
||||
@@ -221,7 +231,21 @@ init:
|
||||
title = re.sub(r'\s+\S', upper, title)
|
||||
self.title = title
|
||||
|
||||
def save(self):
|
||||
import os
|
||||
|
||||
f = file(self.path + "/" + "launcherinfo.py.new", "w")
|
||||
for k, v in self.info.iteritems():
|
||||
f.write("%s = %r\n" % (k, v))
|
||||
f.close()
|
||||
|
||||
try:
|
||||
os.rename(self.path + "/launcherinfo.py.new", self.path + "/launcherinfo.py")
|
||||
except:
|
||||
os.unlink(self.path + "/launcherinfo.py")
|
||||
os.rename(self.path + "/launcherinfo.py.new", self.path + "/launcherinfo.py")
|
||||
|
||||
|
||||
def load_project(dir, name):
|
||||
import os.path
|
||||
|
||||
@@ -241,7 +265,6 @@ init:
|
||||
|
||||
info = dict()
|
||||
info["description"] = desc
|
||||
info["ignored"] = ignored
|
||||
|
||||
if os.path.exists(dir + "/launcherinfo.py"):
|
||||
source = file(dir + "/launcherinfo.py", "rU").read().decode("utf8")
|
||||
@@ -251,6 +274,8 @@ init:
|
||||
|
||||
exec source in info
|
||||
|
||||
del info["__builtins__"]
|
||||
|
||||
store.projects.append(Project(name, dir, gamedir, info))
|
||||
|
||||
def load_projects():
|
||||
@@ -273,6 +298,7 @@ init:
|
||||
contents.sort()
|
||||
|
||||
for dd in contents:
|
||||
|
||||
dir = d + "/" + dd
|
||||
|
||||
if os.path.isdir(dir):
|
||||
@@ -482,7 +508,8 @@ label edit:
|
||||
files.remove(i)
|
||||
files.insert(0, i)
|
||||
|
||||
renpy.launch_editor(files)
|
||||
if not renpy.launch_editor(files):
|
||||
error(u"Error", u"Launching the editor failed. You may need Java, which can be downloaded for free from {a=http://www.java.com}java.com{/a}.", "start")
|
||||
|
||||
store.message = _(u"Launched editor with %d script files.") % len(files)
|
||||
|
||||
@@ -504,10 +531,11 @@ label game_directory:
|
||||
import renpy.subprocess as subprocess
|
||||
subprocess.Popen([ "open", gamedir ])
|
||||
else:
|
||||
store.message = _(u"Opening the game directory is not supported on this platform.\n%s") % gamedir
|
||||
import renpy.subprocess as subprocess
|
||||
subprocess.Popen([ "xdg-open", gamedir ])
|
||||
|
||||
|
||||
gamedir = "".join(quote(i) for i in gamedir)
|
||||
|
||||
gamedir = "".join(quote(i) for i in gamedir)
|
||||
store.message = _(u"Opening game directory:\n%s") % gamedir
|
||||
|
||||
jump top_menu
|
||||
@@ -587,21 +615,23 @@ label archive_files:
|
||||
|
||||
import renpy.tools.archiver as archiver
|
||||
|
||||
extensions = persistent.archive_extensions or "*.png *.gif *.jpg"
|
||||
extensions = project.info.get('archive_extensions', "*.png *.gif *.jpg")
|
||||
extensions = prompt(u"Archiving Files", u"Please enter a space separated list of the file patterns you want archived.", "tools", extensions)
|
||||
if not extensions.strip():
|
||||
renpy.jumps("tools_menu")
|
||||
persistent.archive_extensions = extensions
|
||||
project.info['archive_extensions'] = extensions
|
||||
extensions = [ i.strip() for i in extensions.strip().split() ]
|
||||
|
||||
|
||||
base = persistent.archive_base or "data"
|
||||
base = project.info.get('archive_base', "data")
|
||||
base = prompt(u"Archiving Files", u"Please enter the name of the archive file, without the .rpa extension.", "tools", base)
|
||||
base = base.strip()
|
||||
if not base:
|
||||
renpy.jumps("tools_menu")
|
||||
persistent.archive_base = base
|
||||
project.info['archive_base'] = base
|
||||
|
||||
project.save()
|
||||
|
||||
# Tell the user we're archiving images.
|
||||
title(u"Archiving Files")
|
||||
store.message = u"Please wait while we archive files."
|
||||
@@ -625,6 +655,8 @@ label archive_files:
|
||||
|
||||
if fn == "presplash.png":
|
||||
return False
|
||||
if fn == "presplash.jpg":
|
||||
return False
|
||||
|
||||
for e in extensions:
|
||||
if fnmatch.fnmatch(fn, e):
|
||||
@@ -0,0 +1,332 @@
|
||||
|
||||
# http://www.csn.ul.ie/~caolan/publink/winresdump/winresdump/doc/pefile.html
|
||||
# Contains a reasonable description of the format.
|
||||
|
||||
import struct
|
||||
import sys
|
||||
import array
|
||||
|
||||
# The starting point of the resource segment in the file.
|
||||
RESOURCE_BASE=0x3400
|
||||
|
||||
# The virtual memory location the resource segment is loaded
|
||||
# to.
|
||||
RESOURCE_VIRTUAL=0x6000
|
||||
|
||||
# The offset of the field that tells us with the alignment need be.
|
||||
ALIGNMENT_FIELD = 0x138
|
||||
|
||||
# Locations in the file where we need to patch in the resource
|
||||
# segment length.
|
||||
RESOURCE_LENGTH_PATCHES = [ 0x018c, 0x278 ]
|
||||
|
||||
# Locations in the file where we need to patch in the padded
|
||||
# resource segment length.
|
||||
RESOURCE_PADDED_PATCHES = [ 0x280 ]
|
||||
|
||||
# Locations in the file where we need to patch in the change in the
|
||||
# size of the resource segement (and hence the change in the file's
|
||||
# total size.)
|
||||
SIZE_DELTA_PATCHES = [ 0x150 ]
|
||||
|
||||
# A location in the file that will be checked to be sure it matches
|
||||
# RESOURCE_BASE.
|
||||
CHECK_ADDRESS = 0x284
|
||||
|
||||
# This class performs various operations on memory-loaded binary files,
|
||||
# including modifications.
|
||||
class BinFile(object):
|
||||
|
||||
def set_u32(self, addr, value):
|
||||
self.a[addr+0] = (value >> 0) & 0xff
|
||||
self.a[addr+1] = (value >> 8) & 0xff
|
||||
self.a[addr+2] = (value >> 16) & 0xff
|
||||
self.a[addr+3] = (value >> 24) & 0xff
|
||||
|
||||
def u32(self):
|
||||
addr = self.addr
|
||||
rv = self.a[addr]
|
||||
rv |= self.a[addr+1] << 8
|
||||
rv |= self.a[addr+2] << 16
|
||||
rv |= self.a[addr+3] << 24
|
||||
self.addr += 4
|
||||
return rv
|
||||
|
||||
def u16(self):
|
||||
addr = self.addr
|
||||
rv = self.a[addr]
|
||||
rv |= self.a[addr+1] << 8
|
||||
self.addr += 2
|
||||
return rv
|
||||
|
||||
def u8(self):
|
||||
rv = self.a[self.addr]
|
||||
self.addr += 1
|
||||
return rv
|
||||
|
||||
def name(self):
|
||||
c = self.u16()
|
||||
|
||||
rv = u""
|
||||
for i in range(c):
|
||||
rv += unichr(self.u16())
|
||||
|
||||
return rv
|
||||
|
||||
def seek(self, addr):
|
||||
self.addr = addr
|
||||
|
||||
def tostring(self):
|
||||
return self.a.tostring()
|
||||
|
||||
def substring(self, start, len):
|
||||
return self.a[start:start+len].tostring()
|
||||
|
||||
def __init__(self, fn):
|
||||
data = open(fn, "rb").read()
|
||||
self.a = array.array('B')
|
||||
self.a.fromstring(data)
|
||||
|
||||
##############################################################################
|
||||
# These functions parse data out of the file. In these functions, offset is
|
||||
# relative to the start of the file.
|
||||
|
||||
# This parses a data block out of the resources.
|
||||
def parse_data(offset):
|
||||
pe.seek(RESOURCE_BASE + offset)
|
||||
data_offset = pe.u32()
|
||||
data_len = pe.u32()
|
||||
code_page = pe.u32()
|
||||
pe.u32()
|
||||
|
||||
l = [ ]
|
||||
|
||||
pe.seek(data_offset + RESOURCE_BASE - RESOURCE_VIRTUAL)
|
||||
for i in range(data_len):
|
||||
l.append(chr(pe.u8()))
|
||||
|
||||
return (code_page, "".join(l))
|
||||
|
||||
# This parses a resource directory.
|
||||
def parse_directory(offset):
|
||||
|
||||
pe.seek(RESOURCE_BASE + offset)
|
||||
char = pe.u32()
|
||||
timedate = pe.u32()
|
||||
major = pe.u16()
|
||||
minor = pe.u16()
|
||||
n_named = pe.u16()
|
||||
n_id = pe.u16()
|
||||
|
||||
entries = [ ]
|
||||
|
||||
for i in range(n_named + n_id):
|
||||
entries.append((pe.u32(), pe.u32()))
|
||||
|
||||
rv = { }
|
||||
|
||||
for name, value in entries:
|
||||
|
||||
if name & 0x80000000:
|
||||
pe.seek((name & 0x7fffffff) + RESOURCE_BASE)
|
||||
name = pe.name()
|
||||
|
||||
if value & 0x80000000:
|
||||
value = parse_directory(value & 0x7fffffff)
|
||||
else:
|
||||
value = parse_data(value)
|
||||
|
||||
rv[name] = value
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
##############################################################################
|
||||
# This utility function displays the tree of resources that have been loaded.
|
||||
def show_resources(d, prefix):
|
||||
|
||||
if not isinstance(d, dict):
|
||||
print prefix, "Codepage", d[0], "length", len(d[1])
|
||||
return
|
||||
|
||||
for k in d:
|
||||
print prefix, k
|
||||
show_resources(d[k], prefix + " ")
|
||||
|
||||
##############################################################################
|
||||
# These functions repack the resources into a new resource segment. Here,
|
||||
# the offset is relative to the start of the resource segment.
|
||||
|
||||
class Packer(object):
|
||||
|
||||
def pack(self, d):
|
||||
self.data = ""
|
||||
self.data_offset = 0
|
||||
|
||||
self.entries = ""
|
||||
self.entries_offset = 0
|
||||
|
||||
head = self.pack_dict(d, 0)
|
||||
|
||||
self.data = ""
|
||||
self.data_offset = len(head) + len(self.entries)
|
||||
|
||||
self.entries = ""
|
||||
self.entries_offset = len(head)
|
||||
|
||||
return self.pack_dict(d, 0) + self.entries + self.data
|
||||
|
||||
|
||||
|
||||
def pack_name(self, s):
|
||||
rv = self.data_offset + len(self.data)
|
||||
|
||||
l = len(s)
|
||||
s = s.encode("utf-16le")
|
||||
self.data += struct.pack("<H", l) + s + "\0\0"
|
||||
|
||||
return rv
|
||||
|
||||
def pack_tuple(self, t):
|
||||
codepage, data = t
|
||||
|
||||
rv = len(self.entries) + self.entries_offset
|
||||
|
||||
if len(self.data) % 2:
|
||||
self.data += "P"
|
||||
|
||||
daddr = len(self.data) + self.data_offset
|
||||
|
||||
self.entries += struct.pack("<IIII", daddr + RESOURCE_VIRTUAL, len(data), codepage, 0)
|
||||
self.data += data
|
||||
|
||||
# if len(self.data) % 1 == 1:
|
||||
# self.data += 'P'
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
|
||||
|
||||
def pack_dict(self, d, offset):
|
||||
name_entries = sorted((a, b) for a, b in d.iteritems() if isinstance(a, unicode))
|
||||
id_entries = sorted((a, b) for a, b in d.iteritems() if isinstance(a, int))
|
||||
|
||||
rv = struct.pack("<IIHHHH", 0, 0, 4, 0, len(name_entries), len(id_entries))
|
||||
|
||||
offset += len(rv) + (len(name_entries) + len(id_entries)) * 8
|
||||
|
||||
rest = ""
|
||||
|
||||
for (name, value) in name_entries + id_entries:
|
||||
if isinstance(name, unicode):
|
||||
name = 0x80000000 | self.pack_name(name)
|
||||
|
||||
if isinstance(value, dict):
|
||||
addr = offset | 0x80000000
|
||||
packed = self.pack_dict(value, offset)
|
||||
offset += len(packed)
|
||||
rest += packed
|
||||
else:
|
||||
addr = self.pack_tuple(value)
|
||||
|
||||
rv += struct.pack("<II", name, addr)
|
||||
|
||||
return rv + rest
|
||||
|
||||
##############################################################################
|
||||
# This loads in an icon file, and returns a dictionary that is suitable for
|
||||
# use in the resources of an exe file.
|
||||
def load_icon(fn):
|
||||
f = BinFile(fn)
|
||||
|
||||
f.seek(0)
|
||||
f.u16()
|
||||
f.u16()
|
||||
count = f.u16()
|
||||
|
||||
rv = { }
|
||||
rv[3] = { }
|
||||
|
||||
group = struct.pack("HHH", 0, 1, count)
|
||||
|
||||
for i in range(count):
|
||||
width = f.u8()
|
||||
height = f.u8()
|
||||
colors = f.u8()
|
||||
reserved = f.u8()
|
||||
planes = f.u16()
|
||||
bpp = f.u16()
|
||||
size = f.u32()
|
||||
offset = f.u32()
|
||||
|
||||
addr = f.addr
|
||||
f.seek(offset + 16)
|
||||
if not f.u32():
|
||||
f.set_u32(offset + 20, 0)
|
||||
|
||||
rv[3][i + 1] = { 0 : (1252, f.substring(offset, size)) }
|
||||
|
||||
|
||||
group += struct.pack("BBBBHHIH", width, height, colors, reserved,
|
||||
planes, bpp, size, i + 1)
|
||||
|
||||
f.seek(addr)
|
||||
|
||||
rv[14] = { 1 : { 0 : (1252, group) } }
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
##############################################################################
|
||||
# This is the main function that should be called externally, that copies over
|
||||
# the icons.
|
||||
def change_icons(oldexe, icofn):
|
||||
global pe
|
||||
pe = BinFile(oldexe)
|
||||
|
||||
# Check that RESOURCE_BASE is still valid.
|
||||
pe.seek(CHECK_ADDRESS)
|
||||
if pe.u32() != RESOURCE_BASE:
|
||||
raise Exception("RESOURCE_BASE is no longer correct. Please check all relocations.")
|
||||
|
||||
resources = parse_directory(0)
|
||||
# show_resources(resources, "")
|
||||
resources.update(load_icon(icofn))
|
||||
# show_resources(resources, "")
|
||||
|
||||
rsrc = Packer().pack(resources)
|
||||
rsrc_len = len(rsrc)
|
||||
|
||||
pe.seek(ALIGNMENT_FIELD)
|
||||
alignment = pe.u32()
|
||||
|
||||
if len(rsrc) % alignment:
|
||||
pad = alignment - (len(rsrc) % alignment)
|
||||
padding = "RENPYVNE" * (pad / 8 + 1)
|
||||
padding = padding[:pad]
|
||||
rsrc += padding
|
||||
|
||||
padded_len = len(rsrc)
|
||||
|
||||
for addr in RESOURCE_LENGTH_PATCHES:
|
||||
pe.set_u32(addr, rsrc_len)
|
||||
|
||||
for addr in RESOURCE_PADDED_PATCHES:
|
||||
pe.seek(addr)
|
||||
size_delta = padded_len - pe.u32()
|
||||
pe.set_u32(addr, padded_len)
|
||||
|
||||
for addr in SIZE_DELTA_PATCHES:
|
||||
pe.seek(addr)
|
||||
pe.set_u32(addr, pe.u32() + size_delta)
|
||||
|
||||
|
||||
return pe.tostring()[:RESOURCE_BASE] + rsrc
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
f = file(sys.argv[3], "wb")
|
||||
f.write(change_icons(sys.argv[1], sys.argv[2]))
|
||||
f.close()
|
||||
|
||||