Compare commits
195 Commits
blit
..
renpy-5.4.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 311a09fb97 | |||
| df4154c854 | |||
| cbe66f8353 | |||
| 143db03196 | |||
| e879687a16 | |||
| fcb3286df9 | |||
| ba23df21ab | |||
| 5f6f3d8e70 | |||
| 9d7cd97fcc | |||
| a90491b678 | |||
| ae8fc4b859 | |||
| c4605e915d | |||
| e809b24663 | |||
| ebf669e8af | |||
| 3b7b40b85a | |||
| 4225f92095 | |||
| 31241ca637 | |||
| 6e06e155dd | |||
| c7059bc3cb | |||
| f6c4f7fda1 | |||
| a6f04a6741 | |||
| 12e4028979 | |||
| f06e4ab954 | |||
| be98f91907 | |||
| ada05d2eb2 | |||
| 1d355ceb0b | |||
| e26d93a3fa | |||
| f622a4f7b6 | |||
| 7cb63a3db9 | |||
| 42b11c2e74 | |||
| f4d5ef1425 | |||
| 8109a8dbfe | |||
| c8c1b93894 | |||
| 597be879e4 | |||
| 3d1a2e4d96 | |||
| 00401fd0e9 | |||
| 6eef431161 | |||
| f3b31c8025 | |||
| df4fa900e9 | |||
| fbd92b66b0 | |||
| a29fd3aa8b | |||
| c9af317092 | |||
| 91fdd68ec1 | |||
| c8b90b04e2 | |||
| 8c85390aaf | |||
| b77f295921 | |||
| c0e3dfc99b | |||
| f5c6ea530d | |||
| ce7ccc2f87 | |||
| a4d1670378 | |||
| c143c0c479 | |||
| f7cb4eae9c | |||
| bb35ff5658 | |||
| b479fd4a06 | |||
| c9c0ac5809 | |||
| 04cbf7620c | |||
| 6770b268fc | |||
| 60f988dc6c | |||
| a8e8dc4f49 | |||
| 622db83c50 | |||
| d17b68a17c | |||
| 8d0465e72e | |||
| 3dc117ed96 | |||
| 111b7e440d | |||
| 3e80c3acbb | |||
| 56b6135b8e | |||
| 24ec412f1e | |||
| cd987bdb6d | |||
| d6f3b7aab0 | |||
| f2f9db46e9 | |||
| 57c38e0363 | |||
| 6d055e775a | |||
| 48033b3b1e | |||
| 9d57e0dad1 | |||
| bed546e6e6 | |||
| 94a9422ddc | |||
| 8f7bd1be77 | |||
| e863d4a645 | |||
| 8d1cf70edc | |||
| eadb1c6385 | |||
| ad51d6a674 | |||
| 6880da07b7 | |||
| 35408d81ea | |||
| d75eb13658 | |||
| dc3a01aae4 | |||
| 1792f7a7af | |||
| eb82c47109 | |||
| 229748a68e | |||
| d5d0d12697 | |||
| 6bce3b0e9a | |||
| 349f2479d3 | |||
| 75d2f81973 | |||
| eadbdebcb0 | |||
| 196e3701e9 | |||
| a2904a7618 | |||
| 3a2fc5498a | |||
| c3757fdea9 | |||
| 6ee37d4933 | |||
| 4ded3eba45 | |||
| 277c7b4d88 | |||
| 403bb65826 | |||
| 0d1946f3bc | |||
| 61381a2f12 | |||
| 168729e9d9 | |||
| 42a2d2c8c0 | |||
| 42c8f4339c | |||
| 8c7ca92e54 | |||
| 0919f556b4 | |||
| 50bb65973b | |||
| 660fa3114e | |||
| 9ebad42f5a | |||
| cd8acdcd8a | |||
| 9bbecc250d | |||
| 518a10cae0 | |||
| c1206e7fbd | |||
| daaba9f294 | |||
| 2e864c971d | |||
| 5b9494ebcc | |||
| d228af6ffd | |||
| 454f103818 | |||
| e26989c288 | |||
| 5c06b96c18 | |||
| d3bfda1a11 | |||
| 0308d48953 | |||
| 9525db2212 | |||
| 7afca9d5d5 | |||
| ccca92c88a | |||
| aa3ff2f012 | |||
| 46e8fdd092 | |||
| f2963d33c3 | |||
| 0c96d52b54 | |||
| 74678d5cef | |||
| 3dd6959fe1 | |||
| 7b6ec9db5b | |||
| 404936dae6 | |||
| f7de1b0eb0 | |||
| 8614e6406d | |||
| 0c6e1893e2 | |||
| f1bec25291 | |||
| 429b34b80f | |||
| 5a28ecd5f5 | |||
| ab5d5c0e40 | |||
| c28639ef0b | |||
| a3b3d26816 | |||
| ee6bbf5d9c | |||
| 6e406cdae0 | |||
| 8ef2d97247 | |||
| e9be405aaf | |||
| 23dbab98d2 | |||
| 2865599e34 | |||
| fb5957186a | |||
| 8d2e592589 | |||
| c7714a69fd | |||
| 6d2dce1704 | |||
| 8ab32e8dba | |||
| 00ee770512 | |||
| c5761728b3 | |||
| d33aa31101 | |||
| 34cfc4fa40 | |||
| d603d62680 | |||
| 4991c5761c | |||
| cf3f8afd9f | |||
| 31fc8b213e | |||
| 74be98faba | |||
| b4e6b24a86 | |||
| 7c1980775e | |||
| 90d586968e | |||
| af0778b8bc | |||
| 23bccf06cb | |||
| 7558b20b1e | |||
| 8fbb785674 | |||
| d2210cf08f | |||
| 8d8ba4fb00 | |||
| 915df21775 | |||
| 59c7baf1ff | |||
| dc0d2f8039 | |||
| bcf752d3a6 | |||
| d95cd1f6a0 | |||
| c9f025dab2 | |||
| d484cc2906 | |||
| 18cf867432 | |||
| 149a2904a5 | |||
| ef2e78616f | |||
| 2711f79080 | |||
| fa31e03d06 | |||
| 6a27954677 | |||
| eab1edd5b4 | |||
| b9624fe922 | |||
| 69727f3baf | |||
| 0551eb6135 | |||
| c0b8ca8777 | |||
| 7af2e3d066 | |||
| 0a857f8879 | |||
| 839073ad8c | |||
| eb22c24884 |
@@ -1,129 +0,0 @@
|
||||
# macOS-related files
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Windows-related files
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Python related files.
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyi
|
||||
*.egg-info
|
||||
|
||||
# Editor/tool files.
|
||||
*~
|
||||
*.bak
|
||||
.gdb_history
|
||||
|
||||
# Compiled Ren'Py files.
|
||||
*.rpyc
|
||||
*.rpyb
|
||||
*.rpymc
|
||||
|
||||
# Ren'Py log files.
|
||||
log.txt
|
||||
errors.txt
|
||||
traceback.txt
|
||||
lint.txt
|
||||
|
||||
# Created directories.
|
||||
saves/
|
||||
tmp/
|
||||
cache/
|
||||
|
||||
# Executables.
|
||||
/renpy.exe
|
||||
/renpy2.exe
|
||||
/renpy3.exe
|
||||
/renpy-32.exe
|
||||
|
||||
/renpy.sh
|
||||
/renpy2.sh
|
||||
/renpy3.sh
|
||||
|
||||
/renpy.app
|
||||
/renpy2.app
|
||||
/renpy3.app
|
||||
|
||||
/7z.sfx
|
||||
|
||||
# Libraries.
|
||||
/lib
|
||||
|
||||
# Platforms.
|
||||
/rapt
|
||||
/rapt2
|
||||
/rapt3
|
||||
|
||||
/renios
|
||||
/renios2
|
||||
/renios3
|
||||
|
||||
/web
|
||||
/web2
|
||||
/web3
|
||||
|
||||
# Docs.
|
||||
/sphinx/source/inc
|
||||
/sphinx/source/thequestion.rst
|
||||
/doc
|
||||
/doc-web
|
||||
/LICENSE.txt
|
||||
|
||||
# Gui template.
|
||||
/gui/game/gui
|
||||
|
||||
# Editors.
|
||||
/jedit
|
||||
/atom
|
||||
/vscode
|
||||
|
||||
# Generated source.
|
||||
/renpy/vc_version.py
|
||||
/module/gen/
|
||||
/module/gen3/
|
||||
/module/gen-static/
|
||||
/module/gen3-static/
|
||||
/tutorial/game/tutorial_director.rpy
|
||||
|
||||
# Module build.
|
||||
/module/build
|
||||
/module/dist
|
||||
/module/emscripten-static
|
||||
|
||||
# Download target.
|
||||
/dl
|
||||
|
||||
# Pygame_sdl2.
|
||||
/pygame_sdl2
|
||||
|
||||
# Steam.
|
||||
steam_appid.txt
|
||||
/steamapi.py
|
||||
|
||||
# Android.
|
||||
*.keystore
|
||||
|
||||
# Type analysis.
|
||||
/typings
|
||||
|
||||
# Live2D
|
||||
cubism
|
||||
CubismSdkForNative-4-*.zip
|
||||
|
||||
# Created files.
|
||||
/screenshot*
|
||||
.android.json
|
||||
|
||||
# Old files.
|
||||
/0old
|
||||
|
||||
# Works in progress, throwaway scripts, etc.
|
||||
/scratch
|
||||
/*-dists
|
||||
@@ -1,59 +0,0 @@
|
||||
{
|
||||
"files.watcherExclude": {
|
||||
"**/renpy/0old/**" : true,
|
||||
"**/renpy/atom/**" : true,
|
||||
"**/renpy/cubism/**" : true,
|
||||
"**/renpy/dists/**" : true,
|
||||
"**/renpy/dl/**" : true,
|
||||
"**/renpy/doc/**" : true,
|
||||
"**/renpy/doc-web/**" : true,
|
||||
"**/renpy/jedit/**" : true,
|
||||
"**/renpy/renpy*.app/**" : true,
|
||||
"**/renpy/tmp/**" : true,
|
||||
"**/renpy/web/**" : true,
|
||||
},
|
||||
"files.exclude": {
|
||||
"0old": true,
|
||||
"**-dists": true,
|
||||
"**/*.pyc": true,
|
||||
"**/*.pyo": true,
|
||||
"renpy/**/*.pyi": true,
|
||||
"**/*.rpmc": true,
|
||||
"**/*.rpyc": true,
|
||||
"**/__pycache__": true,
|
||||
"**/screenshot-*": true,
|
||||
"7z.sfx": true,
|
||||
"atom": true,
|
||||
"cubism": true,
|
||||
"dl": true,
|
||||
"doc": true,
|
||||
"doc-web": true,
|
||||
"jedit": true,
|
||||
"lib*": true,
|
||||
"live2d": true,
|
||||
"rapt*": true,
|
||||
"renios*": true,
|
||||
"renpy*.app": true,
|
||||
"renpy*.exe": true,
|
||||
"tmp": true,
|
||||
"web": true,
|
||||
"**/*.rpa": true,
|
||||
"**/*.rpymc": true,
|
||||
"**/cache/": true
|
||||
},
|
||||
|
||||
"python.languageServer": "Pylance",
|
||||
"python.analysis.typeCheckingMode": "basic",
|
||||
"python.analysis.diagnosticMode": "workspace",
|
||||
"python.linting.pylintEnabled": false,
|
||||
"python.analysis.diagnosticSeverityOverrides": {
|
||||
"reportMissingModuleSource": "none"
|
||||
},
|
||||
"esbonio.server.enabled": true,
|
||||
"esbonio.sphinx.confDir": "",
|
||||
"renpy.excludeCompiledFilesFromWorkspace": true,
|
||||
"renpy.watchFoldersForChanges": false,
|
||||
"renpy.warnOnInvalidVariableNames": "Disabled",
|
||||
"renpy.warnOnInvalidFilenameIssues": "Disabled",
|
||||
"renpy.warnOnIndentationAndSpacingIssues": "Disabled",
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
Copyright 2004-2006 PyTom <pytom@bishoujo.us>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
The character images in the demo game (game/9a_*.png) are under their
|
||||
own copyright. I allow them to be used as part of the demo game, but
|
||||
not as part of other games.
|
||||
|
||||
---
|
||||
|
||||
Please note that the above license only applies to Ren'Py
|
||||
proper. A binary distribution of Ren'Py contains a number of other
|
||||
packages, each with their own licenses, which you may be bound to
|
||||
comply with when distributing a Ren'Py game.
|
||||
|
||||
For the purposes of complicance with the LGPL, we've made available
|
||||
the source code to all of the LGPL libraries used by Ren'Py at
|
||||
http://www.bishoujo.us/renpy/dl/lgpl/.
|
||||
|
||||
The following libraries are used in binary packages of Ren'Py:
|
||||
|
||||
Python (Python License)
|
||||
http://www.python.org
|
||||
|
||||
Pygame (GNU LGPL)
|
||||
http://www.pygame.org
|
||||
|
||||
SDL (GNU LGPL)
|
||||
http://www.libsdl.org
|
||||
|
||||
SDL_image (GNU LGPL)
|
||||
http://www.libsdl.org/projects/SDL_image/
|
||||
|
||||
SDL_sound (GNU LGPL)
|
||||
http://www.icculus.org/SDL_sound/
|
||||
|
||||
SDL_ttf (GNU LGPL)
|
||||
http://www.libsdl.org/projects/SDL_ttf
|
||||
|
||||
ctypes (MIT)
|
||||
http://starship.python.net/crew/theller/ctypes/
|
||||
@@ -1,178 +0,0 @@
|
||||
==============================
|
||||
The Ren'Py Visual Novel Engine
|
||||
==============================
|
||||
|
||||
https://www.renpy.org
|
||||
|
||||
Ren'Py development takes place on the ``master`` branch, and occasionally
|
||||
on feature branches.
|
||||
|
||||
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
Ren'Py depends on a number of Python modules written in Cython and C. For
|
||||
changes to Ren'Py that only involve Python modules, you can use the modules
|
||||
found in the latest nightly build. Otherwise, you'll have to compile the
|
||||
modules yourself.
|
||||
|
||||
The development scripts assume a POSIX-like platform. The scripts should run
|
||||
on Linux or macOS, and can be made to run on Windows using an environment
|
||||
like MSYS.
|
||||
|
||||
Nightly Build
|
||||
-------------
|
||||
|
||||
Nightly builds can be downloaded from:
|
||||
|
||||
https://nightly.renpy.org
|
||||
|
||||
Note that the latest nightly build is at the bottom of the list. Once you've
|
||||
unpacked the nightly, change into this repository, and run::
|
||||
|
||||
./after_checkout.sh <path-to-nightly>
|
||||
|
||||
Once this script completes, you should be able to run Ren'Py using renpy.sh,
|
||||
renpy.app, or renpy.exe, as appropriate for your platform.
|
||||
|
||||
If the current nightly build doesn't work, please wait 24 hours for a new
|
||||
build to occur. If that build still doesn't work, contact Tom (`pytom at bishoujo.us`,
|
||||
or @renpytom on twitter) to find out what's wrong.
|
||||
|
||||
The ``doc`` symlink will dangle until documentation is built, as described
|
||||
below.
|
||||
|
||||
Compiling the Modules
|
||||
----------------------
|
||||
|
||||
Building the modules requires you have the many dependencies installed on
|
||||
your system. On Ubuntu and Debian, these dependencies can be installed with
|
||||
the command::
|
||||
|
||||
sudo apt install virtualenvwrapper python3-dev libavcodec-dev libavformat-dev \
|
||||
libswresample-dev libswscale-dev libfreetype6-dev libfribidi-dev libsdl2-dev \
|
||||
libsdl2-image-dev libsdl2-gfx-dev libsdl2-mixer-dev libsdl2-ttf-dev libjpeg-dev
|
||||
|
||||
Ren'Py requires SDL_image 2.6 or greater. If your distribution doesn't include
|
||||
that version, you'll need to download it from:
|
||||
|
||||
https://github.com/libsdl-org/SDL_image/tree/SDL2
|
||||
|
||||
We strongly suggest installing the Ren'Py modules into a Python
|
||||
virtualenv. To create a new virtualenv, open a new terminal and run::
|
||||
|
||||
. /usr/share/virtualenvwrapper/virtualenvwrapper.sh
|
||||
mkvirtualenv renpy
|
||||
|
||||
To return to this virtualenv later, run::
|
||||
|
||||
. /usr/share/virtualenvwrapper/virtualenvwrapper.sh
|
||||
workon renpy
|
||||
|
||||
After activating the virtualenv, install additional dependencies::
|
||||
|
||||
pip install -U cython future six typing pefile requests ecdsa
|
||||
|
||||
Then, install pygame_sdl2 by running the following commands::
|
||||
|
||||
git clone https://www.github.com/renpy/pygame_sdl2
|
||||
pushd pygame_sdl2
|
||||
python setup.py install
|
||||
python setup.py install_headers
|
||||
popd
|
||||
|
||||
Next, set RENPY_DEPS_INSTALL To a \:-separated (\;-separated on Windows)
|
||||
list of paths containing the dependencies, and RENPY_CYTHON to the name
|
||||
of the cython command::
|
||||
|
||||
export RENPY_DEPS_INSTALL="/usr:/usr/lib/$(gcc -dumpmachine)/"
|
||||
export RENPY_CYTHON=cython
|
||||
|
||||
Finally, use setup.py in the Ren'Py ``module`` directory to compile and
|
||||
install the modules that support Ren'Py::
|
||||
|
||||
pushd module
|
||||
python setup.py install
|
||||
popd
|
||||
|
||||
Ren'Py will be installed into the activated virtualenv. It can then be run
|
||||
using the command::
|
||||
|
||||
python renpy.py
|
||||
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
Building the documentation requires Ren'Py to work. You'll either need to
|
||||
link in a nightly build, or compile the modules as described above. You'll
|
||||
also need the `Sphinx <https://www.sphinx-doc.org>`_ documentation generator.
|
||||
If you have pip working, install Sphinx using::
|
||||
|
||||
pip install -U sphinx sphinx-bootstrap-theme
|
||||
|
||||
Once Sphinx is installed, change into the ``sphinx`` directory inside the
|
||||
Ren'Py checkout and run::
|
||||
|
||||
./build.sh
|
||||
|
||||
Format
|
||||
------
|
||||
|
||||
Ren'Py's documentation consists of reStructuredText files found in sphinx/source, and
|
||||
generated documentation found in function docstrings scattered throughout the code. Do
|
||||
not edit the files in sphinx/source/inc directly, as they will be overwritten.
|
||||
|
||||
Docstrings may include tags on the first few lines:
|
||||
|
||||
\:doc: `section` `kind`
|
||||
Indicates that this function should be documented. `section` gives
|
||||
the name of the include file the function will be documented in, while
|
||||
`kind` indicates the kind of object to be documented (one of ``function``,
|
||||
``method`` or ``class``. If omitted, `kind` will be auto-detected.
|
||||
\:name: `name`
|
||||
The name of the function to be documented. Function names are usually
|
||||
detected, so this is only necessary when a function has multiple aliases.
|
||||
\:args: `args`
|
||||
This overrides the detected argument list. It can be used if some arguments
|
||||
to the function are deprecated.
|
||||
|
||||
For example::
|
||||
|
||||
def warp_speed(factor, transwarp=False):
|
||||
"""
|
||||
:doc: warp
|
||||
:name: renpy.warp_speed
|
||||
:args: (factor)
|
||||
|
||||
Exceeds the speed of light.
|
||||
"""
|
||||
|
||||
renpy.engine.warp_drive.engage(factor)
|
||||
|
||||
|
||||
Translating
|
||||
===========
|
||||
|
||||
For best practices when it comes to translating the launcher and template
|
||||
game, please read:
|
||||
|
||||
https://lemmasoft.renai.us/forums/viewtopic.php?p=321603#p321603
|
||||
|
||||
|
||||
Contributing
|
||||
============
|
||||
|
||||
For bug fixes, documentation improvements, and simple changes, just
|
||||
make a pull request. For more complex changes, it might make sense
|
||||
to file an issue first so we can discuss the design.
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
For the complete licensing terms, please read:
|
||||
|
||||
https://www.renpy.org/doc/html/license.html
|
||||
@@ -0,0 +1,55 @@
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
Have a new form of imagemap that uses a color image to distinguish
|
||||
hotspots.
|
||||
|
||||
Keyd compiles.
|
||||
@@ -1,56 +0,0 @@
|
||||
This file contains the names, contact information, and notes about each
|
||||
language Ren'Py is translated in. When you're adding a translation or
|
||||
taking responsibility for a translation, please send a pull request updating
|
||||
this file.
|
||||
|
||||
|
||||
Pig Latin
|
||||
---------
|
||||
|
||||
This is an automatically-generated translation that lets me test various
|
||||
translation mechanisms.
|
||||
|
||||
* Tom Rothamel <pytom@bishoujo.us>
|
||||
|
||||
Russian
|
||||
---------
|
||||
|
||||
Translation to the Russian language. Most of translation provided by Project
|
||||
Gardares, especially Ria-kon.
|
||||
|
||||
* Project Gardares <guardares@gmail.com>
|
||||
|
||||
Malay
|
||||
---------
|
||||
|
||||
Translation to the Malay language. This will be translated to both writing
|
||||
systems used in Malay, namely the Latin-based Rumi script system and the
|
||||
Arabic-based Jawi script system, to provide compatibility in some areas where
|
||||
there is still exist Malay people who still cannot read their language in the
|
||||
newer Rumi script system, or refused to use it. The translation is being made
|
||||
as general and native as possible to cover the entire region that uses Malay
|
||||
language, this means native words take precedence over loanwords if and only if
|
||||
both native and loanwords exist and both are suitable in the context of use.
|
||||
|
||||
* Muhammad Nur Hidayat (MNH48) <translation@mnh48.moe>
|
||||
|
||||
Turkish
|
||||
---------
|
||||
|
||||
Translation to the Turkish language. Some terms and abbreviations (especially
|
||||
those related to coding) are deliberately left untranslated (or the original words
|
||||
are provided in brackets) to avoid confusion, relying on their common use in
|
||||
Turkish language and among Turkish developers.
|
||||
|
||||
* Arda Güler (WKFO) <ardaguler09@gmail.com>
|
||||
|
||||
Finnish
|
||||
---------
|
||||
|
||||
Translation to the Finnish language. Most terms have been translated,
|
||||
sometimes requiring some imagination to come up with suitable terms.
|
||||
The quality is not perfect, but it should make sense to any developer
|
||||
fluent in Finnish but not English. Adjustments will be made if better
|
||||
translations are suggested.
|
||||
|
||||
* Lari Liuhamo (Diapolo10) <lari.liuhamo+github116504@gmail.com>
|
||||
@@ -1,154 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from renpy import version_tuple # @UnresolvedImport
|
||||
|
||||
SOURCE = [
|
||||
"/home/tom/ab/renpy",
|
||||
"/home/tom/ab/renpy-build",
|
||||
"/home/tom/ab/pygame_sdl2",
|
||||
"/home/tom/ab/renpy-build/renpyweb",
|
||||
]
|
||||
|
||||
version = ".".join(str(i) for i in version_tuple)
|
||||
short_version = ".".join(str(i) for i in version_tuple[:-1])
|
||||
major = short_version.split(".")[0]
|
||||
print("Version", version)
|
||||
|
||||
ap = argparse.ArgumentParser()
|
||||
|
||||
ap.add_argument("--release", action="store_true")
|
||||
ap.add_argument("--prerelease", action="store_true")
|
||||
ap.add_argument("--experimental", action="store_true")
|
||||
ap.add_argument("--no-tag", "-n", action="store_true")
|
||||
ap.add_argument("--push-tags", action="store_true")
|
||||
ap.add_argument("--delete-tag")
|
||||
ap.add_argument("--github", action="store_true")
|
||||
ap.add_argument("--real", action="store_true")
|
||||
|
||||
args = ap.parse_args()
|
||||
|
||||
if not args.real:
|
||||
print("Did you want to use scripts/add_all.sh? If not, give --real.")
|
||||
sys.exit(1)
|
||||
|
||||
if args.github:
|
||||
subprocess.call([ "git", "push", "--tags" ])
|
||||
subprocess.call([ "gh", "release", "create", version, "--notes", "See https://www.renpy.org/release/" + short_version, "-t", "Ren'Py {}".format(short_version) ])
|
||||
|
||||
dn = "/home/tom/ab/renpy/dl/" + short_version
|
||||
|
||||
for fn in os.listdir(dn):
|
||||
|
||||
if fn == ".build_cache":
|
||||
continue
|
||||
|
||||
if fn.endswith(".update.gz"):
|
||||
continue
|
||||
|
||||
if fn.endswith(".update.json"):
|
||||
continue
|
||||
|
||||
if fn.startswith("updates.json"):
|
||||
continue
|
||||
|
||||
if fn.endswith(".zsync"):
|
||||
continue
|
||||
|
||||
if fn.endswith(".sums"):
|
||||
continue
|
||||
|
||||
subprocess.call([ "gh", "release", "upload", version, os.path.join(dn, fn) ])
|
||||
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
|
||||
if args.release:
|
||||
subprocess.check_call([ "/home/tom/ab/renpy/scripts/checksums.py", "/home/tom/ab/renpy/dl/" + short_version ])
|
||||
|
||||
if args.delete_tag:
|
||||
for i in SOURCE:
|
||||
|
||||
os.chdir(i)
|
||||
|
||||
if i == SOURCE[0]:
|
||||
tag = args.delete_tag
|
||||
else:
|
||||
tag = "renpy-" + args.delete_tag
|
||||
|
||||
subprocess.call([ "git", "tag", "-d", tag, ])
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
if args.push_tags:
|
||||
for i in SOURCE:
|
||||
os.chdir(i)
|
||||
|
||||
if subprocess.call([ "git", "push", "--tags" ]):
|
||||
print("Tags not pushed: {}".format(os.getcwd()))
|
||||
sys.exit(1)
|
||||
|
||||
print("Pushed tags.")
|
||||
sys.exit(0)
|
||||
|
||||
if args.release:
|
||||
links = [ "release", "prerelease", "experimental" ]
|
||||
tag = True
|
||||
elif args.prerelease:
|
||||
links = [ "prerelease", "experimental" ]
|
||||
tag = True
|
||||
elif args.experimental:
|
||||
links = [ "experimental" ]
|
||||
tag = False
|
||||
else:
|
||||
links = [ ]
|
||||
tag = False
|
||||
|
||||
if args.no_tag:
|
||||
tag = False
|
||||
|
||||
links = [ i + "-" + major for i in links ]
|
||||
|
||||
if tag:
|
||||
for i in SOURCE:
|
||||
os.chdir(i)
|
||||
|
||||
if subprocess.call([ "git", "diff", "--quiet", "HEAD" ]):
|
||||
print("Directory not checked in: {}".format(os.getcwd()))
|
||||
sys.exit(1)
|
||||
|
||||
for i in SOURCE:
|
||||
|
||||
os.chdir(i)
|
||||
|
||||
if i == SOURCE[0]:
|
||||
tag = version
|
||||
else:
|
||||
tag = "renpy-" + version
|
||||
|
||||
subprocess.check_call([ "git", "tag", "-a", tag, "-m", "Tagging Ren'Py + " + version + " release." ])
|
||||
|
||||
os.chdir("/home/tom/ab/renpy/dl")
|
||||
|
||||
for i in links:
|
||||
if os.path.exists(i):
|
||||
os.unlink(i)
|
||||
os.symlink(short_version, i)
|
||||
|
||||
os.chdir("/home/tom/ab/website")
|
||||
subprocess.check_call("./upload.sh")
|
||||
|
||||
os.chdir("/home/tom/ab/renpy/sphinx")
|
||||
if args.release:
|
||||
subprocess.check_call("./upload.sh")
|
||||
elif args.prerelease:
|
||||
subprocess.check_call("./upload_dev.sh")
|
||||
|
||||
print("Version", version)
|
||||
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# This program adds froms to every unqualified call found in the game
|
||||
# directory. It's not perfect, but it's better than nothing.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import glob
|
||||
|
||||
def find_labels(fn, labels):
|
||||
|
||||
f = file(fn, "r")
|
||||
|
||||
for l in f:
|
||||
|
||||
m = re.match(r'^\s*call\s+\w+\s+from\s+(\w+)\s*$', l)
|
||||
if m:
|
||||
labels[m.group(1)] = True
|
||||
|
||||
m = re.match(r'^\s*call\s+expression.*from\s+(\w+)\s*$', l)
|
||||
if m:
|
||||
labels[m.group(1)] = True
|
||||
|
||||
f.close()
|
||||
|
||||
def replace_labels(fn, labels):
|
||||
|
||||
f = file(fn, "r")
|
||||
of = file(fn + ".new", "w")
|
||||
|
||||
|
||||
def replaceit(m):
|
||||
target = m.group(2)
|
||||
|
||||
num = 0
|
||||
|
||||
while True:
|
||||
num += 1
|
||||
label = "_call_%s_%d" % (target, num)
|
||||
|
||||
if label not in labels:
|
||||
break
|
||||
|
||||
labels[label] = True
|
||||
|
||||
return m.group(1) + "call " + target + " from " + label + m.group(3)
|
||||
|
||||
for l in f:
|
||||
l = re.sub(r'^(\s*)call\s+(\w+)(\s*$)', replaceit, l)
|
||||
|
||||
if re.search(r'call\s+expression', l) and not re.search('from', l):
|
||||
|
||||
num = 0
|
||||
|
||||
while True:
|
||||
num += 1
|
||||
label = "_call_expression_%d" % num
|
||||
|
||||
if label not in labels:
|
||||
break
|
||||
|
||||
labels[label] = label
|
||||
|
||||
l = l[:-1] + " from " + label + "\n"
|
||||
|
||||
of.write(l)
|
||||
|
||||
f.close()
|
||||
of.close()
|
||||
|
||||
|
||||
try:
|
||||
os.unlink(fn + ".bak")
|
||||
except:
|
||||
pass
|
||||
|
||||
os.rename(fn, fn + ".bak")
|
||||
os.rename(fn + ".new", fn)
|
||||
|
||||
def main():
|
||||
|
||||
pattern = "*/*.rpy"
|
||||
|
||||
if len(sys.argv) >= 2:
|
||||
pattern = sys.argv[1]
|
||||
|
||||
files = glob.glob(pattern)
|
||||
files = [ i for i in files if not i.startswith("common/") ]
|
||||
|
||||
labels = { }
|
||||
|
||||
for fn in files:
|
||||
print "Finding labels in", fn
|
||||
find_labels(fn, labels)
|
||||
|
||||
for fn in files:
|
||||
print "Replacing labels in", fn
|
||||
replace_labels(fn, labels)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
ROOT="$(dirname $(python -c "import os;print(os.path.realpath('$0'))"))"
|
||||
|
||||
ln -s "$ROOT/help.html" "$ROOT/tutorial/README.html"
|
||||
ln -s "$ROOT/help.html" "$ROOT/the_question/README.html"
|
||||
ln -s "$ROOT/help.html" "$ROOT/templates/english/README.html"
|
||||
|
||||
ln -s "$ROOT/sphinx/source/license.rst" "$ROOT/LICENSE.txt"
|
||||
|
||||
if [ "$1" != "" ]; then
|
||||
ln -s "$1/lib" "$ROOT/lib"
|
||||
ln -s "$1/renpy.app" "$ROOT"
|
||||
ln -s "$1/renpy.exe" "$ROOT"
|
||||
ln -s "$1/renpy.sh" "$ROOT"
|
||||
fi
|
||||
@@ -0,0 +1,3 @@
|
||||
cd game
|
||||
..\archiver.exe images *.png *.jpg
|
||||
pause
|
||||
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# The Ren'Py archiver. This builds a Ren'Py archive file, and the
|
||||
# associated index file. These files are really easy to reverse-engineer,
|
||||
# but are probably better than nothing.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import encodings.zlib_codec
|
||||
import random
|
||||
import glob
|
||||
|
||||
from cPickle import loads, dumps, HIGHEST_PROTOCOL
|
||||
|
||||
# The most we will go without inserting some padding. 10k.
|
||||
padding_every = 10240
|
||||
|
||||
# The amount of padding we will add.
|
||||
padding_max = 4
|
||||
|
||||
def randpadding():
|
||||
|
||||
plen = random.randint(1, padding_max)
|
||||
|
||||
rv = ""
|
||||
|
||||
for i in range(0, plen):
|
||||
rv += chr(random.randint(1, 255))
|
||||
|
||||
return rv
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print "Usage: %s <file-prefix> <files ...>" % sys.argv[0]
|
||||
return
|
||||
|
||||
prefix = sys.argv[1]
|
||||
|
||||
# Archive file.
|
||||
archivef = file(prefix + ".rpa", "wb")
|
||||
|
||||
# Index file.
|
||||
indexf = file(prefix + ".rpi", "wb")
|
||||
|
||||
index = { }
|
||||
|
||||
random.seed()
|
||||
|
||||
offset = 0
|
||||
|
||||
# Needed because windows sucks. It doesn't do globbing on the
|
||||
# command line.
|
||||
files = [ ]
|
||||
for i in sys.argv[2:]:
|
||||
files.extend(glob.glob(i))
|
||||
|
||||
for fn in files:
|
||||
index[fn] = [ ]
|
||||
|
||||
print "Adding %s..." % fn
|
||||
|
||||
datafile = file(fn, "rb")
|
||||
|
||||
while True:
|
||||
|
||||
# Pad with junk.
|
||||
padding = randpadding()
|
||||
archivef.write(padding)
|
||||
offset += len(padding)
|
||||
|
||||
# Pick a random block size.
|
||||
block = random.randint(1, padding_every)
|
||||
|
||||
data = datafile.read(block)
|
||||
|
||||
if not data:
|
||||
break
|
||||
|
||||
dlen = len(data)
|
||||
|
||||
archivef.write(data)
|
||||
|
||||
index[fn].append((offset, dlen))
|
||||
offset += dlen
|
||||
|
||||
datafile.close()
|
||||
|
||||
archivef.close()
|
||||
|
||||
indexf.write(dumps(index, HIGHEST_PROTOCOL).encode("zlib"))
|
||||
indexf.close()
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1 @@
|
||||
build_exe.py run_game
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from distutils.core import setup
|
||||
import py2exe
|
||||
import sys
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print "Usage: build_exe.py <prefix>"
|
||||
print ""
|
||||
print "Builds <prefix>.exe."
|
||||
print "Expects icons in <prefix>.ico."
|
||||
|
||||
sys.exit(-1)
|
||||
|
||||
def program(name):
|
||||
return dict(script="run_game.py",
|
||||
icon_resources=[ (0, name + ".ico"),
|
||||
],
|
||||
dest_base=name)
|
||||
|
||||
programs = [
|
||||
program(sys.argv[1]),
|
||||
]
|
||||
|
||||
sys.argv[1:] = [ 'py2exe', '-x', '-a', '--dll-excludes', 'w9xpopen.exe' ]
|
||||
|
||||
setup(name="RenPy",
|
||||
windows=programs,
|
||||
console=[ "console.py" ],
|
||||
zipfile='lib/renpy.code',
|
||||
options={ 'py2exe' : { 'excludes' : [ 'doctest',
|
||||
'pygame.macosx',
|
||||
'pygame.surfarray',
|
||||
'pygame.mixer',
|
||||
'pygame.mixer_music',
|
||||
'Numeric', ],
|
||||
'optimize' : 2,
|
||||
} },
|
||||
)
|
||||
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import glob
|
||||
import os
|
||||
|
||||
files = glob.glob("common/*.rpy")
|
||||
files.sort()
|
||||
|
||||
styles = [ ]
|
||||
|
||||
for fn in files:
|
||||
f = file(fn)
|
||||
|
||||
fi = iter(f)
|
||||
|
||||
for l in iter(fi):
|
||||
|
||||
l = l.strip()
|
||||
|
||||
if l.startswith("### "):
|
||||
|
||||
prefix, style, parent = l.split()
|
||||
|
||||
desc = ""
|
||||
|
||||
for l in fi:
|
||||
|
||||
l = l.strip()
|
||||
if not l.startswith("# "):
|
||||
break
|
||||
|
||||
desc = desc + " " + l[2:]
|
||||
|
||||
styles.append(' style.create("%s", "%s", "%s")' % (style, parent, desc[1:]))
|
||||
|
||||
f.close()
|
||||
|
||||
f = file("common/style.rpy")
|
||||
of = file("common/style.rpy.new", "w")
|
||||
|
||||
fi = iter(f)
|
||||
|
||||
for l in fi:
|
||||
|
||||
of.write(l)
|
||||
|
||||
if l.startswith("# AUTOMATICALLY GENERATED"):
|
||||
|
||||
for l in styles:
|
||||
of.write(l + "\n")
|
||||
|
||||
for l in fi:
|
||||
if l.startswith("# END AUTOMATICALLY GENERATED"):
|
||||
break
|
||||
|
||||
of.write(l)
|
||||
|
||||
f.close()
|
||||
of.close()
|
||||
|
||||
os.rename("common/style.rpy", "common/style.rpy.bak")
|
||||
os.rename("common/style.rpy.new", "common/style.rpy")
|
||||
@@ -0,0 +1,55 @@
|
||||
This package was debianized by Michael Fedrowitz <michaelf@debian.org>
|
||||
on Sun, 23 Feb 2003.
|
||||
|
||||
It was downloaded from
|
||||
'http://ftp.gnome.org/pub/GNOME/sources/ttf-bitstream-vera/'.
|
||||
|
||||
Upstream Author: Bitstream, Inc.
|
||||
|
||||
Copyright:
|
||||
|
||||
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
|
||||
Vera is a trademark of Bitstream, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the fonts accompanying this license ("Fonts") and associated
|
||||
documentation files (the "Font Software"), to reproduce and distribute
|
||||
the Font Software, including without limitation the rights to use,
|
||||
copy, merge, publish, distribute, and/or sell copies of the Font
|
||||
Software, and to permit persons to whom the Font Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright and trademark notices and this permission notice
|
||||
shall be included in all copies of one or more of the Font Software
|
||||
typefaces.
|
||||
|
||||
The Font Software may be modified, altered, or added to, and in
|
||||
particular the designs of glyphs or characters in the Fonts may be
|
||||
modified and additional glyphs or characters may be added to the
|
||||
Fonts, only if the fonts are renamed to names not containing either
|
||||
the words "Bitstream" or the word "Vera".
|
||||
|
||||
This License becomes null and void to the extent applicable to Fonts
|
||||
or Font Software that has been modified and is distributed under the
|
||||
"Bitstream Vera" names.
|
||||
|
||||
The Font Software may be sold as part of a larger software package but
|
||||
no copy of one or more of the Font Software typefaces may be sold by
|
||||
itself.
|
||||
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
|
||||
BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
|
||||
OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
|
||||
SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the names of Gnome, the Gnome
|
||||
Foundation, and Bitstream Inc., shall not be used in advertising or
|
||||
otherwise to promote the sale, use or other dealings in this Font
|
||||
Software without prior written authorization from the Gnome Foundation
|
||||
or Bitstream Inc., respectively. For further information, contact:
|
||||
fonts at gnome dot org.
|
||||
|
Before Width: | Height: | Size: 693 B After Width: | Height: | Size: 693 B |
@@ -0,0 +1,61 @@
|
||||
# This file contains a number of definitions of standard
|
||||
# locations and transitions. We've moved them into the common
|
||||
# directory so that it's easy for an updated version of all of these
|
||||
# definitions.
|
||||
|
||||
init -425:
|
||||
|
||||
# Positions ##############################################################
|
||||
|
||||
# These are positions that can be used inside at clauses. We set
|
||||
# them up here so that they can be used throughout the program.
|
||||
$ left = Position(xpos=0.0, xanchor='left')
|
||||
$ center = Position()
|
||||
$ right = Position(xpos=1.0, xanchor='right')
|
||||
|
||||
# Offscreen positions for use with the move transition. Images at
|
||||
# these positions are still shown (and consume
|
||||
# resources)... remember to hide the image after the transition.
|
||||
$ offscreenleft = Position(xpos=0.0, xanchor='right')
|
||||
$ offscreenright = Position(xpos=1.0, xanchor='left')
|
||||
|
||||
# Transitions ############################################################
|
||||
|
||||
# Simple transitions.
|
||||
$ fade = Fade(.5, 0, .5) # Fade to black and back.
|
||||
$ dissolve = Dissolve(0.5)
|
||||
$ pixellate = Pixellate(1.0, 5)
|
||||
|
||||
# Various uses of CropMove.
|
||||
$ wiperight = CropMove(1.0, "wiperight")
|
||||
$ wipeleft = CropMove(1.0, "wipeleft")
|
||||
$ wipeup = CropMove(1.0, "wipeup")
|
||||
$ wipedown = CropMove(1.0, "wipedown")
|
||||
|
||||
$ slideright = CropMove(1.0, "slideright")
|
||||
$ slideleft = CropMove(1.0, "slideleft")
|
||||
$ slideup = CropMove(1.0, "slideup")
|
||||
$ slidedown = CropMove(1.0, "slidedown")
|
||||
|
||||
$ slideawayright = CropMove(1.0, "slideawayright")
|
||||
$ slideawayleft = CropMove(1.0, "slideawayleft")
|
||||
$ slideawayup = CropMove(1.0, "slideawayup")
|
||||
$ slideawaydown = CropMove(1.0, "slideawaydown")
|
||||
|
||||
$ irisout = CropMove(1.0, "irisout")
|
||||
$ irisin = CropMove(1.0, "irisin")
|
||||
|
||||
# This moves changed images to their new locations
|
||||
$ move = MoveTransition(0.5)
|
||||
|
||||
# These shake the screen up and down for a quarter second.
|
||||
# The delay needs to be an integer multiple of the period.
|
||||
$ vpunch = Move((0, 10), (0, -10), .10, bounce=True, repeat=True, delay=.275)
|
||||
$ hpunch = Move((15, 0), (-15, 0), .10, bounce=True, repeat=True, delay=.275)
|
||||
|
||||
# These use the ImageDissolve to do some nifty effects.
|
||||
$ blinds = ImageDissolve(im.Tile("blindstile.png"), 1.0, 8)
|
||||
$ squares = ImageDissolve(im.Tile("squarestile.png"), 1.0, 256)
|
||||
|
||||
image black = Solid((0, 0, 0, 255))
|
||||
|
||||
@@ -0,0 +1,590 @@
|
||||
# This file contains code for the game menu, and associated
|
||||
# functionality that looks an awful lot like it.
|
||||
|
||||
init -499:
|
||||
python:
|
||||
|
||||
######################################################################
|
||||
# First up, we define a bunch of configuration variable, which the
|
||||
# user can change.
|
||||
|
||||
# The contents of the game menu choices.
|
||||
library.game_menu = [
|
||||
( "return", "Return", ui.jumps("_return"), 'True'),
|
||||
( "prefs", "Preferences", ui.jumps("_prefs_screen"), 'True' ),
|
||||
( "save", "Save Game", ui.jumps("_save_screen"), 'not renpy.context().main_menu' ),
|
||||
( "load", "Load Game", ui.jumps("_load_screen"), 'True'),
|
||||
( "mainmenu", "Main Menu", lambda : _mainmenu_prompt(), 'not renpy.context().main_menu' ),
|
||||
( "quit", "Quit", lambda : _quit_prompt("quit"), 'True' ),
|
||||
]
|
||||
|
||||
# If not None, a map from the names of the game menu
|
||||
# navigation buttons to new fixed positions for them on
|
||||
# the screen.
|
||||
library.game_menu_positions = None
|
||||
|
||||
# The number of columns of files to show at once.
|
||||
library.file_page_cols = 2
|
||||
|
||||
# The number of rows of files to show at once.
|
||||
library.file_page_rows = 5
|
||||
|
||||
# The number of pages to add quick access buttons for.
|
||||
library.file_quick_access_pages = 5
|
||||
|
||||
# The width of a thumbnail.
|
||||
library.thumbnail_width = 66
|
||||
|
||||
# The height of a thumbnail.
|
||||
library.thumbnail_height = 50
|
||||
|
||||
# Sound played when entering the library without clicking a
|
||||
# button.
|
||||
library.enter_sound = None
|
||||
|
||||
# Sound played when leaving the library without clicking a
|
||||
# button.
|
||||
library.exit_sound = None
|
||||
|
||||
# Transition that occurs when entering the game menu.
|
||||
library.enter_transition = None
|
||||
|
||||
# Transition that occurs when leaving the game menu.
|
||||
library.exit_transition = None
|
||||
|
||||
# This lets us disable the file pager. (So we only have one
|
||||
# page of files.)
|
||||
library.disable_file_pager = False
|
||||
|
||||
# This lets us disable the thumbnails in the file pager.
|
||||
library.disable_thumbnails = False
|
||||
|
||||
# If True, we will be prompted before loading a game. (This can
|
||||
# be changed from inside the game code, so that one can load from
|
||||
# the first few screens but not after that.)
|
||||
_load_prompt = True
|
||||
|
||||
# The screen that we go to when entering the game menu.
|
||||
_game_menu_screen = "_save_screen"
|
||||
|
||||
######################################################################
|
||||
# Next, support code.
|
||||
|
||||
# This is used to store scratch data that's used by the
|
||||
# library, but shouldn't be saved out as part of the savegame.
|
||||
_scratch = object()
|
||||
|
||||
# This returns a window containing the game menu navigation
|
||||
# buttons, set up to jump to the appropriate screen sections.
|
||||
#
|
||||
# This can be overridden by user code. It's called with the
|
||||
# name of the selected screen... one of "mainmenu", "prefs",
|
||||
# "save", "load", or "quit", at least for the default game
|
||||
# menu. If None, then it's an indication that none of the
|
||||
# nav buttons should be shown.
|
||||
def _game_nav(screen, buttons=True):
|
||||
|
||||
ui.add(renpy.Keymap(toggle_fullscreen = renpy.toggle_fullscreen))
|
||||
ui.add(renpy.Keymap(game_menu=ui.jumps("_noisy_return")))
|
||||
|
||||
### gm_root_window default
|
||||
# (window) The style used for the root window of the game
|
||||
# menu. This is primarily used to change the background of
|
||||
# the game menu.
|
||||
|
||||
ui.window(style='gm_root_window')
|
||||
ui.null()
|
||||
|
||||
if not screen:
|
||||
return
|
||||
|
||||
### gm_nav_window default
|
||||
# (window) The style used by a window containing
|
||||
# buttons that allow the user to navigate through the
|
||||
# different screens of the game menu.
|
||||
|
||||
### gm_nav_vbox thin_vbox
|
||||
# (box) The style that is used by the box inside the
|
||||
# gm_nav_window
|
||||
|
||||
if library.game_menu_positions:
|
||||
ui.fixed()
|
||||
else:
|
||||
ui.window(style='gm_nav_window')
|
||||
ui.vbox(focus='gm_nav', style='gm_nav_vbox')
|
||||
|
||||
for key, label, clicked, enabled in library.game_menu:
|
||||
|
||||
disabled = False
|
||||
|
||||
if not eval(enabled):
|
||||
disabled = True
|
||||
clicked = None
|
||||
|
||||
### gm_nav_button button
|
||||
# (window, hover) The style of an unselected game menu
|
||||
# navigation button.
|
||||
|
||||
### gm_nav_button_text button_text
|
||||
# (text, hover) The style of the text of an unselected game
|
||||
# menu navigation button.
|
||||
|
||||
### gm_nav_selected_button selected_button
|
||||
# (window, hover) The style of a selected game menu
|
||||
# navigation button.
|
||||
|
||||
### gm_nav_selected_button_text selected_button_text
|
||||
# (text, hover) The style of the text of a selected game
|
||||
# menu navigation button.
|
||||
|
||||
if library.game_menu_positions:
|
||||
kwargs = library.game_menu_positions.get(label, { })
|
||||
else:
|
||||
kwargs = { }
|
||||
|
||||
_button_factory(label, "gm_nav",
|
||||
selected=(key==screen),
|
||||
disabled=disabled,
|
||||
clicked=clicked, **kwargs)
|
||||
|
||||
ui.close()
|
||||
|
||||
# This is called from the game menu to interact with the
|
||||
# user. It suppresses all of the underlays and overlays.
|
||||
def _game_interact():
|
||||
|
||||
return ui.interact(suppress_underlay=True,
|
||||
suppress_overlay=True,
|
||||
mouse="gamemenu")
|
||||
|
||||
|
||||
# This renders a slot with a file in it, in the file picker.
|
||||
def _render_savefile(name, info, newest):
|
||||
|
||||
image, extra = info
|
||||
|
||||
|
||||
### file_picker_entry button
|
||||
# (window, hover) The style that is used for each of the
|
||||
# slots in the file picker.
|
||||
ui.button(style='file_picker_entry',
|
||||
clicked=ui.returns(("return", (name, True))))
|
||||
|
||||
|
||||
### file_picker_entry_box thin_hbox
|
||||
# (box) The style that is used for the hbox inside of a
|
||||
# file picker entry.
|
||||
ui.hbox(style='file_picker_entry_box')
|
||||
|
||||
if not library.disable_thumbnails:
|
||||
ui.add(image)
|
||||
|
||||
### file_picker_text default
|
||||
# (text) A base style for all text that is displayed in the
|
||||
# file picker.
|
||||
|
||||
### file_picker_new file_picker_text
|
||||
# (text) The style that is applied to the number of the new slot in
|
||||
# the file picker.
|
||||
|
||||
### file_picker_old file_picker_text
|
||||
# (text) The style that is applied to the number of the old slot in
|
||||
# the file picker.
|
||||
|
||||
|
||||
if name == newest:
|
||||
ui.text(name + ". ", style='file_picker_new')
|
||||
else:
|
||||
ui.text(name + ". ", style='file_picker_old')
|
||||
|
||||
### file_picker_extra_info file_picker_text
|
||||
# (text) The style that is applied to extra info in the file
|
||||
# picker. The extra info is the save time, and the save_name
|
||||
# if one exists.
|
||||
ui.text(extra, style='file_picker_extra_info')
|
||||
|
||||
ui.close()
|
||||
|
||||
# This renders an empty slot in the file picker.
|
||||
def _render_new_slot(name, save):
|
||||
|
||||
if save:
|
||||
clicked=ui.returns(("return", (name, False)))
|
||||
enable_hover = True
|
||||
else:
|
||||
clicked = None
|
||||
enable_hover = True
|
||||
|
||||
ui.button(style='file_picker_entry',
|
||||
clicked=clicked,
|
||||
enable_hover=enable_hover)
|
||||
|
||||
ui.hbox(style='file_picker_entry_box')
|
||||
|
||||
if not library.disable_thumbnails:
|
||||
ui.null(width=library.thumbnail_width,
|
||||
height=library.thumbnail_height)
|
||||
|
||||
|
||||
ui.text(name + ". ", style='file_picker_old')
|
||||
|
||||
### file_picker_empty_slot file_picker_text
|
||||
# (text) The style that is used for the empty slot indicator
|
||||
# in the file picker.
|
||||
ui.text(_("Empty Slot."), style='file_picker_empty_slot')
|
||||
ui.close()
|
||||
|
||||
_scratch.file_picker_index = None
|
||||
|
||||
# This displays a file picker that can chose a save file from
|
||||
# the list of save files.
|
||||
def _file_picker(selected, save):
|
||||
|
||||
# The number of slots in a page.
|
||||
file_page_length = library.file_page_cols * library.file_page_rows
|
||||
|
||||
saves, newest = renpy.saved_games(regexp=r'[0-9]+')
|
||||
|
||||
# The index of the first entry in the page.
|
||||
fpi = _scratch.file_picker_index
|
||||
|
||||
if fpi is None:
|
||||
fpi = 0
|
||||
|
||||
if newest:
|
||||
fpi = (int(newest) - 1) // file_page_length * file_page_length
|
||||
|
||||
if fpi < 0:
|
||||
fpi = 0
|
||||
|
||||
|
||||
while True:
|
||||
|
||||
if fpi < 0:
|
||||
fpi = 0
|
||||
|
||||
_scratch.file_picker_index = fpi
|
||||
|
||||
# Show Navigation
|
||||
_game_nav(selected)
|
||||
|
||||
|
||||
### file_picker_window default
|
||||
# (window) A window containing the file picker
|
||||
# that is used to choose slots for loading and saving.
|
||||
|
||||
### file_picker_window_vbox thin_vbox
|
||||
# (box) The vbox containing both the nav and the grid in
|
||||
# the file picker.
|
||||
|
||||
ui.window(style='file_picker_window')
|
||||
ui.vbox(style='file_picker_window_vbox') # whole thing.
|
||||
|
||||
if not library.disable_file_pager:
|
||||
|
||||
### file_picker_navbox thick_hbox
|
||||
# (box) The box containing the naviation (next/previous)
|
||||
# buttons in the file picker.
|
||||
|
||||
### file_picker_nav_button button
|
||||
# (window, hover) The style that is used for enabled file
|
||||
# picker navigation buttons.
|
||||
|
||||
### file_picker_nav_button_text button_text
|
||||
# (text) The style that is used for the label of enabled
|
||||
# file picker navigation buttons.
|
||||
|
||||
# Draw the navigation.
|
||||
ui.hbox(style='file_picker_navbox') # nav buttons.
|
||||
|
||||
def tb(cond, label, clicked):
|
||||
_button_factory(label, "file_picker_nav", disabled=not cond, clicked=clicked)
|
||||
|
||||
# Previous
|
||||
tb(fpi > 0, _('Previous'), ui.returns(("fpidelta", -1)))
|
||||
|
||||
# Quick Access
|
||||
for i in range(0, library.file_quick_access_pages):
|
||||
target = i * file_page_length
|
||||
tb(fpi != target, str(i + 1), ui.returns(("fpiset", target)))
|
||||
|
||||
# Next
|
||||
tb(True, _('Next'), ui.returns(("fpidelta", +1)))
|
||||
|
||||
# Done with nav buttons.
|
||||
ui.close()
|
||||
|
||||
# This draws a single slot.
|
||||
def entry(offset):
|
||||
i = fpi + offset
|
||||
|
||||
name = str(i + 1)
|
||||
|
||||
if name not in saves:
|
||||
_render_new_slot(name, save)
|
||||
else:
|
||||
_render_savefile(name, saves[name], newest)
|
||||
|
||||
### file_picker_grid default
|
||||
# The style of the grid containing the file
|
||||
# picker entries.
|
||||
|
||||
# Actually draw a slot.
|
||||
ui.grid(library.file_page_cols,
|
||||
library.file_page_rows,
|
||||
style='file_picker_grid',
|
||||
transpose=True) # slots
|
||||
|
||||
for i in range(0, file_page_length):
|
||||
entry(i)
|
||||
|
||||
ui.close() # slots
|
||||
|
||||
ui.close() # whole thing
|
||||
|
||||
result = _game_interact()
|
||||
type, value = result
|
||||
|
||||
if type == "return":
|
||||
return value
|
||||
|
||||
if type == "fpidelta":
|
||||
fpi += value * file_page_length
|
||||
|
||||
if type == "fpiset":
|
||||
fpi = value
|
||||
|
||||
|
||||
# This renders a yes/no prompt, as part of the game menu. If
|
||||
# screen is None, then it omits the game menu navigation.
|
||||
def _yesno_prompt(screen, message):
|
||||
"""
|
||||
@param screen: The screen button that should be highlighted when this prompt is shown. If None, then no game menu navigation is shown.
|
||||
|
||||
@param message: The message that is shown to the user to prompt them to answer yes or no.
|
||||
|
||||
This function returns True if the user clicks Yes, or False if the user clicks No.
|
||||
"""
|
||||
|
||||
_game_nav(screen)
|
||||
|
||||
### yesno_window default
|
||||
# (window) The style of a window containing a yes/no prompt.
|
||||
|
||||
### yesno_window_vbox thick_vbox
|
||||
# (box) The style of a box containing the widgets in a
|
||||
# yes/no prompt.
|
||||
|
||||
ui.window(style='yesno_window')
|
||||
ui.vbox(style='yesno_window_vbox')
|
||||
|
||||
### yesno_label default
|
||||
# (text) The style used for the prompt in a yes/no
|
||||
# dialog.
|
||||
|
||||
### yesno_button button
|
||||
# (window, hover) The style of yes/no buttons.
|
||||
|
||||
### yesno_button_text button_text
|
||||
# (window, hover) The style of yes/no button text.
|
||||
|
||||
_label_factory(message, "yesno", xpos=0.5, xanchor='center')
|
||||
|
||||
ui.grid(5, 1, xfill=True)
|
||||
|
||||
# The extra nulls are because we want equal whitespace surrounding
|
||||
# the two buttons. It should work as long as we have xfill=True
|
||||
ui.null()
|
||||
_button_factory("Yes", 'yesno', clicked=ui.returns(True), xpos=0.5, xanchor='center')
|
||||
ui.null()
|
||||
_button_factory("No", 'yesno', clicked=ui.returns(False), xpos=0.5, xanchor='center')
|
||||
ui.null()
|
||||
|
||||
ui.close()
|
||||
ui.close()
|
||||
|
||||
return _game_interact()
|
||||
|
||||
|
||||
def _show_exception(title, message):
|
||||
|
||||
### error_window default
|
||||
# (window) The style of the window containing internal error
|
||||
# messages.
|
||||
|
||||
### error_title default
|
||||
# (text) The style of the text containing the title of an
|
||||
# error message.
|
||||
|
||||
### error_body default
|
||||
# (text) The style of the body of an error message.
|
||||
|
||||
ui.window(style='error_window')
|
||||
ui.vbox()
|
||||
|
||||
ui.text(title, style='error_title')
|
||||
ui.text("")
|
||||
ui.text(message, style='error_body')
|
||||
ui.text("")
|
||||
ui.text(_("Please click to continue."), style='error_body')
|
||||
|
||||
ui.close()
|
||||
|
||||
ui.saybehavior()
|
||||
|
||||
ui.interact()
|
||||
|
||||
|
||||
library.old_names["Are you sure you want to quit?"] = "Are you sure you want to quit the game?"
|
||||
|
||||
def _quit_prompt(screen="quit"):
|
||||
|
||||
def prompt():
|
||||
return _yesno_prompt(screen, "Are you sure you want to quit?")
|
||||
|
||||
if renpy.invoke_in_new_context(prompt):
|
||||
renpy.quit()
|
||||
else:
|
||||
return
|
||||
|
||||
library.old_names["Are you sure you want to return to the main menu?\nThis will lose unsaved progress."] = "Are you sure you want to return to the main menu?\nThis will end your game."
|
||||
|
||||
def _mainmenu_prompt(screen="mainmenu"):
|
||||
|
||||
def prompt():
|
||||
return _yesno_prompt(screen, "Are you sure you want to return to the main menu?\nThis will lose unsaved progress.")
|
||||
|
||||
if renpy.invoke_in_new_context(prompt):
|
||||
renpy.full_restart()
|
||||
else:
|
||||
return
|
||||
|
||||
|
||||
|
||||
##############################################################################
|
||||
# The actual menus begin around here.
|
||||
|
||||
# First up, we have the code that is common to all of the entry points into
|
||||
# the game.
|
||||
|
||||
|
||||
# This is the code that executes when entering a menu context, except
|
||||
# for the scene statement.
|
||||
label _enter_menu_without_scene:
|
||||
$ renpy.movie_stop()
|
||||
$ renpy.take_screenshot((library.thumbnail_width, library.thumbnail_height))
|
||||
|
||||
# This may be changed, if we are already in the main menu.
|
||||
$ renpy.context().main_menu = False
|
||||
|
||||
return
|
||||
|
||||
label _enter_menu:
|
||||
call _enter_menu_without_scene from _call_enter_menu_without_scene_1
|
||||
scene
|
||||
return
|
||||
|
||||
# Factored this all into one place, to make our lives a bit easier.
|
||||
label _enter_game_menu:
|
||||
call _enter_menu from _call__enter_menu_2
|
||||
if library.enter_transition:
|
||||
$ renpy.transition(library.enter_transition)
|
||||
|
||||
if renpy.has_label("enter_game_menu"):
|
||||
call expression "enter_game_menu" from _call_enter_game_menu_1
|
||||
|
||||
return
|
||||
|
||||
##############################################################################
|
||||
# Now, we have the actual entry points into the various menu screens.
|
||||
|
||||
# Entry points from the game into menu-space.
|
||||
label _game_menu:
|
||||
call _enter_game_menu from _call__enter_game_menu_0
|
||||
jump expression _game_menu_screen
|
||||
|
||||
|
||||
label _game_menu_save:
|
||||
call _enter_game_menu from _call__enter_game_menu_1
|
||||
jump _save_screen
|
||||
|
||||
label _game_menu_load:
|
||||
call _enter_game_menu from _call__enter_game_menu_2
|
||||
jump _load_screen
|
||||
|
||||
label _game_menu_preferences:
|
||||
call _enter_game_menu from _call__enter_game_menu_3
|
||||
jump _prefs_screen
|
||||
|
||||
label _confirm_quit:
|
||||
call _enter_menu from _call__enter_menu_3
|
||||
$ _quit_prompt(None)
|
||||
return
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Finally, we have the actual menu screens that are shown to the user.
|
||||
|
||||
init -499:
|
||||
$ library.old_names["Loading will lose unsaved progress.\nAre you sure you want to do this?"] = "Loading a new game will end your current game.\nAre you sure you want to do this?"
|
||||
|
||||
label _load_screen:
|
||||
|
||||
python hide:
|
||||
|
||||
fn, exists = _file_picker("load", False )
|
||||
|
||||
if not renpy.context().main_menu and _load_prompt:
|
||||
if _yesno_prompt("load", "Loading will lose unsaved progress.\nAre you sure you want to do this?"):
|
||||
renpy.load(fn)
|
||||
else:
|
||||
renpy.load(fn)
|
||||
|
||||
jump _load_screen
|
||||
|
||||
label _save_screen:
|
||||
$ _fn, _exists = _file_picker("save", True)
|
||||
|
||||
if not _exists or _yesno_prompt("save", "Are you sure you want to overwrite your save?"):
|
||||
python hide:
|
||||
|
||||
if save_name:
|
||||
full_save_name = "\n" + save_name
|
||||
else:
|
||||
full_save_name = ""
|
||||
|
||||
try:
|
||||
renpy.save(_fn, renpy.time.strftime("%b %d, %H:%M") +
|
||||
full_save_name)
|
||||
|
||||
except Exception, e:
|
||||
|
||||
if config.debug:
|
||||
raise
|
||||
|
||||
message = ( _("The error message was:\n\n") +
|
||||
e.__class__.__name__ + ": " + unicode(e) + "\n\n" +
|
||||
_("You may want to try saving in a different slot, or playing for a while and trying again later."))
|
||||
|
||||
_show_exception(_("Save Failed."), message)
|
||||
|
||||
|
||||
jump _save_screen
|
||||
|
||||
label _quit:
|
||||
$ renpy.quit()
|
||||
|
||||
# Make some noise, then return.
|
||||
label _noisy_return:
|
||||
$ renpy.play(library.exit_sound)
|
||||
|
||||
# Return to the game.
|
||||
label _return:
|
||||
|
||||
if renpy.context().main_menu:
|
||||
jump _main_menu
|
||||
|
||||
if library.exit_transition:
|
||||
$ renpy.transition(library.exit_transition)
|
||||
|
||||
return
|
||||
@@ -0,0 +1,246 @@
|
||||
# This file consists of renpy functions that aren't expected to be
|
||||
# touched by the user too much. We reserve the _ prefix for names
|
||||
# defined in the library.
|
||||
|
||||
# It's strongly reccomended that you don't edit this file, as future
|
||||
# releases of Ren'Py will probably change this file to include more
|
||||
# functionality.
|
||||
|
||||
# It's also strongly recommended that you leave this file in the
|
||||
# game directory, so its functionality is included in your game.
|
||||
|
||||
|
||||
|
||||
init -500:
|
||||
python:
|
||||
|
||||
# These are settings that the user can tweak to control the
|
||||
# look of the main menu and the load/save/escape screens.
|
||||
|
||||
# The class that stores library variables.
|
||||
class _Library(object):
|
||||
def __setattr__(self, name, value):
|
||||
|
||||
if getattr(self, 'lock', False):
|
||||
if not name in vars(self):
|
||||
raise Exception('library.%s is not a known configuration variable.' % name)
|
||||
|
||||
object.__setattr__(self, name, value)
|
||||
|
||||
|
||||
|
||||
|
||||
# Used to store library settings.
|
||||
library = _Library()
|
||||
|
||||
# The minimum version of the module we work with. Don't change
|
||||
# this unless you know what you're doing.
|
||||
library.module_version = 4008002
|
||||
|
||||
# Should we warn the user if the module is missing or has a bad
|
||||
# version?
|
||||
library.module_warning = False
|
||||
|
||||
# Used to translate strings in the library.
|
||||
library.translations = { }
|
||||
|
||||
# Used internally to maintain compatiblity with old
|
||||
# translations of strings.
|
||||
library.old_names = { }
|
||||
|
||||
# True if the skip indicator should be shown.
|
||||
library.skip_indicator = True
|
||||
|
||||
# This is updated to give the user an idea of where a save is
|
||||
# taking place.
|
||||
save_name = ''
|
||||
|
||||
def _button_factory(label,
|
||||
type=None,
|
||||
selected=None,
|
||||
disabled=False,
|
||||
clicked=None,
|
||||
**properties):
|
||||
"""
|
||||
This function is called to create the various buttons used
|
||||
in the game menu. By overriding this function, one can
|
||||
(for example) replace the default textbuttons with image buttons.
|
||||
When it is called, it's expected to add a button to the screen.
|
||||
|
||||
@param label: The label of this button, before translation.
|
||||
|
||||
@param type: The type of the button. One of "mm" (main menu),
|
||||
"gm_nav" (game menu), "file_picker_nav", "yesno", or "prefs".
|
||||
|
||||
@param selected: True if the button is selected, False if not,
|
||||
or None if it doesn't matter.
|
||||
|
||||
@param disabled: True if the button is disabled, False if not.
|
||||
|
||||
@param clicked: A function that should be executed when the
|
||||
button is clicked.
|
||||
|
||||
@param properties: Addtional layout properties.
|
||||
"""
|
||||
|
||||
style = type
|
||||
|
||||
if selected and not disabled:
|
||||
style += "_selected"
|
||||
|
||||
if disabled:
|
||||
clicked = None
|
||||
|
||||
style = style + "_button"
|
||||
text_style = style + "_text"
|
||||
|
||||
ui.textbutton(_(label), style=style, text_style=text_style, clicked=clicked, **properties)
|
||||
|
||||
def _label_factory(label, type, **properties):
|
||||
"""
|
||||
This function is called to create a new label. It can be
|
||||
overridden by the user to change how these labels are created.
|
||||
|
||||
@param label: The label of the box.
|
||||
|
||||
@param type: "prefs" or "yesno".
|
||||
|
||||
@param properties: This may contain position properties.
|
||||
"""
|
||||
|
||||
ui.text(_(label), style=type + "_label", **properties)
|
||||
|
||||
# The function that's used to translate strings in the game menu.
|
||||
def _(s):
|
||||
"""
|
||||
Translates s into another language or something.
|
||||
"""
|
||||
|
||||
if s in library.translations:
|
||||
return library.translations[s]
|
||||
|
||||
if s in library.old_names and library.old_names[s] in library.translations:
|
||||
return library.translations[library.old_names[s]]
|
||||
|
||||
return s
|
||||
|
||||
# Are the windows currently hidden?
|
||||
_windows_hidden = False
|
||||
|
||||
# Set up the default keymap.
|
||||
python hide:
|
||||
|
||||
# Called to make a screenshot happen.
|
||||
def screenshot():
|
||||
import os.path
|
||||
|
||||
i = 1
|
||||
while True:
|
||||
fn = "screenshot%04d.bmp" % i
|
||||
if not os.path.exists(fn):
|
||||
break
|
||||
i += 1
|
||||
|
||||
|
||||
renpy.screenshot(fn)
|
||||
|
||||
def invoke_game_menu():
|
||||
renpy.play(library.enter_sound)
|
||||
renpy.call_in_new_context('_game_menu')
|
||||
|
||||
def toggle_skipping():
|
||||
|
||||
if not config.skipping:
|
||||
config.skipping = "slow"
|
||||
else:
|
||||
config.skipping = None
|
||||
|
||||
renpy.restart_interaction()
|
||||
|
||||
def fast_skip():
|
||||
if config.fast_skipping or config.developer:
|
||||
config.skipping = "fast"
|
||||
|
||||
# The default keymap.
|
||||
km = renpy.Keymap(
|
||||
rollback = renpy.rollback,
|
||||
screenshot = screenshot,
|
||||
toggle_fullscreen = renpy.toggle_fullscreen,
|
||||
toggle_music = renpy.toggle_music,
|
||||
toggle_skip = toggle_skipping,
|
||||
fast_skip = fast_skip,
|
||||
game_menu = invoke_game_menu,
|
||||
hide_windows = renpy.curried_call_in_new_context("_hide_windows"),
|
||||
launch_editor = renpy.launch_editor,
|
||||
)
|
||||
|
||||
config.underlay = [ km ]
|
||||
|
||||
|
||||
# The skip indicator.
|
||||
python hide:
|
||||
|
||||
def skip_indicator():
|
||||
|
||||
### skip_indicator default
|
||||
# (text) The style and placement of the skip indicator.
|
||||
|
||||
if config.skipping == "slow" and library.skip_indicator:
|
||||
ui.text(_("Skip Mode"), style='skip_indicator')
|
||||
|
||||
if config.skipping == "fast" and library.skip_indicator:
|
||||
ui.text(_("Fast Skip Mode"), style='skip_indicator')
|
||||
|
||||
config.overlay_functions.append(skip_indicator)
|
||||
|
||||
return
|
||||
|
||||
label _hide_windows:
|
||||
|
||||
if _windows_hidden:
|
||||
return
|
||||
|
||||
python:
|
||||
_windows_hidden = True
|
||||
ui.saybehavior()
|
||||
ui.interact(suppress_overlay=True)
|
||||
_windows_hidden = False
|
||||
|
||||
return
|
||||
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Code for the game menu.
|
||||
|
||||
|
||||
|
||||
# This code here handles check for the correct version of the Ren'Py module.
|
||||
|
||||
label _check_module:
|
||||
|
||||
if not library.module_warning:
|
||||
return
|
||||
|
||||
python hide:
|
||||
module_info = _("While Ren'Py games are playable without the _renpy module, some features may be disabled. For more information, read the module/README.txt file or go to http://www.bishoujo.us/renpy/.")
|
||||
|
||||
if renpy.module_version() == 0:
|
||||
_show_exception(_("_renpy module not found."),
|
||||
_("The _renpy module could not be loaded on your system.") + "\n\n" + module_info)
|
||||
elif renpy.module_version() < library.module_version:
|
||||
_show_exception(_("Old _renpy module found."),
|
||||
_("An old version (%d) of the Ren'Py module was found on your system, while this game requires version %d.") % (renpy.module_version(), library.module_version) + "\n\n" + module_info)
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
init -401:
|
||||
# Random nice things to have.
|
||||
$ centered = Character(None, what_style="centered_text", window_style="centered_window")
|
||||
image text = renpy.ParameterizedText(style="centered_text")
|
||||
|
||||
# Lock the library object.
|
||||
$ library.lock = True
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
# This file contains code fo the main menu, and anything else that
|
||||
# happens upon initial execution of a Ren'Py program.
|
||||
|
||||
init -498:
|
||||
|
||||
python hide:
|
||||
|
||||
library.old_names['Quit'] = 'Quit Game'
|
||||
|
||||
# The contents of the main menu.
|
||||
library.main_menu = [
|
||||
( "Start Game", "start", 'True'),
|
||||
( "Continue Game", ui.jumps("_load_screen"), 'True' ),
|
||||
( "Preferences", ui.jumps("_prefs_screen"), 'True' ),
|
||||
( "Quit", ui.jumps("_quit"), 'True' ),
|
||||
]
|
||||
|
||||
# If not None, this is used to fix the positions of the
|
||||
# things in the main menu.
|
||||
library.main_menu_positions = None
|
||||
|
||||
# This is the true starting point of the program. Sssh... Don't
|
||||
# tell anyone.
|
||||
label _start:
|
||||
|
||||
call _check_module from _call__check_module_1
|
||||
|
||||
if renpy.has_label("splashscreen") and not _restart:
|
||||
call expression "splashscreen" from _call_splashscreen_1
|
||||
|
||||
# Clean out any residual scene from the splashscreen.
|
||||
scene black
|
||||
|
||||
$ renpy.call_in_new_context("_enter_main_menu")
|
||||
|
||||
# Should never happen... but might as well do something
|
||||
jump start
|
||||
|
||||
# At this point, we've been switched into a new context. So we
|
||||
# initialize it.
|
||||
label _enter_main_menu:
|
||||
|
||||
call _enter_menu from _call__enter_menu_1
|
||||
|
||||
$ renpy.context().main_menu = True
|
||||
|
||||
# This is called to show the main menu to the user.
|
||||
label _main_menu:
|
||||
|
||||
# Let the user completely override the main menu. (But please note
|
||||
# it still lives in the menu context, rather than the game context.)
|
||||
if renpy.has_label("main_menu"):
|
||||
jump expression "main_menu"
|
||||
|
||||
# This is the default main menu, which we get if the user hasn't
|
||||
# defined his own, or if that function calls this explicitly.
|
||||
label _library_main_menu:
|
||||
|
||||
scene
|
||||
|
||||
python hide:
|
||||
|
||||
ui.add(renpy.Keymap(toggle_fullscreen = renpy.toggle_fullscreen))
|
||||
|
||||
### mm_root_window default
|
||||
# (window) The style used for the root window of the main
|
||||
# menu. This is primarily used to set a background for the
|
||||
# main menu.
|
||||
|
||||
ui.window(style='mm_root_window')
|
||||
ui.fixed()
|
||||
|
||||
### mm_menu_window default
|
||||
# (window) A window that contains the choices in
|
||||
# the main menu. Change this to change the placement of
|
||||
# these choices on the main menu screen.
|
||||
|
||||
### mm_menu_window_vbox thin_vbox
|
||||
# (box) The vbox containing the main menu choices.
|
||||
|
||||
if library.main_menu_positions:
|
||||
ui.fixed()
|
||||
else:
|
||||
ui.window(style='mm_menu_window')
|
||||
ui.vbox(style='mm_menu_window_vbox')
|
||||
|
||||
for text, clicked, enabled in library.main_menu:
|
||||
|
||||
if isinstance(clicked, basestring):
|
||||
clicked = ui.jumpsoutofcontext(clicked)
|
||||
|
||||
### mm_button button
|
||||
# (window, hover) The style that is used on buttons that are
|
||||
# part of the main menu.
|
||||
|
||||
### mm_button_text button_text
|
||||
# (text, hover) The style that is used for the labels of
|
||||
# buttons that are part of the main menu.
|
||||
|
||||
if library.main_menu_positions:
|
||||
kwargs = library.main_menu_positions.get(text, { })
|
||||
else:
|
||||
kwargs = { }
|
||||
|
||||
if not eval(enabled):
|
||||
clicked = None
|
||||
disabled = True
|
||||
else:
|
||||
disabled = False
|
||||
|
||||
_button_factory(text, "mm", clicked=clicked, disabled=disabled, **kwargs)
|
||||
|
||||
ui.close()
|
||||
ui.close()
|
||||
|
||||
store._result = ui.interact(suppress_overlay = True,
|
||||
suppress_underlay = True,
|
||||
mouse="mainmenu")
|
||||
|
||||
# Computed jump to the appropriate label.
|
||||
jump _main_menu
|
||||
@@ -0,0 +1,654 @@
|
||||
# This file contains the code to implement the Ren'Py preferences
|
||||
# screen.
|
||||
|
||||
init -450:
|
||||
python:
|
||||
|
||||
# This is a map from the name of the style that is applied to
|
||||
# a list of preferences that should be placed into a vbox
|
||||
# with that style.
|
||||
library.preferences = { }
|
||||
|
||||
# Ditto, for joystick preferences.
|
||||
library.joystick_preferences = { }
|
||||
|
||||
# This is a map from preference name to that preference
|
||||
# object, that can be used in rearranging preferences.
|
||||
library.all_preferences = { }
|
||||
|
||||
# If true, the preference choices will be arranged in an
|
||||
# hbox.
|
||||
library.hbox_pref_choices = False
|
||||
|
||||
# A list of (readable name, synthetic key) tuples
|
||||
# corresponding to joystick events.
|
||||
library.joystick_keys = [
|
||||
('Left', 'joy_left'),
|
||||
('Right', 'joy_right'),
|
||||
('Up', 'joy_up'),
|
||||
('Down', 'joy_down'),
|
||||
('Select/Dismiss', 'joy_dismiss'),
|
||||
('Rollback', 'joy_rollback'),
|
||||
('Hold to Skip', 'joy_holdskip'),
|
||||
('Toggle Skip', 'joy_toggleskip'),
|
||||
('Hide Text', 'joy_hide'),
|
||||
('Menu', 'joy_menu'),
|
||||
]
|
||||
|
||||
def _prefs_screen_run(prefs_map):
|
||||
|
||||
_game_nav("prefs")
|
||||
|
||||
### prefs_window default
|
||||
# (window) A window containing all preferences.
|
||||
|
||||
ui.window(style='prefs_window')
|
||||
ui.fixed()
|
||||
|
||||
for style, prefs in prefs_map.iteritems():
|
||||
|
||||
ui.vbox(style=style)
|
||||
for i in prefs:
|
||||
i.render_preference()
|
||||
ui.close()
|
||||
|
||||
ui.close()
|
||||
|
||||
_game_interact()
|
||||
|
||||
|
||||
class _Preference(object):
|
||||
"""
|
||||
This is a class that's used to represent a multiple-choice
|
||||
preference.
|
||||
"""
|
||||
|
||||
def __init__(self, name, field, values, base=_preferences):
|
||||
"""
|
||||
@param name: The name of this preference. It will be
|
||||
displayed to the user.
|
||||
|
||||
@param variable: The field on the base object
|
||||
that will be assigned the selected value. This field
|
||||
must exist.
|
||||
|
||||
@param values: A list of value name, value, condition
|
||||
triples. The value name is the name of this value that
|
||||
will be shown to the user. The value is the literal
|
||||
python value that will be assigned if this value is
|
||||
selected. The condition is a condition that will be
|
||||
evaluated to determine if this is a legal value. If no
|
||||
conditions are true, this preference will not be
|
||||
displayed to the user. A condition of None is always
|
||||
considered to be True.
|
||||
|
||||
@param base: The base object on which the variable is
|
||||
read from and set. This defaults to _preferences,
|
||||
the user preferences object.
|
||||
"""
|
||||
|
||||
self.name = name
|
||||
self.field = field
|
||||
self.values = values
|
||||
self.base = base
|
||||
|
||||
library.all_preferences[name] = self
|
||||
|
||||
def render_preference(self):
|
||||
values = [ (name, val) for name, val, cond in self.values
|
||||
if cond is None or renpy.eval(cond) ]
|
||||
|
||||
if not values:
|
||||
return
|
||||
|
||||
### prefs_pref default
|
||||
# (window) A window containing an individual
|
||||
# preference.
|
||||
|
||||
### prefs_pref_vbox thin_vbox
|
||||
# (box) The style of the vbox containing a preference.
|
||||
|
||||
### prefs_label default
|
||||
# (text) The style that is applied to the label of
|
||||
# a block of preferences.
|
||||
|
||||
|
||||
ui.window(style='prefs_pref')
|
||||
ui.vbox(style='prefs_pref_vbox')
|
||||
|
||||
_label_factory(self.name, "prefs")
|
||||
|
||||
cur = getattr(self.base, self.field)
|
||||
|
||||
### prefs_hbox default
|
||||
# If library.hbox_pref_choices is True, the style
|
||||
# of the hbox containing the choices.
|
||||
|
||||
if library.hbox_pref_choices:
|
||||
ui.hbox(style='prefs_hbox')
|
||||
|
||||
for name, value in values:
|
||||
|
||||
### prefs_button button
|
||||
# (window, hover) The style of an unselected preferences
|
||||
# button.
|
||||
|
||||
### prefs_button_text button_text
|
||||
# (text, hover) The style of the text of an unselected
|
||||
# preferences button.
|
||||
|
||||
### prefs_selected_button selected_button
|
||||
# (window, hover) The style of a selected preferences
|
||||
# button.
|
||||
|
||||
### prefs_selected_button_text selected_button_text
|
||||
# (text, hover) The style of the text of a selected
|
||||
# preferences button.
|
||||
|
||||
def clicked(value=value):
|
||||
setattr(self.base, self.field, value)
|
||||
return True
|
||||
|
||||
_button_factory(name, "prefs",
|
||||
selected=cur==value,
|
||||
clicked=clicked)
|
||||
|
||||
if library.hbox_pref_choices:
|
||||
ui.close()
|
||||
|
||||
ui.close()
|
||||
|
||||
|
||||
class _VolumePreference(object):
|
||||
"""
|
||||
This represents a preference that controls one of the
|
||||
volumes in the system. It is represented as a slider bar,
|
||||
and a button that can be pushed to play a sample sound on
|
||||
a channel.
|
||||
"""
|
||||
|
||||
def __init__(self, name, mixer, enable='True', sound='None', channel=0):
|
||||
"""
|
||||
@param name: The name of this preference, as shown to the user.
|
||||
|
||||
@param mixer: The mixer this preference controls.
|
||||
|
||||
@param enable: A string giving a python expression. If
|
||||
the expression is evaluates to false, this preference
|
||||
is not shown.
|
||||
|
||||
@param sound: A string that is evaluated to yield
|
||||
another string. The yielded string is expected to give
|
||||
a sound file, which is played as the sample sound. (We
|
||||
apologize for the convolution of this.)
|
||||
|
||||
@param channel: The number of the channel the sample
|
||||
sound is played on.
|
||||
"""
|
||||
|
||||
self.name = name
|
||||
self.mixer = mixer
|
||||
self.enable = enable
|
||||
self.sound = sound
|
||||
self.channel = channel
|
||||
|
||||
library.all_preferences[name] = self
|
||||
|
||||
def render_preference(self):
|
||||
|
||||
if not eval(self.enable):
|
||||
return
|
||||
|
||||
sound = eval(self.sound)
|
||||
|
||||
ui.window(style='prefs_pref')
|
||||
ui.vbox(style='prefs_pref_vbox')
|
||||
|
||||
_label_factory(self.name, "prefs")
|
||||
|
||||
def changed(v):
|
||||
|
||||
if v == 0:
|
||||
_preferences.mute[self.mixer] = True
|
||||
else:
|
||||
_preferences.mute[self.mixer] = False
|
||||
|
||||
v /= 128.0
|
||||
|
||||
_preferences.volumes[self.mixer] = v
|
||||
|
||||
### prefs_volume_slider prefs_slider
|
||||
# (bar) The style that is applied to volume
|
||||
# sliders.
|
||||
|
||||
ui.bar(128,
|
||||
int(_preferences.volumes[self.mixer] * 128),
|
||||
changed=changed,
|
||||
style='prefs_volume_slider')
|
||||
|
||||
if sound:
|
||||
def clicked():
|
||||
renpy.sound.play(sound, channel=self.channel)
|
||||
|
||||
_button_factory("Test", "prefs", clicked=clicked)
|
||||
|
||||
ui.close()
|
||||
|
||||
class _SliderPreference(object):
|
||||
"""
|
||||
A class that represents a preference that is controlled by a
|
||||
slider.
|
||||
"""
|
||||
|
||||
def __init__(self, name, range, get, set, enable='True'):
|
||||
"""
|
||||
@param set: The name of this preference, that is shown to the user.
|
||||
|
||||
@param range: An integer giving the maximum value of
|
||||
this slider. The slider goes from 0 to range.
|
||||
|
||||
@param get: A function that's called to get the
|
||||
initial value of the slider. It's called with no
|
||||
arguments, and should return an integet between 0 and
|
||||
range, inclusive.
|
||||
|
||||
@param set: A function that's called when the value of
|
||||
the slider is set by the user. It is called with a
|
||||
single integer, in the range 0 to range, inclusive.
|
||||
|
||||
@param enable: A string giving a python expression. If
|
||||
the expression is evaluates to false, this preference
|
||||
is not shown.
|
||||
"""
|
||||
|
||||
self.name = name
|
||||
self.range = range
|
||||
self.get = get
|
||||
self.set = set
|
||||
self.enable = enable
|
||||
|
||||
library.all_preferences[name] = self
|
||||
|
||||
def render_preference(self):
|
||||
|
||||
if not eval(self.enable):
|
||||
return
|
||||
|
||||
ui.window(style='prefs_pref')
|
||||
ui.vbox(style='prefs_pref_vbox')
|
||||
|
||||
_label_factory(self.name, "prefs")
|
||||
|
||||
def changed(v):
|
||||
self.set(v)
|
||||
|
||||
### prefs_slider bar
|
||||
# (bar) The style that is applied to preference
|
||||
# sliders.
|
||||
|
||||
ui.bar(self.range,
|
||||
self.get(),
|
||||
changed=changed,
|
||||
style='prefs_slider')
|
||||
|
||||
ui.close()
|
||||
|
||||
|
||||
class _PreferenceSpinner(object):
|
||||
"""
|
||||
This is a class that's used to represent a preference
|
||||
spinner, which is a preference that can be incremented
|
||||
and decremented, when shown to the user.
|
||||
"""
|
||||
|
||||
def __init__(self, name, field, minimum, maximum, delta,
|
||||
cond = "True", render = lambda x : str(x),
|
||||
base=_preferences):
|
||||
"""
|
||||
@param name: The name of this preference, that is presented
|
||||
to the user.
|
||||
|
||||
@param field: The name of the field on the base object
|
||||
that is updated by this spinner.
|
||||
|
||||
@param minimum: The minimum value that this spinner can set
|
||||
the value to.
|
||||
|
||||
@param maximum: The maximum value that this spinner can set
|
||||
the value to.
|
||||
|
||||
@param delta: The delta by which this spinner is
|
||||
incremented or decremented.
|
||||
|
||||
@param cond: If this condition is not true, this spinner is
|
||||
not shown.
|
||||
|
||||
@param render: This function is called with the value of
|
||||
the field, and is expected to render that value to a
|
||||
string.
|
||||
|
||||
@param base: The base object that this spinner updates
|
||||
the field on. It defaults to _preferences, the preferences
|
||||
object.
|
||||
"""
|
||||
|
||||
self.name = name
|
||||
self.field = field
|
||||
self.minimum = minimum
|
||||
self.maximum = maximum
|
||||
self.delta = delta
|
||||
self.cond = cond
|
||||
self.render = render
|
||||
self.base = base
|
||||
|
||||
library.all_preferences[name] = self
|
||||
|
||||
def render_preference(self):
|
||||
|
||||
### prefs_spinner default
|
||||
# The position of the prefs spinner.
|
||||
|
||||
### prefs_spinner_label prefs_label
|
||||
# (text) This is the style that displays the value of a
|
||||
# preference spinner.
|
||||
|
||||
### prefs_spinner_button prefs_button
|
||||
# (window, hover) The style of the + or - buttons in a
|
||||
# preference spinner.
|
||||
|
||||
### prefs_spinner_button_text prefs_button_text
|
||||
# (text, hover) The style of the text of the + and - buttons
|
||||
# in a preference spinner.
|
||||
|
||||
if not renpy.eval(self.cond):
|
||||
return
|
||||
|
||||
ui.window(style='prefs_pref')
|
||||
ui.vbox(style='prefs_pref_vbox')
|
||||
|
||||
_label_factory(self.name, "prefs")
|
||||
|
||||
cur = getattr(self.base, self.field)
|
||||
|
||||
|
||||
def minus_clicked():
|
||||
value = cur - self.delta
|
||||
value = max(self.minimum, value)
|
||||
setattr(self.base, self.field, value)
|
||||
return True
|
||||
|
||||
def plus_clicked():
|
||||
value = cur + self.delta
|
||||
value = min(self.maximum, value)
|
||||
setattr(self.base, self.field, value)
|
||||
return True
|
||||
|
||||
ui.hbox(style='prefs_spinner')
|
||||
_button_factory("-", "prefs_spinner", clicked=minus_clicked)
|
||||
_label_factory(self.render(cur), "prefs_spinner")
|
||||
_button_factory("+", "prefs_spinner", clicked=plus_clicked)
|
||||
ui.close()
|
||||
|
||||
ui.close()
|
||||
|
||||
|
||||
class _JoystickPreference(object):
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
library.all_preferences[name] = self
|
||||
|
||||
def render_preference(self):
|
||||
|
||||
### prefs_js_button prefs_button
|
||||
# (window, hover) The style of buttons giving a joystick mapping.
|
||||
|
||||
### prefs_js_button_text prefs_button_text
|
||||
# (text, hover) The style of the text in buttons giving a joystick mapping.
|
||||
|
||||
### joy_window prefs_window
|
||||
# (window) The window containing the joystick message.
|
||||
|
||||
### joy_vbox thick_vbox
|
||||
# (window) The vbox containing the joistick mapping message.
|
||||
|
||||
### joyfunc_label prefs_label
|
||||
# (text, position) The style of the joystick mapping function name.
|
||||
|
||||
### joyprompt_label prefs_label
|
||||
# (text, position) The style of the joystick mapping prompt message.
|
||||
|
||||
|
||||
def set_binding(key, label):
|
||||
_game_nav(None)
|
||||
|
||||
ui.window(style='joy_window')
|
||||
ui.vbox(style='joy_vbox')
|
||||
_label_factory(_("Joystick Mapping") + " - " + _(label), "joyfunc")
|
||||
_label_factory('Move the joystick or press a button to create the mapping. Click the mouse to remove the mapping.', 'joyprompt')
|
||||
ui.close()
|
||||
|
||||
ui.saybehavior()
|
||||
ui.add(renpy.display.joystick.JoyBehavior())
|
||||
binding = _game_interact()
|
||||
|
||||
if not isinstance(binding, basestring):
|
||||
del _preferences.joymap[key]
|
||||
else:
|
||||
_preferences.joymap[key] = binding
|
||||
|
||||
|
||||
ui.window(style='prefs_pref')
|
||||
ui.vbox(style='prefs_pref_vbox')
|
||||
|
||||
_label_factory(self.name, 'prefs')
|
||||
|
||||
|
||||
|
||||
for label, key in library.joystick_keys:
|
||||
|
||||
def clicked(label=label, key=key):
|
||||
renpy.invoke_in_new_context(set_binding, key, label)
|
||||
return True
|
||||
|
||||
_button_factory(_(label) + " - " + _(_preferences.joymap.get(key, "Not Assigned")), "prefs_js", clicked=clicked)
|
||||
|
||||
# def clicked():
|
||||
# for label, key in library.joystick_keys:
|
||||
# renpy.invoke_in_new_context(set_binding, key, label)
|
||||
|
||||
# return True
|
||||
|
||||
# _button_factory("Assign All Mappings", "prefs_js", clicked=clicked)
|
||||
|
||||
ui.close()
|
||||
|
||||
class _JumpPreference(object):
|
||||
|
||||
def __init__(self, name, target, condition="True"):
|
||||
self.name = name
|
||||
self.target = target
|
||||
self.condition = condition
|
||||
|
||||
library.all_preferences[name] = self
|
||||
|
||||
def render_preference(self):
|
||||
|
||||
### prefs_jump prefs_pref
|
||||
# (window) The style of a window containing a jump preference.
|
||||
|
||||
### prefs_jump_button prefs_button
|
||||
# (window, hover) The style of a jump preference button.
|
||||
|
||||
### prefs_jump_button_text prefs_button_text
|
||||
# (text, hover) The style of jump preference button text.
|
||||
|
||||
ui.window(style='prefs_jump')
|
||||
|
||||
if eval(self.condition):
|
||||
clicked=ui.jumps(self.target)
|
||||
else:
|
||||
clicked=None
|
||||
|
||||
_button_factory(self.name, 'prefs_jump', clicked=clicked)
|
||||
|
||||
def _remove_preference(name):
|
||||
"""
|
||||
Removes the preference with the given name from the
|
||||
preferences menu.
|
||||
"""
|
||||
|
||||
pref = library.all_preferences.get(name, None)
|
||||
if not pref:
|
||||
return
|
||||
|
||||
for k, v in library.preferences.iteritems():
|
||||
if pref in v:
|
||||
v.remove(pref)
|
||||
|
||||
|
||||
python hide:
|
||||
|
||||
# Enablers for some preferences.
|
||||
library.has_music = True
|
||||
library.has_sound = True
|
||||
library.sample_sound = None
|
||||
library.has_transitions = True
|
||||
library.has_cps = True
|
||||
library.has_afm = True
|
||||
library.has_skipping = True
|
||||
library.has_skip_after_choice = True
|
||||
|
||||
|
||||
# Left
|
||||
|
||||
pl1 = _Preference('Display', 'fullscreen', [
|
||||
('Window', False, None),
|
||||
('Fullscreen', True, None),
|
||||
])
|
||||
|
||||
pl2 = _Preference('Transitions', 'transitions', [
|
||||
('All', 2, 'library.has_transitions'),
|
||||
('Some', 1, 'library.has_transitions and default_transition'),
|
||||
('None', 0, 'library.has_transitions'),
|
||||
])
|
||||
|
||||
|
||||
# Center
|
||||
|
||||
pc1 = _Preference('TAB and CTRL Skip', 'skip_unseen', [
|
||||
('Seen Messages', False, 'config.allow_skipping and library.has_skipping'),
|
||||
('All Messages', True, 'config.allow_skipping and library.has_skipping'),
|
||||
])
|
||||
|
||||
|
||||
pc2= _Preference('After Choices', 'skip_after_choices', [
|
||||
('Stop Skipping', False, 'config.allow_skipping and library.has_skip_after_choice'),
|
||||
('Continue Skipping', True, 'config.allow_skipping and library.has_skip_after_choice'),
|
||||
])
|
||||
|
||||
def cps_render(n):
|
||||
if n == 0:
|
||||
return "Infinite"
|
||||
else:
|
||||
return str(n)
|
||||
|
||||
def cps_get():
|
||||
cps = _preferences.text_cps
|
||||
if cps == 0:
|
||||
cps = 100
|
||||
else:
|
||||
cps -= 1
|
||||
return cps
|
||||
|
||||
def cps_set(cps):
|
||||
cps += 1
|
||||
if cps == 101:
|
||||
cps = 0
|
||||
_preferences.text_cps = cps
|
||||
|
||||
|
||||
pc3 = _SliderPreference('Text Speed', 100, cps_get, cps_set,
|
||||
'library.has_cps')
|
||||
|
||||
|
||||
def afm_get():
|
||||
afm = _preferences.afm_time
|
||||
if afm == 0:
|
||||
afm = 40
|
||||
else:
|
||||
afm -= 1
|
||||
return afm
|
||||
|
||||
def afm_set(afm):
|
||||
afm += 1
|
||||
if afm == 41:
|
||||
afm = 0
|
||||
_preferences.afm_time = afm
|
||||
|
||||
pc4 = _SliderPreference('Auto-Forward Time', 40, afm_get, afm_set,
|
||||
'library.has_afm')
|
||||
|
||||
# Right
|
||||
|
||||
pr1 = _VolumePreference("Music Volume", 'music', 'library.has_music')
|
||||
pr2 = _VolumePreference("Sound Volume", 'sfx', 'library.has_sound', 'library.sample_sound')
|
||||
|
||||
_JumpPreference('Joystick...', '_joystick_screen', 'renpy.display.joystick.enabled')
|
||||
|
||||
_JoystickPreference('Joystick Configuration')
|
||||
|
||||
# Advanced
|
||||
|
||||
### prefs_column default
|
||||
# The style of a vbox containing a column of preferences.
|
||||
|
||||
### prefs_left prefs_column
|
||||
# The position of the left column of preferences.
|
||||
|
||||
### prefs_center prefs_column
|
||||
# The position of the center column of preferences.
|
||||
|
||||
### prefs_right prefs_column
|
||||
# The position of the right column of preferences.
|
||||
|
||||
library.preferences['prefs_left'] = [
|
||||
library.all_preferences['Display'],
|
||||
library.all_preferences['Transitions'],
|
||||
library.all_preferences['Joystick...'],
|
||||
]
|
||||
|
||||
library.preferences['prefs_center'] = [
|
||||
library.all_preferences['TAB and CTRL Skip'],
|
||||
library.all_preferences['After Choices'],
|
||||
library.all_preferences['Text Speed'],
|
||||
library.all_preferences['Auto-Forward Time'],
|
||||
]
|
||||
|
||||
library.preferences['prefs_right'] = [
|
||||
library.all_preferences['Music Volume'],
|
||||
library.all_preferences['Sound Volume'],
|
||||
]
|
||||
|
||||
library.joystick_preferences['prefs_center'] = [
|
||||
library.all_preferences['Joystick Configuration'],
|
||||
]
|
||||
|
||||
|
||||
label _prefs_screen:
|
||||
|
||||
$ _prefs_screen_run(library.preferences)
|
||||
|
||||
jump _prefs_screen
|
||||
|
||||
label _joystick_screen:
|
||||
|
||||
$ _prefs_screen_run(library.joystick_preferences)
|
||||
|
||||
jump _joystick_screen
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 606 B After Width: | Height: | Size: 606 B |
@@ -0,0 +1,456 @@
|
||||
# This file is responsible for creating and defining the default styles
|
||||
# used by the system.
|
||||
|
||||
# This file should be considered part of the Ren'Py library, and not
|
||||
# something that needs to be modified by the user. Instead, just update
|
||||
# the appropriate style property in an init: block in your script.
|
||||
#
|
||||
# For example, to change the default window backgrounds to a
|
||||
# transparent dark red, add:
|
||||
#
|
||||
# init:
|
||||
# $ style.window.background = renpy.Solid((128, 0, 0, 128)
|
||||
#
|
||||
# to your script. No need to mess around here, it will just make your
|
||||
# life harder when a new version of Ren'Py is released.
|
||||
|
||||
init -1000:
|
||||
python hide:
|
||||
|
||||
# Style Declarations #################################################
|
||||
|
||||
style.create('default', None,
|
||||
'The default style that all styles inherit from.')
|
||||
|
||||
style.create('hbox', 'default', '(box) The base style for hboxen.')
|
||||
style.create('vbox', 'default', '(box) The base style for vboxen.')
|
||||
|
||||
style.create('window', 'default', '(window) The base style for the windows that contain dialogue, thoughts, and menus.')
|
||||
|
||||
style.create('image_placement', 'default', 'This style is used to control the default placement of images on the screen.')
|
||||
|
||||
style.create('image', 'default', 'This is the style of images themselves. Don\'t change this, change image_placement instead.')
|
||||
style.create('animation', 'default', 'This is the default style of animations. Don\'t change this, change image_placement instead.')
|
||||
|
||||
# say
|
||||
style.create('say_label', 'default', '(text) The style that is used by default for the label of dialogue. The label is used to indicate who is saying something.')
|
||||
style.create('say_dialogue', 'default', "(text) The style that is used by default for the text of dialogue.")
|
||||
style.create('say_thought', 'default', "(text) The label that is used by default for the text of thoughts or narration, when no speaker is given.""")
|
||||
style.create('say_window', 'window', '(window) The default style for windows containing dialogue and thoughts.')
|
||||
|
||||
# menu
|
||||
style.create('menu', 'default', "(position) The style that is used for the vbox containing a menu.")
|
||||
style.create('menu_caption', 'default', "(text) The style that is used to render a menu caption.")
|
||||
style.create('menu_choice', 'default', "(text, hover) The style that is used to render the text of a menu choice.""")
|
||||
style.create('menu_choice_button', 'default', "(window, hover, sound) The style that is used to render the button containing a menu choice.")
|
||||
style.create('menu_choice_chosen', 'menu_choice', "(text, hover) The style that is used to render the text of a menu choice that has been chosen by the user sometime in the past.""")
|
||||
style.create('menu_choice_chosen_button', 'menu_choice_button', "(window, hover, sound) The style that is used to render the button containing a menu choice that has been chosen by the user sometime in the past.")
|
||||
style.create('menu_window', 'window', '(window) The default style for windows containing a menu.')
|
||||
|
||||
# input
|
||||
style.create('input_text', 'default', '(text) The style used for the text of an input box.')
|
||||
style.create('input_prompt', 'default', '(text) The style used for the prompt of an input box.')
|
||||
style.create('input_window', 'window', '(window) The style used for the window of an input box.')
|
||||
|
||||
# centered
|
||||
style.create('centered_window', 'default', '(window) The style that is used for a "window" containing centered text.')
|
||||
style.create('centered_text', 'default', '(text) The style used for centered text.')
|
||||
|
||||
# imagemap
|
||||
style.create('imagemap', 'image_placement', 'The style that is used for imagemaps.')
|
||||
style.create('imagemap_button', 'default', '(window, sound, hover) The style that is used for buttons inside imagemaps.')
|
||||
|
||||
# imagebutton
|
||||
style.create('image_button', 'default', '(window, sound, hover) The default style used for image buttons.')
|
||||
style.create('image_button_image', 'default', 'The default style used for images inside image buttons.')
|
||||
|
||||
# button
|
||||
style.create('button', 'default', '(window, sound, hover) The default style used for buttons in the main and game menus.')
|
||||
style.create('button_text', 'default', '(text, hover) The default style used for the label of a button.')
|
||||
style.create('selected_button', 'button', '(window, hover) The style that is used for a selected button (for example, the active screen or a chosen preference).')
|
||||
style.create('selected_button_text', 'button_text', '(text, hover) The style that is used for the label of a selected button.')
|
||||
|
||||
# bar
|
||||
style.create('bar', 'default', '(bar) The style that is used by default for bars.')
|
||||
|
||||
# boxen used by the various menus.
|
||||
style.create('thin_hbox', 'hbox', '(box) A hbox with a small amount of spacing.')
|
||||
style.create('thick_hbox', 'hbox', '(box) A hbox with a large amount of spacing.')
|
||||
style.create('thin_vbox', 'vbox', '(box) A vbox with a small amount of spacing.')
|
||||
style.create('thick_vbox', 'vbox', '(box) A vbox with a large amount of spacing.')
|
||||
|
||||
# AUTOMATICALLY GENERATED
|
||||
style.create("gm_root_window", "default", "(window) The style used for the root window of the game menu. This is primarily used to change the background of the game menu.")
|
||||
style.create("gm_nav_window", "default", "(window) The style used by a window containing buttons that allow the user to navigate through the different screens of the game menu.")
|
||||
style.create("gm_nav_vbox", "thin_vbox", "(box) The style that is used by the box inside the gm_nav_window")
|
||||
style.create("gm_nav_button", "button", "(window, hover) The style of an unselected game menu navigation button.")
|
||||
style.create("gm_nav_button_text", "button_text", "(text, hover) The style of the text of an unselected game menu navigation button.")
|
||||
style.create("gm_nav_selected_button", "selected_button", "(window, hover) The style of a selected game menu navigation button.")
|
||||
style.create("gm_nav_selected_button_text", "selected_button_text", "(text, hover) The style of the text of a selected game menu navigation button.")
|
||||
style.create("file_picker_entry", "button", "(window, hover) The style that is used for each of the slots in the file picker.")
|
||||
style.create("file_picker_entry_box", "thin_hbox", "(box) The style that is used for the hbox inside of a file picker entry.")
|
||||
style.create("file_picker_text", "default", "(text) A base style for all text that is displayed in the file picker.")
|
||||
style.create("file_picker_new", "file_picker_text", "(text) The style that is applied to the number of the new slot in the file picker.")
|
||||
style.create("file_picker_old", "file_picker_text", "(text) The style that is applied to the number of the old slot in the file picker.")
|
||||
style.create("file_picker_extra_info", "file_picker_text", "(text) The style that is applied to extra info in the file picker. The extra info is the save time, and the save_name if one exists.")
|
||||
style.create("file_picker_empty_slot", "file_picker_text", "(text) The style that is used for the empty slot indicator in the file picker.")
|
||||
style.create("file_picker_window", "default", "(window) A window containing the file picker that is used to choose slots for loading and saving.")
|
||||
style.create("file_picker_window_vbox", "thin_vbox", "(box) The vbox containing both the nav and the grid in the file picker.")
|
||||
style.create("file_picker_navbox", "thick_hbox", "(box) The box containing the naviation (next/previous) buttons in the file picker.")
|
||||
style.create("file_picker_nav_button", "button", "(window, hover) The style that is used for enabled file picker navigation buttons.")
|
||||
style.create("file_picker_nav_button_text", "button_text", "(text) The style that is used for the label of enabled file picker navigation buttons.")
|
||||
style.create("file_picker_grid", "default", "The style of the grid containing the file picker entries.")
|
||||
style.create("yesno_window", "default", "(window) The style of a window containing a yes/no prompt.")
|
||||
style.create("yesno_window_vbox", "thick_vbox", "(box) The style of a box containing the widgets in a yes/no prompt.")
|
||||
style.create("yesno_label", "default", "(text) The style used for the prompt in a yes/no dialog.")
|
||||
style.create("yesno_button", "button", "(window, hover) The style of yes/no buttons.")
|
||||
style.create("yesno_button_text", "button_text", "(window, hover) The style of yes/no button text.")
|
||||
style.create("error_window", "default", "(window) The style of the window containing internal error messages.")
|
||||
style.create("error_title", "default", "(text) The style of the text containing the title of an error message.")
|
||||
style.create("error_body", "default", "(text) The style of the body of an error message.")
|
||||
style.create("skip_indicator", "default", "(text) The style and placement of the skip indicator.")
|
||||
style.create("mm_root_window", "default", "(window) The style used for the root window of the main menu. This is primarily used to set a background for the main menu.")
|
||||
style.create("mm_menu_window", "default", "(window) A window that contains the choices in the main menu. Change this to change the placement of these choices on the main menu screen.")
|
||||
style.create("mm_menu_window_vbox", "thin_vbox", "(box) The vbox containing the main menu choices.")
|
||||
style.create("mm_button", "button", "(window, hover) The style that is used on buttons that are part of the main menu.")
|
||||
style.create("mm_button_text", "button_text", "(text, hover) The style that is used for the labels of buttons that are part of the main menu.")
|
||||
style.create("prefs_window", "default", "(window) A window containing all preferences.")
|
||||
style.create("prefs_pref", "default", "(window) A window containing an individual preference.")
|
||||
style.create("prefs_pref_vbox", "thin_vbox", "(box) The style of the vbox containing a preference.")
|
||||
style.create("prefs_label", "default", "(text) The style that is applied to the label of a block of preferences.")
|
||||
style.create("prefs_hbox", "default", "If library.hbox_pref_choices is True, the style of the hbox containing the choices.")
|
||||
style.create("prefs_button", "button", "(window, hover) The style of an unselected preferences button.")
|
||||
style.create("prefs_button_text", "button_text", "(text, hover) The style of the text of an unselected preferences button.")
|
||||
style.create("prefs_selected_button", "selected_button", "(window, hover) The style of a selected preferences button.")
|
||||
style.create("prefs_selected_button_text", "selected_button_text", "(text, hover) The style of the text of a selected preferences button.")
|
||||
style.create("prefs_volume_slider", "prefs_slider", "(bar) The style that is applied to volume sliders.")
|
||||
style.create("prefs_slider", "bar", "(bar) The style that is applied to preference sliders.")
|
||||
style.create("prefs_spinner", "default", "The position of the prefs spinner.")
|
||||
style.create("prefs_spinner_label", "prefs_label", "(text) This is the style that displays the value of a preference spinner.")
|
||||
style.create("prefs_spinner_button", "prefs_button", "(window, hover) The style of the + or - buttons in a preference spinner.")
|
||||
style.create("prefs_spinner_button_text", "prefs_button_text", "(text, hover) The style of the text of the + and - buttons in a preference spinner.")
|
||||
style.create("prefs_js_button", "prefs_button", "(window, hover) The style of buttons giving a joystick mapping.")
|
||||
style.create("prefs_js_button_text", "prefs_button_text", "(text, hover) The style of the text in buttons giving a joystick mapping.")
|
||||
style.create("joy_window", "prefs_window", "(window) The window containing the joystick message.")
|
||||
style.create("joy_vbox", "thick_vbox", "(window) The vbox containing the joistick mapping message.")
|
||||
style.create("joyfunc_label", "prefs_label", "(text, position) The style of the joystick mapping function name.")
|
||||
style.create("joyprompt_label", "prefs_label", "(text, position) The style of the joystick mapping prompt message.")
|
||||
style.create("prefs_jump", "prefs_pref", "(window) The style of a window containing a jump preference.")
|
||||
style.create("prefs_jump_button", "prefs_button", "(window, hover) The style of a jump preference button.")
|
||||
style.create("prefs_jump_button_text", "prefs_button_text", "(text, hover) The style of jump preference button text.")
|
||||
style.create("prefs_column", "default", "The style of a vbox containing a column of preferences.")
|
||||
style.create("prefs_left", "prefs_column", "The position of the left column of preferences.")
|
||||
style.create("prefs_center", "prefs_column", "The position of the center column of preferences.")
|
||||
style.create("prefs_right", "prefs_column", "The position of the right column of preferences.")
|
||||
# END AUTOMATICALLY GENERATED
|
||||
|
||||
# Colors #############################################################
|
||||
|
||||
dark_cyan = (0, 192, 255, 255)
|
||||
bright_cyan = (0, 255, 255, 255)
|
||||
|
||||
dark_red = (255, 128, 128, 255)
|
||||
bright_red = (255, 64, 64, 255)
|
||||
|
||||
green = (0, 128, 0, 255)
|
||||
|
||||
# The Default Style ###################################################
|
||||
|
||||
# Magic.
|
||||
style.default.enable_hover = True
|
||||
|
||||
# Text properties.
|
||||
style.default.font = "Vera.ttf"
|
||||
style.default.antialias = True
|
||||
style.default.size = 22
|
||||
style.default.color = (255, 255, 255, 255)
|
||||
style.default.bold = False
|
||||
style.default.italic = False
|
||||
style.default.underline = False
|
||||
style.default.drop_shadow = (1, 1)
|
||||
style.default.drop_shadow_color = (0, 0, 0, 255)
|
||||
style.default.minwidth = 0
|
||||
style.default.textalign = 0
|
||||
style.default.text_y_fudge = 0
|
||||
style.default.first_indent = 0
|
||||
style.default.rest_indent = 0
|
||||
style.default.line_spacing = 0
|
||||
|
||||
# Window properties.
|
||||
style.default.background = None
|
||||
style.default.xpadding = 0
|
||||
style.default.ypadding = 0
|
||||
style.default.xmargin = 0
|
||||
style.default.ymargin = 0
|
||||
style.default.xfill = False
|
||||
style.default.yfill = False
|
||||
style.default.xminimum = 0 # Includes margins and padding.
|
||||
style.default.yminimum = 0 # Includes margins and padding.
|
||||
|
||||
# Placement properties.
|
||||
style.default.xpos = None # 0
|
||||
style.default.ypos = None # 0
|
||||
style.default.xanchor = None # 0
|
||||
style.default.yanchor = None # 0
|
||||
style.default.xmaximum = None
|
||||
style.default.ymaximum = None
|
||||
|
||||
# Sound properties.
|
||||
style.default.sound = None
|
||||
|
||||
# Box properties.
|
||||
style.default.box_spacing = 0
|
||||
style.default.box_layout = None
|
||||
|
||||
######################################################################
|
||||
# The style of various boxes.
|
||||
|
||||
style.hbox.box_layout = 'horizontal'
|
||||
style.vbox.box_layout = 'vertical'
|
||||
|
||||
style.thin_hbox.box_spacing = 3
|
||||
style.thick_hbox.box_spacing = 30
|
||||
style.thin_vbox.box_spacing = 0
|
||||
style.thick_vbox.box_spacing = 30
|
||||
|
||||
######################################################################
|
||||
# Windows.
|
||||
|
||||
style.window.background = Solid((0, 0, 128, 128))
|
||||
style.window.xpadding = 10
|
||||
style.window.ypadding = 5
|
||||
style.window.xmargin = 10
|
||||
style.window.ymargin = 5
|
||||
style.window.xfill = True
|
||||
style.window.yfill = False
|
||||
style.window.xminimum = 0 # Includes margins and padding.
|
||||
style.window.yminimum = 150 # Includes margins and padding.
|
||||
|
||||
style.window.xpos = 0.5
|
||||
style.window.ypos = 1.0
|
||||
style.window.xanchor = 'center'
|
||||
style.window.yanchor = 'bottom'
|
||||
|
||||
######################################################################
|
||||
# Image placement.
|
||||
|
||||
style.image_placement.xpos = 0.5
|
||||
style.image_placement.ypos = 1.0
|
||||
style.image_placement.xanchor = 'center'
|
||||
style.image_placement.yanchor = 'bottom'
|
||||
|
||||
|
||||
######################################################################
|
||||
# Dialogue
|
||||
|
||||
style.say_label.bold = True
|
||||
|
||||
style.menu_choice.hover_color = (255, 255, 0, 255) # yellow
|
||||
style.menu_choice.activate_color = (255, 255, 0, 255) # yellow
|
||||
style.menu_choice.idle_color = (0, 255, 255, 255) # cyan
|
||||
|
||||
style.input_text.color = (255, 255, 0, 255)
|
||||
|
||||
# Styles used by centered.
|
||||
style.centered_window.xpos = 0.5
|
||||
style.centered_window.xanchor = 'center'
|
||||
style.centered_window.xfill = False
|
||||
style.centered_window.ypos = 0.5
|
||||
style.centered_window.yanchor = 'center'
|
||||
style.centered_window.yfill = False
|
||||
style.centered_window.xpadding = 10
|
||||
|
||||
style.centered_text.textalign = 0.5
|
||||
style.centered_text.xpos = 0.5
|
||||
style.centered_text.ypos = 0.5
|
||||
style.centered_text.xanchor = 'center'
|
||||
style.centered_text.yanchor = 'center'
|
||||
|
||||
|
||||
######################################################################
|
||||
# Buttons.
|
||||
|
||||
style.button.xpos = 0.5
|
||||
style.button.xanchor = 'center'
|
||||
|
||||
style.button_text.xpos = 0.5
|
||||
style.button_text.xanchor = 'center'
|
||||
style.button_text.size = 24
|
||||
style.button_text.color = dark_cyan
|
||||
style.button_text.hover_color = bright_cyan
|
||||
style.button_text.activate_color = bright_cyan
|
||||
style.button_text.insensitive_color = (192, 192, 192, 255)
|
||||
style.button_text.drop_shadow = (2, 2)
|
||||
|
||||
style.selected_button_text.color = dark_red
|
||||
style.selected_button_text.hover_color = bright_red
|
||||
style.selected_button_text.activate_color = bright_red
|
||||
|
||||
######################################################################
|
||||
# Bar.
|
||||
|
||||
style.bar.left_bar = Solid(bright_cyan)
|
||||
style.bar.right_bar = Solid((0, 0, 0, 128))
|
||||
style.bar.left_gutter = 0
|
||||
style.bar.right_gutter = 0
|
||||
style.bar.thumb = None
|
||||
style.bar.thumb_offset = 0
|
||||
style.bar.thumb_shadow = None
|
||||
|
||||
|
||||
######################################################################
|
||||
# Main menu.
|
||||
|
||||
style.mm_root_window.background = Solid((0, 0, 0, 255))
|
||||
style.mm_root_window.xfill = True
|
||||
style.mm_root_window.yfill = True
|
||||
|
||||
style.mm_menu_window.xpos = 0.9
|
||||
style.mm_menu_window.xanchor = 'right'
|
||||
style.mm_menu_window.ypos = 0.9
|
||||
style.mm_menu_window.yanchor = 'bottom'
|
||||
|
||||
|
||||
######################################################################
|
||||
# Game menu common.
|
||||
|
||||
style.gm_root_window.background = Solid((0, 0, 0, 255))
|
||||
style.gm_root_window.xfill = True
|
||||
style.gm_root_window.yfill = True
|
||||
|
||||
style.gm_nav_window.xpos = 0.9
|
||||
style.gm_nav_window.xanchor = 'right'
|
||||
style.gm_nav_window.ypos = 0.95
|
||||
style.gm_nav_window.yanchor = 'bottom'
|
||||
|
||||
|
||||
##############################################################################
|
||||
# File picker.
|
||||
|
||||
style.file_picker_window.xpos = 0
|
||||
style.file_picker_window.xanchor = 'left'
|
||||
style.file_picker_window.ypos = 0
|
||||
style.file_picker_window.yanchor = 'top'
|
||||
style.file_picker_window.xpadding = 5
|
||||
|
||||
style.file_picker_navbox.xpos = 10
|
||||
|
||||
style.file_picker_grid.xfill = True
|
||||
|
||||
style.file_picker_entry.xpadding = 5
|
||||
style.file_picker_entry.ypadding = 2
|
||||
style.file_picker_entry.xmargin = 5
|
||||
style.file_picker_entry.xfill = True
|
||||
style.file_picker_entry.ymargin = 2
|
||||
style.file_picker_entry.background = Solid((255, 255, 255, 255))
|
||||
style.file_picker_entry.hover_background = Solid((255, 255, 192, 255))
|
||||
style.file_picker_entry.activate_background = Solid((255, 255, 192, 255))
|
||||
|
||||
style.file_picker_text.size = 18
|
||||
style.file_picker_text.color = dark_cyan
|
||||
style.file_picker_text.hover_color = bright_cyan
|
||||
|
||||
style.file_picker_new.hover_color = bright_red
|
||||
style.file_picker_new.activate_color = bright_red
|
||||
style.file_picker_new.idle_color = dark_red
|
||||
style.file_picker_new.minwidth = 30
|
||||
|
||||
style.file_picker_old.minwidth = 30
|
||||
|
||||
|
||||
######################################################################
|
||||
# Yes/No Dialog
|
||||
|
||||
style.yesno_label.color = green
|
||||
style.yesno_label.textalign = 0.5
|
||||
|
||||
style.yesno_window.xfill = True
|
||||
style.yesno_window.yminimum = 0.5
|
||||
style.yesno_window.xmargin = .1
|
||||
|
||||
style.yesno_window_vbox.xpos = 0.5
|
||||
style.yesno_window_vbox.xanchor = 'center'
|
||||
style.yesno_window_vbox.ypos = 0.5
|
||||
style.yesno_window_vbox.yanchor = 'center'
|
||||
|
||||
##############################################################################
|
||||
# Preferences.
|
||||
|
||||
|
||||
style.prefs_pref.xpos = 0.5
|
||||
style.prefs_pref.xanchor = 'center'
|
||||
style.prefs_pref.bottom_margin = 10
|
||||
|
||||
style.prefs_label.xpos = 0.5
|
||||
style.prefs_label.xanchor = "center"
|
||||
style.prefs_label.color = green
|
||||
|
||||
style.prefs_slider.xmaximum=200
|
||||
style.prefs_slider.ymaximum=22
|
||||
style.prefs_slider.xpos = 0.5
|
||||
style.prefs_slider.xanchor = 'center'
|
||||
|
||||
style.prefs_hbox.xpos = 0.5
|
||||
style.prefs_hbox.xanchor = 'center'
|
||||
|
||||
style.prefs_button.xpos = 0.5
|
||||
style.prefs_button.xanchor = 'center'
|
||||
|
||||
style.prefs_selected_button.xpos = 0.5
|
||||
style.prefs_selected_button.xanchor = 'center'
|
||||
|
||||
style.prefs_window.xfill=True
|
||||
style.prefs_window.ypadding = 0.05
|
||||
|
||||
style.prefs_column.box_spacing = 6
|
||||
|
||||
style.prefs_left.xanchor = 'center'
|
||||
style.prefs_left.xpos = 1.0 / 6.0
|
||||
|
||||
style.prefs_center.xanchor = 'center'
|
||||
style.prefs_center.xpos = 3.0 / 6.0
|
||||
|
||||
style.prefs_right.xanchor = 'center'
|
||||
style.prefs_right.xpos = 5.0 / 6.0
|
||||
|
||||
style.prefs_spinner.xpos = 0.5
|
||||
style.prefs_spinner.xanchor = 'center'
|
||||
|
||||
style.prefs_spinner_label.minwidth = 100
|
||||
style.prefs_spinner_label.textalign = 0.5
|
||||
|
||||
style.prefs_js_button_text.size = 18
|
||||
style.prefs_js_button_text.drop_shadow = (1, 1)
|
||||
|
||||
style.joyfunc_label.textalign = 0.5
|
||||
style.joyprompt_label.textalign = 0.5
|
||||
|
||||
style.joy_window.xfill = True
|
||||
style.joy_window.yminimum = 0.5
|
||||
style.joy_window.xmargin = .1
|
||||
|
||||
style.joy_vbox.xpos = 0.5
|
||||
style.joy_vbox.xanchor = 'center'
|
||||
style.joy_vbox.ypos = 0.5
|
||||
style.joy_vbox.yanchor = 'center'
|
||||
|
||||
|
||||
######################################################################
|
||||
# Skip indicator.
|
||||
|
||||
style.skip_indicator.xpos = 10
|
||||
style.skip_indicator.ypos = 10
|
||||
|
||||
######################################################################
|
||||
# Error messages.
|
||||
|
||||
style.error_window.background = Solid((220, 220, 255, 255))
|
||||
style.error_window.xfill = True
|
||||
style.error_window.yfill = True
|
||||
style.error_window.xpadding = 20
|
||||
style.error_window.ypadding = 20
|
||||
|
||||
style.error_title.color = (255, 128, 128, 255)
|
||||
|
||||
style.error_body.color = (128, 128, 255, 255)
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os.path
|
||||
|
||||
import codecs
|
||||
import optparse
|
||||
import traceback
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
# Extra things used for distribution.
|
||||
import encodings.utf_8
|
||||
import encodings.zlib_codec
|
||||
import encodings.unicode_escape
|
||||
import encodings.string_escape
|
||||
import encodings.raw_unicode_escape
|
||||
import math
|
||||
|
||||
def main():
|
||||
|
||||
dirname = os.path.dirname(sys.argv[0])
|
||||
|
||||
if dirname:
|
||||
os.chdir(dirname)
|
||||
|
||||
# Add the path to the module.
|
||||
sys.path.append("module")
|
||||
|
||||
name = os.path.basename(sys.argv[0])
|
||||
|
||||
if name.find(".") != -1:
|
||||
name = name[:name.find(".")]
|
||||
|
||||
if name.find("_") != -1:
|
||||
name = name[name.find("_") + 1:]
|
||||
|
||||
op = optparse.OptionParser()
|
||||
op.add_option('--game', dest='game', default=name,
|
||||
help='The directory the game is in.')
|
||||
|
||||
op.add_option('--python', dest='python', default=None,
|
||||
help='Run the argument in the python interpreter.')
|
||||
|
||||
op.add_option('--lint', dest='lint', default=False, action='store_true',
|
||||
help='Run a number of expensive tests, to try to detect errors in the script.')
|
||||
|
||||
op.add_option('--leak', dest='leak', action='store_true', default=False,
|
||||
help='When the game exits, dumps a profile of memory usage.')
|
||||
|
||||
options, args = op.parse_args()
|
||||
|
||||
if options.python:
|
||||
execfile(options.python)
|
||||
sys.exit(0)
|
||||
|
||||
if not options.lint:
|
||||
import renpy.display.presplash
|
||||
renpy.display.presplash.start(options.game)
|
||||
|
||||
# Load up all of Ren'Py, in the right order.
|
||||
import renpy
|
||||
|
||||
try:
|
||||
renpy.main.main(options.game, lint=options.lint)
|
||||
|
||||
except Exception, e:
|
||||
|
||||
f = file("traceback.txt", "wU")
|
||||
|
||||
f.write(codecs.BOM_UTF8)
|
||||
|
||||
print >>f, "I'm sorry, but an exception occured while executing your Ren'Py"
|
||||
print >>f, "script."
|
||||
print >>f
|
||||
|
||||
type, value, tb = sys.exc_info()
|
||||
|
||||
|
||||
print >>f, type.__name__ + ":",
|
||||
print >>f, unicode(e).encode('utf-8')
|
||||
print >>f
|
||||
print >>f, renpy.game.exception_info
|
||||
|
||||
print >>f
|
||||
print >>f, "-- Full Traceback ------------------------------------------------------------"
|
||||
print >>f
|
||||
|
||||
traceback.print_tb(tb, None, sys.stdout)
|
||||
traceback.print_tb(tb, None, f)
|
||||
|
||||
print >>f, type.__name__ + ":",
|
||||
print type.__name__ + ":",
|
||||
|
||||
print >>f, unicode(e).encode('utf-8')
|
||||
print unicode(e).encode('utf-8')
|
||||
|
||||
print
|
||||
print >>f
|
||||
|
||||
print renpy.game.exception_info
|
||||
print >>f, renpy.game.exception_info
|
||||
|
||||
print >>f
|
||||
print >>f, "Ren'Py Version:", renpy.version
|
||||
|
||||
f.close()
|
||||
|
||||
try:
|
||||
os.startfile('traceback.txt')
|
||||
except:
|
||||
pass
|
||||
|
||||
if options.leak:
|
||||
memory_profile()
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
def memory_profile():
|
||||
|
||||
import renpy
|
||||
|
||||
print "Memory Profile"
|
||||
print
|
||||
print "Showing all objects in memory at program termination."
|
||||
print
|
||||
|
||||
import gc
|
||||
gc.collect()
|
||||
|
||||
objs = gc.get_objects()
|
||||
|
||||
c = { } # count
|
||||
dead_renders = 0
|
||||
|
||||
for i in objs:
|
||||
t = type(i)
|
||||
c[t] = c.get(t, 0) + 1
|
||||
|
||||
if isinstance(i, renpy.display.render.Render):
|
||||
if i.dead:
|
||||
dead_renders += 1
|
||||
|
||||
|
||||
results = [ (count, ty) for ty, count in c.iteritems() ]
|
||||
results.sort()
|
||||
|
||||
for count, ty in results:
|
||||
print count, str(ty)
|
||||
|
||||
if dead_renders:
|
||||
print
|
||||
print "*** found", dead_renders, "dead Renders. ***"
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,822 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Perforce Defect Tracking Integration Project
|
||||
# <http://www.ravenbrook.com/project/p4dti/>
|
||||
#
|
||||
# COVERAGE.PY -- COVERAGE TESTING
|
||||
#
|
||||
# Gareth Rees, Ravenbrook Limited, 2001-12-04
|
||||
# Ned Batchelder, 2004-12-12
|
||||
# http://nedbatchelder.com/code/modules/coverage.html
|
||||
#
|
||||
#
|
||||
# 1. INTRODUCTION
|
||||
#
|
||||
# This module provides coverage testing for Python code.
|
||||
#
|
||||
# The intended readership is all Python developers.
|
||||
#
|
||||
# This document is not confidential.
|
||||
#
|
||||
# See [GDR 2001-12-04a] for the command-line interface, programmatic
|
||||
# interface and limitations. See [GDR 2001-12-04b] for requirements and
|
||||
# design.
|
||||
|
||||
"""Usage:
|
||||
|
||||
coverage.py -x MODULE.py [ARG1 ARG2 ...]
|
||||
Execute module, passing the given command-line arguments, collecting
|
||||
coverage data.
|
||||
|
||||
coverage.py -e
|
||||
Erase collected coverage data.
|
||||
|
||||
coverage.py -r [-m] FILE1 FILE2 ...
|
||||
Report on the statement coverage for the given files. With the -m
|
||||
option, show line numbers of the statements that weren't executed.
|
||||
|
||||
coverage.py -a [-d dir] FILE1 FILE2 ...
|
||||
Make annotated copies of the given files, marking statements that
|
||||
are executed with > and statements that are missed with !. With
|
||||
the -d option, make the copies in that directory. Without the -d
|
||||
option, make each copy in the same directory as the original.
|
||||
|
||||
Coverage data is saved in the file .coverage by default. Set the
|
||||
COVERAGE_FILE environment variable to save it somewhere else."""
|
||||
|
||||
__version__ = "2.2.20041231" # see detailed history at the end of this file.
|
||||
|
||||
import compiler
|
||||
import compiler.visitor
|
||||
import os
|
||||
import re
|
||||
import string
|
||||
import sys
|
||||
import types
|
||||
|
||||
|
||||
# 2. IMPLEMENTATION
|
||||
#
|
||||
# This uses the "singleton" pattern.
|
||||
#
|
||||
# The word "morf" means a module object (from which the source file can
|
||||
# be deduced by suitable manipulation of the __file__ attribute) or a
|
||||
# filename.
|
||||
#
|
||||
# When we generate a coverage report we have to canonicalize every
|
||||
# filename in the coverage dictionary just in case it refers to the
|
||||
# module we are reporting on. It seems a shame to throw away this
|
||||
# information so the data in the coverage dictionary is transferred to
|
||||
# the 'cexecuted' dictionary under the canonical filenames.
|
||||
#
|
||||
# The coverage dictionary is called "c" and the trace function "t". The
|
||||
# reason for these short names is that Python looks up variables by name
|
||||
# at runtime and so execution time depends on the length of variables!
|
||||
# In the bottleneck of this application it's appropriate to abbreviate
|
||||
# names to increase speed.
|
||||
|
||||
# A dictionary with an entry for (Python source file name, line number
|
||||
# in that file) if that line has been executed.
|
||||
c = {}
|
||||
|
||||
# t(f, x, y). This method is passed to sys.settrace as a trace
|
||||
# function. See [van Rossum 2001-07-20b, 9.2] for an explanation of
|
||||
# sys.settrace and the arguments and return value of the trace function.
|
||||
# See [van Rossum 2001-07-20a, 3.2] for a description of frame and code
|
||||
# objects.
|
||||
|
||||
def t(f, w, a):
|
||||
if w == 'line':
|
||||
c[(f.f_code.co_filename, f.f_lineno)] = 1
|
||||
return t
|
||||
|
||||
class StatementFindingAstVisitor(compiler.visitor.ASTVisitor):
|
||||
def __init__(self, statements, excluded, suite_spots):
|
||||
compiler.visitor.ASTVisitor.__init__(self)
|
||||
self.statements = statements
|
||||
self.excluded = excluded
|
||||
self.suite_spots = suite_spots
|
||||
self.excluding_suite = 0
|
||||
|
||||
def doRecursive(self, node):
|
||||
self.recordNodeLine(node)
|
||||
for n in node.getChildNodes():
|
||||
self.dispatch(n)
|
||||
|
||||
visitStmt = visitModule = doRecursive
|
||||
|
||||
def doCode(self, node):
|
||||
if hasattr(node, 'decorators') and node.decorators:
|
||||
self.dispatch(node.decorators)
|
||||
self.doSuite(node, node.code)
|
||||
|
||||
visitFunction = visitClass = doCode
|
||||
|
||||
def getFirstLine(self, node):
|
||||
# Find the first line in the tree node.
|
||||
lineno = node.lineno
|
||||
for n in node.getChildNodes():
|
||||
f = self.getFirstLine(n)
|
||||
if lineno and f:
|
||||
lineno = min(lineno, f)
|
||||
else:
|
||||
lineno = lineno or f
|
||||
return lineno
|
||||
|
||||
def getLastLine(self, node):
|
||||
# Find the first line in the tree node.
|
||||
lineno = node.lineno
|
||||
for n in node.getChildNodes():
|
||||
lineno = max(lineno, self.getLastLine(n))
|
||||
return lineno
|
||||
|
||||
def doStatement(self, node):
|
||||
self.recordLine(self.getFirstLine(node))
|
||||
|
||||
visitAssert = visitAssign = visitAssTuple = visitDiscard = visitPrint = \
|
||||
visitPrintnl = visitRaise = visitSubscript = \
|
||||
visitDecorators = \
|
||||
doStatement
|
||||
|
||||
def recordNodeLine(self, node):
|
||||
return self.recordLine(node.lineno)
|
||||
|
||||
def recordLine(self, lineno):
|
||||
# Returns a bool, whether the line is included or excluded.
|
||||
if lineno:
|
||||
# Multi-line tests introducing suites have to get charged to their
|
||||
# keyword.
|
||||
if lineno in self.suite_spots:
|
||||
lineno = self.suite_spots[lineno][0]
|
||||
# If we're inside an exluded suite, record that this line was
|
||||
# excluded.
|
||||
if self.excluding_suite:
|
||||
self.excluded[lineno] = 1
|
||||
return 0
|
||||
# If this line is excluded, or suite_spots maps this line to
|
||||
# another line that is exlcuded, then we're excluded.
|
||||
elif self.excluded.has_key(lineno) or \
|
||||
self.suite_spots.has_key(lineno) and \
|
||||
self.excluded.has_key(self.suite_spots[lineno][1]):
|
||||
return 0
|
||||
# Otherwise, this is an executable line.
|
||||
else:
|
||||
self.statements[lineno] = 1
|
||||
return 1
|
||||
return 0
|
||||
|
||||
default = recordNodeLine
|
||||
|
||||
def recordAndDispatch(self, node):
|
||||
self.recordNodeLine(node)
|
||||
self.dispatch(node)
|
||||
|
||||
def doSuite(self, intro, body, exclude=0):
|
||||
exsuite = self.excluding_suite
|
||||
if exclude or (intro and not self.recordNodeLine(intro)):
|
||||
self.excluding_suite = 1
|
||||
self.recordAndDispatch(body)
|
||||
self.excluding_suite = exsuite
|
||||
|
||||
def doPlainWordSuite(self, prevsuite, suite):
|
||||
# Finding the exclude lines for else's is tricky, because they aren't
|
||||
# present in the compiler parse tree. Look at the previous suite,
|
||||
# and find its last line. If any line between there and the else's
|
||||
# first line are excluded, then we exclude the else.
|
||||
lastprev = self.getLastLine(prevsuite)
|
||||
firstelse = self.getFirstLine(suite)
|
||||
for l in range(lastprev+1, firstelse):
|
||||
if self.suite_spots.has_key(l):
|
||||
self.doSuite(None, suite, exclude=self.excluded.has_key(l))
|
||||
break
|
||||
else:
|
||||
self.doSuite(None, suite)
|
||||
|
||||
def doElse(self, prevsuite, node):
|
||||
if node.else_:
|
||||
self.doPlainWordSuite(prevsuite, node.else_)
|
||||
|
||||
def visitFor(self, node):
|
||||
self.doSuite(node, node.body)
|
||||
self.doElse(node.body, node)
|
||||
|
||||
def visitIf(self, node):
|
||||
# The first test has to be handled separately from the rest.
|
||||
# The first test is credited to the line with the "if", but the others
|
||||
# are credited to the line with the test for the elif.
|
||||
self.doSuite(node, node.tests[0][1])
|
||||
for t, n in node.tests[1:]:
|
||||
self.doSuite(t, n)
|
||||
self.doElse(node.tests[-1][1], node)
|
||||
|
||||
def visitWhile(self, node):
|
||||
self.doSuite(node, node.body)
|
||||
self.doElse(node.body, node)
|
||||
|
||||
def visitTryExcept(self, node):
|
||||
self.doSuite(node, node.body)
|
||||
for i in range(len(node.handlers)):
|
||||
a, b, h = node.handlers[i]
|
||||
if not a:
|
||||
# It's a plain "except:". Find the previous suite.
|
||||
if i > 0:
|
||||
prev = node.handlers[i-1][2]
|
||||
else:
|
||||
prev = node.body
|
||||
self.doPlainWordSuite(prev, h)
|
||||
else:
|
||||
self.doSuite(a, h)
|
||||
self.doElse(node.handlers[-1][2], node)
|
||||
|
||||
def visitTryFinally(self, node):
|
||||
self.doSuite(node, node.body)
|
||||
self.doPlainWordSuite(node.body, node.final)
|
||||
|
||||
def visitGlobal(self, node):
|
||||
# "global" statements don't execute like others (they don't call the
|
||||
# trace function), so don't record their line numbers.
|
||||
pass
|
||||
|
||||
the_coverage = None
|
||||
|
||||
class coverage:
|
||||
error = "coverage error"
|
||||
|
||||
# Name of the cache file (unless environment variable is set).
|
||||
cache_default = ".coverage"
|
||||
|
||||
# Environment variable naming the cache file.
|
||||
cache_env = "COVERAGE_FILE"
|
||||
|
||||
# A map from canonical Python source file name to a dictionary in
|
||||
# which there's an entry for each line number that has been
|
||||
# executed.
|
||||
cexecuted = {}
|
||||
|
||||
# Cache of results of calling the analysis2() method, so that you can
|
||||
# specify both -r and -a without doing double work.
|
||||
analysis_cache = {}
|
||||
|
||||
# Cache of results of calling the canonical_filename() method, to
|
||||
# avoid duplicating work.
|
||||
canonical_filename_cache = {}
|
||||
|
||||
def __init__(self):
|
||||
global the_coverage
|
||||
if the_coverage:
|
||||
raise self.error, "Only one coverage object allowed."
|
||||
self.usecache = 1
|
||||
self.cache = None
|
||||
self.exclude_re = ''
|
||||
|
||||
def help(self, error=None):
|
||||
if error:
|
||||
print error
|
||||
print
|
||||
print __doc__
|
||||
sys.exit(1)
|
||||
|
||||
def command_line(self):
|
||||
import getopt
|
||||
settings = {}
|
||||
optmap = {
|
||||
'-a': 'annotate',
|
||||
'-d:': 'directory=',
|
||||
'-e': 'erase',
|
||||
'-h': 'help',
|
||||
'-i': 'ignore-errors',
|
||||
'-m': 'show-missing',
|
||||
'-r': 'report',
|
||||
'-x': 'execute',
|
||||
}
|
||||
short_opts = string.join(map(lambda o: o[1:], optmap.keys()), '')
|
||||
long_opts = optmap.values()
|
||||
options, args = getopt.getopt(sys.argv[1:], short_opts,
|
||||
long_opts)
|
||||
for o, a in options:
|
||||
if optmap.has_key(o):
|
||||
settings[optmap[o]] = 1
|
||||
elif optmap.has_key(o + ':'):
|
||||
settings[optmap[o + ':']] = a
|
||||
elif o[2:] in long_opts:
|
||||
settings[o[2:]] = 1
|
||||
elif o[2:] + '=' in long_opts:
|
||||
settings[o[2:]] = a
|
||||
else:
|
||||
self.help("Unknown option: '%s'." % o)
|
||||
if settings.get('help'):
|
||||
self.help()
|
||||
for i in ['erase', 'execute']:
|
||||
for j in ['annotate', 'report']:
|
||||
if settings.get(i) and settings.get(j):
|
||||
self.help("You can't specify the '%s' and '%s' "
|
||||
"options at the same time." % (i, j))
|
||||
args_needed = (settings.get('execute')
|
||||
or settings.get('annotate')
|
||||
or settings.get('report'))
|
||||
action = settings.get('erase') or args_needed
|
||||
if not action:
|
||||
self.help("You must specify at least one of -e, -x, -r, or -a.")
|
||||
if not args_needed and args:
|
||||
self.help("Unexpected arguments %s." % args)
|
||||
|
||||
self.get_ready()
|
||||
self.exclude('#pragma[: ]+[nN][oO] [cC][oO][vV][eE][rR]')
|
||||
|
||||
if settings.get('erase'):
|
||||
self.erase()
|
||||
if settings.get('execute'):
|
||||
if not args:
|
||||
self.help("Nothing to do.")
|
||||
sys.argv = args
|
||||
self.start()
|
||||
import __main__
|
||||
sys.path[0] = os.path.dirname(sys.argv[0])
|
||||
execfile(sys.argv[0], __main__.__dict__)
|
||||
if not args:
|
||||
args = self.cexecuted.keys()
|
||||
ignore_errors = settings.get('ignore-errors')
|
||||
show_missing = settings.get('show-missing')
|
||||
directory = settings.get('directory=')
|
||||
if settings.get('report'):
|
||||
self.report(args, show_missing, ignore_errors)
|
||||
if settings.get('annotate'):
|
||||
self.annotate(args, directory, ignore_errors)
|
||||
|
||||
def use_cache(self, usecache):
|
||||
self.usecache = usecache
|
||||
|
||||
def get_ready(self):
|
||||
if self.usecache and not self.cache:
|
||||
self.cache = os.environ.get(self.cache_env, self.cache_default)
|
||||
self.restore()
|
||||
self.analysis_cache = {}
|
||||
|
||||
def start(self):
|
||||
self.get_ready()
|
||||
sys.settrace(t)
|
||||
|
||||
def stop(self):
|
||||
sys.settrace(None)
|
||||
|
||||
def erase(self):
|
||||
global c
|
||||
c = {}
|
||||
self.analysis_cache = {}
|
||||
self.cexecuted = {}
|
||||
if self.cache and os.path.exists(self.cache):
|
||||
os.remove(self.cache)
|
||||
self.exclude_re = ''
|
||||
|
||||
def exclude(self, re):
|
||||
if self.exclude_re:
|
||||
self.exclude_re += "|"
|
||||
self.exclude_re += "(" + re + ")"
|
||||
|
||||
# save(). Save coverage data to the coverage cache.
|
||||
|
||||
def save(self):
|
||||
if self.usecache and self.cache:
|
||||
self.canonicalize_filenames()
|
||||
cache = open(self.cache, 'wb')
|
||||
import marshal
|
||||
marshal.dump(self.cexecuted, cache)
|
||||
cache.close()
|
||||
|
||||
# restore(). Restore coverage data from the coverage cache (if it
|
||||
# exists).
|
||||
|
||||
def restore(self):
|
||||
global c
|
||||
c = {}
|
||||
self.cexecuted = {}
|
||||
assert self.usecache
|
||||
if not os.path.exists(self.cache):
|
||||
return
|
||||
try:
|
||||
cache = open(self.cache, 'rb')
|
||||
import marshal
|
||||
cexecuted = marshal.load(cache)
|
||||
cache.close()
|
||||
if isinstance(cexecuted, types.DictType):
|
||||
self.cexecuted = cexecuted
|
||||
except:
|
||||
pass
|
||||
|
||||
# canonical_filename(filename). Return a canonical filename for the
|
||||
# file (that is, an absolute path with no redundant components and
|
||||
# normalized case). See [GDR 2001-12-04b, 3.3].
|
||||
|
||||
def canonical_filename(self, filename):
|
||||
if not self.canonical_filename_cache.has_key(filename):
|
||||
f = filename
|
||||
if os.path.isabs(f) and not os.path.exists(f):
|
||||
f = os.path.basename(f)
|
||||
if not os.path.isabs(f):
|
||||
for path in [os.curdir] + sys.path:
|
||||
g = os.path.join(path, f)
|
||||
if os.path.exists(g):
|
||||
f = g
|
||||
break
|
||||
cf = os.path.normcase(os.path.abspath(f))
|
||||
self.canonical_filename_cache[filename] = cf
|
||||
return self.canonical_filename_cache[filename]
|
||||
|
||||
# canonicalize_filenames(). Copy results from "executed" to
|
||||
# "cexecuted", canonicalizing filenames on the way. Clear the
|
||||
# "executed" map.
|
||||
|
||||
def canonicalize_filenames(self):
|
||||
global c
|
||||
for filename, lineno in c.keys():
|
||||
f = self.canonical_filename(filename)
|
||||
if not self.cexecuted.has_key(f):
|
||||
self.cexecuted[f] = {}
|
||||
self.cexecuted[f][lineno] = 1
|
||||
c = {}
|
||||
|
||||
# morf_filename(morf). Return the filename for a module or file.
|
||||
|
||||
def morf_filename(self, morf):
|
||||
if isinstance(morf, types.ModuleType):
|
||||
if not hasattr(morf, '__file__'):
|
||||
raise self.error, "Module has no __file__ attribute."
|
||||
file = morf.__file__
|
||||
else:
|
||||
file = morf
|
||||
return self.canonical_filename(file)
|
||||
|
||||
# analyze_morf(morf). Analyze the module or filename passed as
|
||||
# the argument. If the source code can't be found, raise an error.
|
||||
# Otherwise, return a tuple of (1) the canonical filename of the
|
||||
# source code for the module, (2) a list of lines of statements
|
||||
# in the source code, and (3) a list of lines of excluded statements.
|
||||
|
||||
def analyze_morf(self, morf):
|
||||
if self.analysis_cache.has_key(morf):
|
||||
return self.analysis_cache[morf]
|
||||
filename = self.morf_filename(morf)
|
||||
ext = os.path.splitext(filename)[1]
|
||||
if ext == '.pyc':
|
||||
if not os.path.exists(filename[0:-1]):
|
||||
raise self.error, ("No source for compiled code '%s'."
|
||||
% filename)
|
||||
filename = filename[0:-1]
|
||||
elif ext != '.py':
|
||||
raise self.error, "File '%s' not Python source." % filename
|
||||
source = open(filename, 'r')
|
||||
lines, excluded_lines = self.find_executable_statements(
|
||||
source.read(), exclude=self.exclude_re
|
||||
)
|
||||
source.close()
|
||||
result = filename, lines, excluded_lines
|
||||
self.analysis_cache[morf] = result
|
||||
return result
|
||||
|
||||
def get_suite_spots(self, tree, spots):
|
||||
import symbol, token
|
||||
for i in range(1, len(tree)):
|
||||
if type(tree[i]) == type(()):
|
||||
if tree[i][0] == symbol.suite:
|
||||
# Found a suite, look back for the colon and keyword.
|
||||
lineno_colon = lineno_word = None
|
||||
for j in range(i-1, 0, -1):
|
||||
if tree[j][0] == token.COLON:
|
||||
lineno_colon = tree[j][2]
|
||||
elif tree[j][0] == token.NAME:
|
||||
if tree[j][1] == 'elif':
|
||||
# Find the line number of the first non-terminal
|
||||
# after the keyword.
|
||||
t = tree[j+1]
|
||||
while t and token.ISNONTERMINAL(t[0]):
|
||||
t = t[1]
|
||||
if t:
|
||||
lineno_word = t[2]
|
||||
else:
|
||||
lineno_word = tree[j][2]
|
||||
break
|
||||
elif tree[j][0] == symbol.except_clause:
|
||||
# "except" clauses look like:
|
||||
# ('except_clause', ('NAME', 'except', lineno), ...)
|
||||
if tree[j][1][0] == token.NAME:
|
||||
lineno_word = tree[j][1][2]
|
||||
break
|
||||
if lineno_colon and lineno_word:
|
||||
# Found colon and keyword, mark all the lines
|
||||
# between the two with the two line numbers.
|
||||
for l in range(lineno_word, lineno_colon+1):
|
||||
spots[l] = (lineno_word, lineno_colon)
|
||||
self.get_suite_spots(tree[i], spots)
|
||||
|
||||
def find_executable_statements(self, text, exclude=None):
|
||||
# Find lines which match an exclusion pattern.
|
||||
excluded = {}
|
||||
suite_spots = {}
|
||||
if exclude:
|
||||
reExclude = re.compile(exclude)
|
||||
lines = text.split('\n')
|
||||
for i in range(len(lines)):
|
||||
if reExclude.search(lines[i]):
|
||||
excluded[i+1] = 1
|
||||
|
||||
import parser
|
||||
tree = parser.suite(text+'\n\n').totuple(1)
|
||||
self.get_suite_spots(tree, suite_spots)
|
||||
|
||||
# Use the compiler module to parse the text and find the executable
|
||||
# statements. We add newlines to be impervious to final partial lines.
|
||||
statements = {}
|
||||
ast = compiler.parse(text+'\n\n')
|
||||
visitor = StatementFindingAstVisitor(statements, excluded, suite_spots)
|
||||
compiler.walk(ast, visitor, walker=visitor)
|
||||
|
||||
lines = statements.keys()
|
||||
lines.sort()
|
||||
excluded_lines = excluded.keys()
|
||||
excluded_lines.sort()
|
||||
return lines, excluded_lines
|
||||
|
||||
# format_lines(statements, lines). Format a list of line numbers
|
||||
# for printing by coalescing groups of lines as long as the lines
|
||||
# represent consecutive statements. This will coalesce even if
|
||||
# there are gaps between statements, so if statements =
|
||||
# [1,2,3,4,5,10,11,12,13,14] and lines = [1,2,5,10,11,13,14] then
|
||||
# format_lines will return "1-2, 5-11, 13-14".
|
||||
|
||||
def format_lines(self, statements, lines):
|
||||
pairs = []
|
||||
i = 0
|
||||
j = 0
|
||||
start = None
|
||||
pairs = []
|
||||
while i < len(statements) and j < len(lines):
|
||||
if statements[i] == lines[j]:
|
||||
if start == None:
|
||||
start = lines[j]
|
||||
end = lines[j]
|
||||
j = j + 1
|
||||
elif start:
|
||||
pairs.append((start, end))
|
||||
start = None
|
||||
i = i + 1
|
||||
if start:
|
||||
pairs.append((start, end))
|
||||
def stringify(pair):
|
||||
start, end = pair
|
||||
if start == end:
|
||||
return "%d" % start
|
||||
else:
|
||||
return "%d-%d" % (start, end)
|
||||
import string
|
||||
return string.join(map(stringify, pairs), ", ")
|
||||
|
||||
# Backward compatibility with version 1.
|
||||
def analysis(self, morf):
|
||||
f, s, _, m, mf = self.analysis2(morf)
|
||||
return f, s, m, mf
|
||||
|
||||
def analysis2(self, morf):
|
||||
filename, statements, excluded = self.analyze_morf(morf)
|
||||
self.canonicalize_filenames()
|
||||
if not self.cexecuted.has_key(filename):
|
||||
self.cexecuted[filename] = {}
|
||||
missing = []
|
||||
for line in statements:
|
||||
if not self.cexecuted[filename].has_key(line):
|
||||
missing.append(line)
|
||||
return (filename, statements, excluded, missing,
|
||||
self.format_lines(statements, missing))
|
||||
|
||||
def morf_name(self, morf):
|
||||
if isinstance(morf, types.ModuleType):
|
||||
return morf.__name__
|
||||
else:
|
||||
return os.path.splitext(os.path.basename(morf))[0]
|
||||
|
||||
def report(self, morfs, show_missing=1, ignore_errors=0):
|
||||
if not isinstance(morfs, types.ListType):
|
||||
morfs = [morfs]
|
||||
max_name = max([5,] + map(len, map(self.morf_name, morfs)))
|
||||
fmt_name = "%%- %ds " % max_name
|
||||
fmt_err = fmt_name + "%s: %s"
|
||||
header = fmt_name % "Name" + " Stmts Exec Cover"
|
||||
fmt_coverage = fmt_name + "% 6d % 6d % 5d%%"
|
||||
if show_missing:
|
||||
header = header + " Missing"
|
||||
fmt_coverage = fmt_coverage + " %s"
|
||||
print header
|
||||
print "-" * len(header)
|
||||
total_statements = 0
|
||||
total_executed = 0
|
||||
for morf in morfs:
|
||||
name = self.morf_name(morf)
|
||||
try:
|
||||
_, statements, _, missing, readable = self.analysis2(morf)
|
||||
n = len(statements)
|
||||
m = n - len(missing)
|
||||
if n > 0:
|
||||
pc = 100.0 * m / n
|
||||
else:
|
||||
pc = 100.0
|
||||
args = (name, n, m, pc)
|
||||
if show_missing:
|
||||
args = args + (readable,)
|
||||
print fmt_coverage % args
|
||||
total_statements = total_statements + n
|
||||
total_executed = total_executed + m
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except:
|
||||
if not ignore_errors:
|
||||
type, msg = sys.exc_info()[0:2]
|
||||
print fmt_err % (name, type, msg)
|
||||
if len(morfs) > 1:
|
||||
print "-" * len(header)
|
||||
if total_statements > 0:
|
||||
pc = 100.0 * total_executed / total_statements
|
||||
else:
|
||||
pc = 100.0
|
||||
args = ("TOTAL", total_statements, total_executed, pc)
|
||||
if show_missing:
|
||||
args = args + ("",)
|
||||
print fmt_coverage % args
|
||||
|
||||
# annotate(morfs, ignore_errors).
|
||||
|
||||
blank_re = re.compile("\\s*(#|$)")
|
||||
else_re = re.compile("\\s*else\\s*:\\s*(#|$)")
|
||||
|
||||
def annotate(self, morfs, directory=None, ignore_errors=0):
|
||||
for morf in morfs:
|
||||
try:
|
||||
filename, statements, excluded, missing, _ = self.analysis2(morf)
|
||||
self.annotate_file(filename, statements, excluded, missing, directory)
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except:
|
||||
if not ignore_errors:
|
||||
raise
|
||||
|
||||
def annotate_file(self, filename, statements, excluded, missing, directory=None):
|
||||
source = open(filename, 'r')
|
||||
if directory:
|
||||
dest_file = os.path.join(directory,
|
||||
os.path.basename(filename)
|
||||
+ ',cover')
|
||||
else:
|
||||
dest_file = filename + ',cover'
|
||||
dest = open(dest_file, 'w')
|
||||
lineno = 0
|
||||
i = 0
|
||||
j = 0
|
||||
covered = 1
|
||||
while 1:
|
||||
line = source.readline()
|
||||
if line == '':
|
||||
break
|
||||
lineno = lineno + 1
|
||||
while i < len(statements) and statements[i] < lineno:
|
||||
i = i + 1
|
||||
while j < len(missing) and missing[j] < lineno:
|
||||
j = j + 1
|
||||
if i < len(statements) and statements[i] == lineno:
|
||||
covered = j >= len(missing) or missing[j] > lineno
|
||||
if self.blank_re.match(line):
|
||||
dest.write(' ')
|
||||
elif self.else_re.match(line):
|
||||
# Special logic for lines containing only
|
||||
# 'else:'. See [GDR 2001-12-04b, 3.2].
|
||||
if i >= len(statements) and j >= len(missing):
|
||||
dest.write('! ')
|
||||
elif i >= len(statements) or j >= len(missing):
|
||||
dest.write('> ')
|
||||
elif statements[i] == missing[j]:
|
||||
dest.write('! ')
|
||||
else:
|
||||
dest.write('> ')
|
||||
elif lineno in excluded:
|
||||
dest.write('- ')
|
||||
elif covered:
|
||||
dest.write('> ')
|
||||
else:
|
||||
dest.write('! ')
|
||||
dest.write(line)
|
||||
source.close()
|
||||
dest.close()
|
||||
|
||||
# Singleton object.
|
||||
the_coverage = coverage()
|
||||
|
||||
# Module functions call methods in the singleton object.
|
||||
def use_cache(*args, **kw): return the_coverage.use_cache(*args, **kw)
|
||||
def start(*args, **kw): return the_coverage.start(*args, **kw)
|
||||
def stop(*args, **kw): return the_coverage.stop(*args, **kw)
|
||||
def erase(*args, **kw): return the_coverage.erase(*args, **kw)
|
||||
def exclude(*args, **kw): return the_coverage.exclude(*args, **kw)
|
||||
def analysis(*args, **kw): return the_coverage.analysis(*args, **kw)
|
||||
def analysis2(*args, **kw): return the_coverage.analysis2(*args, **kw)
|
||||
def report(*args, **kw): return the_coverage.report(*args, **kw)
|
||||
def annotate(*args, **kw): return the_coverage.annotate(*args, **kw)
|
||||
def annotate_file(*args, **kw): return the_coverage.annotate_file(*args, **kw)
|
||||
|
||||
# Save coverage data when Python exits. (The atexit module wasn't
|
||||
# introduced until Python 2.0, so use sys.exitfunc when it's not
|
||||
# available.)
|
||||
try:
|
||||
import atexit
|
||||
atexit.register(the_coverage.save)
|
||||
except ImportError:
|
||||
sys.exitfunc = the_coverage.save
|
||||
|
||||
# Command-line interface.
|
||||
if __name__ == '__main__':
|
||||
the_coverage.command_line()
|
||||
|
||||
|
||||
# A. REFERENCES
|
||||
#
|
||||
# [GDR 2001-12-04a] "Statement coverage for Python"; Gareth Rees;
|
||||
# Ravenbrook Limited; 2001-12-04;
|
||||
# <http://www.garethrees.org/2001/12/04/python-coverage/>.
|
||||
#
|
||||
# [GDR 2001-12-04b] "Statement coverage for Python: design and
|
||||
# analysis"; Gareth Rees; Ravenbrook Limited; 2001-12-04;
|
||||
# <http://www.garethrees.org/2001/12/04/python-coverage/design.html>.
|
||||
#
|
||||
# [van Rossum 2001-07-20a] "Python Reference Manual (releae 2.1.1)";
|
||||
# Guide van Rossum; 2001-07-20;
|
||||
# <http://www.python.org/doc/2.1.1/ref/ref.html>.
|
||||
#
|
||||
# [van Rossum 2001-07-20b] "Python Library Reference"; Guido van Rossum;
|
||||
# 2001-07-20; <http://www.python.org/doc/2.1.1/lib/lib.html>.
|
||||
#
|
||||
#
|
||||
# B. DOCUMENT HISTORY
|
||||
#
|
||||
# 2001-12-04 GDR Created.
|
||||
#
|
||||
# 2001-12-06 GDR Added command-line interface and source code
|
||||
# annotation.
|
||||
#
|
||||
# 2001-12-09 GDR Moved design and interface to separate documents.
|
||||
#
|
||||
# 2001-12-10 GDR Open cache file as binary on Windows. Allow
|
||||
# simultaneous -e and -x, or -a and -r.
|
||||
#
|
||||
# 2001-12-12 GDR Added command-line help. Cache analysis so that it
|
||||
# only needs to be done once when you specify -a and -r.
|
||||
#
|
||||
# 2001-12-13 GDR Improved speed while recording. Portable between
|
||||
# Python 1.5.2 and 2.1.1.
|
||||
#
|
||||
# 2002-01-03 GDR Module-level functions work correctly.
|
||||
#
|
||||
# 2002-01-07 GDR Update sys.path when running a file with the -x option,
|
||||
# so that it matches the value the program would get if it were run on
|
||||
# its own.
|
||||
#
|
||||
# 2004-12-12 NMB Significant code changes.
|
||||
# - Finding executable statements has been rewritten so that docstrings and
|
||||
# other quirks of Python execution aren't mistakenly identified as missing
|
||||
# lines.
|
||||
# - Lines can be excluded from consideration, even entire suites of lines.
|
||||
# - The filesystem cache of covered lines can be disabled programmatically.
|
||||
# - Modernized the code.
|
||||
#
|
||||
# 2004-12-14 NMB Minor tweaks. Return 'analysis' to its original behavior
|
||||
# and add 'analysis2'. Add a global for 'annotate', and factor it, adding
|
||||
# 'annotate_file'.
|
||||
#
|
||||
# 2004-12-31 NMB Allow for keyword arguments in the module global functions.
|
||||
#
|
||||
# C. COPYRIGHT AND LICENCE
|
||||
#
|
||||
# Copyright 2001 Gareth Rees. All rights reserved.
|
||||
# Copyright 2004 Ned Batchelder. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
# DAMAGE.
|
||||
#
|
||||
# $Id: coverage.py 5 2004-12-14 12:08:23Z ned $
|
||||
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 505 B After Width: | Height: | Size: 505 B |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 94 KiB |
@@ -1,332 +1,200 @@
|
||||
#!/home/tom/ab/renpy/lib/py3-linux-x86_64/python
|
||||
|
||||
# Builds a distribution of Ren'Py.
|
||||
from __future__ import division, absolute_import, with_statement, print_function, unicode_literals
|
||||
|
||||
import future.standard_library
|
||||
import future.utils
|
||||
PY2 = future.utils.PY2
|
||||
|
||||
import sys
|
||||
import os.path
|
||||
import os
|
||||
import compileall
|
||||
import shutil
|
||||
import subprocess
|
||||
import argparse
|
||||
import time
|
||||
import collections
|
||||
import sys
|
||||
|
||||
try:
|
||||
from importlib import reload
|
||||
except ImportError:
|
||||
pass
|
||||
def match_times(source, dest):
|
||||
|
||||
stat = os.stat(source)
|
||||
os.utime(dest, (stat.st_atime, stat.st_mtime))
|
||||
|
||||
def dosify(s):
|
||||
return s.replace("\n", "\r\n")
|
||||
|
||||
def copy_file(source, dest, license="", dos=True):
|
||||
|
||||
if dest.endswith(".bak"):
|
||||
return
|
||||
|
||||
if dest.endswith(".cache"):
|
||||
return
|
||||
|
||||
if dest.endswith("~"):
|
||||
return
|
||||
|
||||
print source, "->", dest
|
||||
|
||||
sf = file(source, "rb")
|
||||
df = file(dest, "wb")
|
||||
|
||||
if dest.endswith(".py") or dest.endswith(".rpy"):
|
||||
if not dest.endswith("subprocess.py"):
|
||||
df.write(license)
|
||||
|
||||
data = sf.read()
|
||||
if dest.endswith(".txt") or dest.endswith(".py") or dest.endswith(".pyw") or \
|
||||
dest.endswith(".rpy") or dest.endswith(".bat"):
|
||||
if dos:
|
||||
data = dosify(data)
|
||||
if data.startswith("#!"):
|
||||
data = data.replace("\r\n", "\n\r\n", 1)
|
||||
|
||||
df.write(data)
|
||||
|
||||
sf.close()
|
||||
df.close()
|
||||
|
||||
match_times(source, dest)
|
||||
|
||||
|
||||
ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||
def copy_tree(source, dest, should_copy=lambda fn : True, license=""):
|
||||
|
||||
os.makedirs(dest)
|
||||
|
||||
for dirpath, dirnames, filenames in os.walk(source):
|
||||
|
||||
def zip_rapt_symbols(destination):
|
||||
"""
|
||||
Zips up the rapt symbols.
|
||||
"""
|
||||
if "/saves" in dirpath:
|
||||
continue
|
||||
|
||||
import zipfile
|
||||
if "/CVS" in dirpath:
|
||||
continue
|
||||
|
||||
if PY2:
|
||||
zf = zipfile.ZipFile(destination + "/android-native-symbols.zip", "w", zipfile.ZIP_DEFLATED)
|
||||
else:
|
||||
zf = zipfile.ZipFile(destination + "/android-native-symbols.zip", "w", zipfile.ZIP_DEFLATED, compresslevel=3)
|
||||
if "/.svn" in dirpath:
|
||||
continue
|
||||
|
||||
for dn, dirs, files in os.walk("rapt/symbols"):
|
||||
for fn in dirs + files:
|
||||
fn = os.path.join(dn, fn)
|
||||
arcname = os.path.relpath(fn, "rapt/symbols")
|
||||
zf.write(fn, arcname)
|
||||
reldir = dirpath[len(source):]
|
||||
dstrel = dest + "/" + reldir
|
||||
|
||||
zf.close()
|
||||
for i in dirnames:
|
||||
if i == "CVS":
|
||||
continue
|
||||
|
||||
def copy_tutorial_file(src, dest):
|
||||
"""
|
||||
Copies a file from src to dst. Lines between "# tutorial-only" and
|
||||
"# end-tutorial-only" comments are omitted from the copy.
|
||||
"""
|
||||
if i == ".svn":
|
||||
continue
|
||||
|
||||
# True if we want to copy the line.
|
||||
copy = True
|
||||
os.mkdir(dstrel + "/" + i)
|
||||
|
||||
with open(src, "r") as sf, open(dest, "w") as df:
|
||||
for l in sf:
|
||||
if "# tutorial-only" in l:
|
||||
copy = False
|
||||
elif "# end-tutorial-only" in l:
|
||||
copy = True
|
||||
else:
|
||||
if copy:
|
||||
df.write(l)
|
||||
for i in filenames:
|
||||
if not should_copy(i):
|
||||
continue
|
||||
|
||||
def link_directory(dirname):
|
||||
dn = os.path.join(ROOT, dirname)
|
||||
if i.startswith("."):
|
||||
continue
|
||||
|
||||
if os.path.exists(dn):
|
||||
os.unlink(dn)
|
||||
copy_file(dirpath + "/" + i, dstrel + "/" + i, license=license)
|
||||
|
||||
|
||||
if PY2:
|
||||
source = dn + "2"
|
||||
else:
|
||||
source = dn + "3"
|
||||
|
||||
if os.path.exists(source):
|
||||
os.symlink(source, dn)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
start = time.time()
|
||||
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("version", nargs="?")
|
||||
ap.add_argument("--fast", action="store_true")
|
||||
ap.add_argument("--pygame", action="store", default=None)
|
||||
ap.add_argument("--no-rapt", action="store_true")
|
||||
ap.add_argument("--variant", action="store")
|
||||
ap.add_argument("--sign", action="store_true", default=True)
|
||||
ap.add_argument("--nosign", action="store_false", dest="sign")
|
||||
ap.add_argument("--notarized", action="store_true", dest="notarized")
|
||||
ap.add_argument("--vc-version-only", action="store_true")
|
||||
ap.add_argument("--link-directories", action="store_true")
|
||||
ap.add_argument("--append-version", action="store_true")
|
||||
|
||||
args = ap.parse_args()
|
||||
|
||||
link_directory("rapt")
|
||||
link_directory("renios")
|
||||
link_directory("web")
|
||||
|
||||
if args.link_directories:
|
||||
return
|
||||
|
||||
if args.sign:
|
||||
os.environ["RENPY_MAC_IDENTITY"] = "Developer ID Application: Tom Rothamel (XHTE5H7Z79)"
|
||||
|
||||
if PY2 and not sys.flags.optimize:
|
||||
raise Exception("Not running with python optimization.")
|
||||
|
||||
if not os.path.abspath(sys.executable).startswith(ROOT + "/lib"):
|
||||
raise Exception("Distribute must be run with the python in lib/.")
|
||||
|
||||
# Revision updating is done early, so we can do it even if the rest
|
||||
# of the program fails.
|
||||
|
||||
# Determine the version. We grab the current revision, and if any
|
||||
# file has changed, bump it by 1.
|
||||
import renpy
|
||||
|
||||
if args.version is None:
|
||||
args.version = ".".join(str(i) for i in renpy.version_tuple[:-1])
|
||||
|
||||
try:
|
||||
s = subprocess.check_output([ "git", "describe", "--tags", "--dirty", ]).decode("utf-8").strip()
|
||||
parts = s.strip().split("-")
|
||||
dirty = "dirty" in parts
|
||||
|
||||
commits_per_day = collections.defaultdict(int)
|
||||
|
||||
for i in subprocess.check_output([ "git", "log", "-99", "--pretty=%cd", "--date=format:%Y%m%d" ]).decode("utf-8").split():
|
||||
commits_per_day[i[2:]] += 1
|
||||
|
||||
if dirty:
|
||||
key = time.strftime("%Y%m%d")[2:]
|
||||
vc_version = "{}{:02d}".format(key, commits_per_day[key] + 1)
|
||||
else:
|
||||
key = max(commits_per_day.keys())
|
||||
vc_version = "{}{:02d}".format(key, commits_per_day[key])
|
||||
except Exception:
|
||||
vc_version = 0
|
||||
|
||||
with open("renpy/vc_version.py", "w") as f:
|
||||
import socket
|
||||
official = socket.gethostname() == "eileen"
|
||||
nightly = args.version and "nightly" in args.version
|
||||
|
||||
f.write("vc_version = {}\n".format(vc_version))
|
||||
f.write("official = {}\n".format(official))
|
||||
f.write("nightly = {}\n".format(nightly))
|
||||
|
||||
if args.vc_version_only:
|
||||
return
|
||||
|
||||
try:
|
||||
reload(sys.modules['renpy.vc_version']) # @UndefinedVariable
|
||||
except Exception:
|
||||
import renpy.vc_version # @UnusedImport
|
||||
|
||||
reload(sys.modules['renpy'])
|
||||
|
||||
if args.append_version:
|
||||
args.version += "-" + renpy.version_only
|
||||
|
||||
# Check that the versions match.
|
||||
full_version = renpy.version_only # @UndefinedVariable
|
||||
if "-" not in args.version \
|
||||
and not full_version.startswith(args.version):
|
||||
raise Exception("The command-line and Ren'Py versions do not match.")
|
||||
|
||||
os.environ['RENPY_BUILD_VERSION'] = args.version
|
||||
|
||||
# The destination directory.
|
||||
destination = os.path.join("dl", args.version)
|
||||
|
||||
if args.variant:
|
||||
destination += "-" + args.variant
|
||||
|
||||
if os.path.exists(os.path.join(destination, "checksums.txt")):
|
||||
raise Exception("The checksums.txt file exists.")
|
||||
|
||||
print("Version {} ({})".format(args.version, full_version))
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
renpy_sh = "./renpy3.sh"
|
||||
else:
|
||||
renpy_sh = "./renpy2.sh"
|
||||
|
||||
# Compile all the python files.
|
||||
compileall.compile_dir("renpy/", ddir="renpy/", force=True, quiet=1)
|
||||
|
||||
# Compile the various games.
|
||||
if not args.fast:
|
||||
for i in [ 'tutorial', 'launcher', 'the_question' ]:
|
||||
print("Compiling", i)
|
||||
subprocess.check_call([renpy_sh, i, "compile" ])
|
||||
|
||||
# Kick off the rapt build.
|
||||
if not args.fast:
|
||||
|
||||
print("Cleaning RAPT.")
|
||||
|
||||
sys.path.insert(0, os.path.join(ROOT, "rapt", "buildlib"))
|
||||
|
||||
import rapt.interface # type: ignore
|
||||
import rapt.build # type: ignore
|
||||
|
||||
interface = rapt.interface.Interface()
|
||||
rapt.build.distclean(interface)
|
||||
|
||||
print("Compiling RAPT and renios.")
|
||||
|
||||
compileall.compile_dir("rapt/buildlib/", ddir="rapt/buildlib/", quiet=1)
|
||||
compileall.compile_dir("renios/buildlib/", ddir="renios/buildlib/", quiet=1)
|
||||
|
||||
if not os.path.exists(destination):
|
||||
os.makedirs(destination)
|
||||
|
||||
zip_rapt_symbols(destination)
|
||||
|
||||
if args.fast:
|
||||
|
||||
cmd = [
|
||||
renpy_sh,
|
||||
"launcher",
|
||||
"distribute",
|
||||
"launcher",
|
||||
"--package",
|
||||
"sdk",
|
||||
"--destination",
|
||||
destination,
|
||||
"--no-update",
|
||||
]
|
||||
|
||||
else:
|
||||
cmd = [
|
||||
renpy_sh,
|
||||
"launcher",
|
||||
"distribute",
|
||||
"launcher",
|
||||
"--destination",
|
||||
destination,
|
||||
]
|
||||
|
||||
if args.notarized:
|
||||
cmd.extend([
|
||||
"--macapp",
|
||||
"notarized/out",
|
||||
])
|
||||
|
||||
print()
|
||||
subprocess.check_call(cmd)
|
||||
|
||||
# Sign the update.
|
||||
if not args.fast:
|
||||
subprocess.check_call([
|
||||
"scripts/sign_update.py",
|
||||
"/home/tom/ab/keys/renpy_private.pem",
|
||||
os.path.join(destination, "updates.json"),
|
||||
])
|
||||
|
||||
# Package pygame_sdl2.
|
||||
if not args.fast:
|
||||
subprocess.check_call([
|
||||
"pygame_sdl2/setup.py",
|
||||
"-q",
|
||||
"egg_info",
|
||||
"--tag-build",
|
||||
"-for-renpy-" + args.version,
|
||||
"sdist",
|
||||
"-d",
|
||||
os.path.abspath(destination)
|
||||
])
|
||||
|
||||
# Write 7z.exe.
|
||||
sdk = "renpy-{}-sdk".format(args.version)
|
||||
|
||||
if not args.fast:
|
||||
|
||||
# shutil.copy("renpy-ppc.zip", os.path.join(destination, "renpy-ppc.zip"))
|
||||
|
||||
with open("7z.sfx", "rb") as f:
|
||||
sfx = f.read()
|
||||
|
||||
os.chdir(destination)
|
||||
|
||||
if os.path.exists(sdk):
|
||||
shutil.rmtree(sdk)
|
||||
|
||||
subprocess.check_call([ "unzip", "-q", sdk + ".zip" ])
|
||||
|
||||
if os.path.exists(sdk + ".7z"):
|
||||
os.unlink(sdk + ".7z")
|
||||
|
||||
sys.stdout.write("Creating -sdk.7z")
|
||||
|
||||
p = subprocess.Popen([ "7z", "a", sdk + ".7z", sdk], stdout=subprocess.PIPE)
|
||||
for i, _l in enumerate(p.stdout): # type: ignore
|
||||
if i % 10 != 0:
|
||||
continue
|
||||
|
||||
sys.stdout.write(".")
|
||||
sys.stdout.flush()
|
||||
|
||||
if p.wait() != 0:
|
||||
raise Exception("7z failed")
|
||||
|
||||
with open(sdk + ".7z", "rb") as f:
|
||||
data = f.read()
|
||||
|
||||
with open(sdk + ".7z.exe", "wb") as f:
|
||||
f.write(sfx)
|
||||
f.write(data)
|
||||
|
||||
os.unlink(sdk + ".7z")
|
||||
shutil.rmtree(sdk)
|
||||
|
||||
else:
|
||||
os.chdir(destination)
|
||||
|
||||
if os.path.exists(sdk + ".7z.exe"):
|
||||
os.unlink(sdk + ".7z.exe")
|
||||
|
||||
print()
|
||||
|
||||
print("Distribute took {:.0f} seconds.".format(time.time() - start))
|
||||
|
||||
target = sys.argv[1]
|
||||
gamedir = sys.argv[2]
|
||||
|
||||
# Read license.
|
||||
lf = file("LICENSE.txt")
|
||||
license = "#!/usr/bin/env python\n\r\n"
|
||||
|
||||
for l in lf:
|
||||
|
||||
if l.startswith("---"):
|
||||
break
|
||||
|
||||
license += "# " + l
|
||||
|
||||
lf.close()
|
||||
|
||||
license = dosify(license)
|
||||
|
||||
if os.path.exists(target):
|
||||
raise Exception("Target exists!")
|
||||
|
||||
# Start off with the target.
|
||||
copy_tree("dist", target,
|
||||
should_copy = lambda fn : fn not in [ 'traceback.txt' ] and not fn.endswith(".log"))
|
||||
|
||||
# Copy renpy modules.
|
||||
copy_tree("renpy", target + "/renpy",
|
||||
should_copy = lambda fn : fn.endswith(".py"),
|
||||
license=license)
|
||||
|
||||
doc_files = [
|
||||
'example.html',
|
||||
'reference.html',
|
||||
'style.css',
|
||||
'RELEASING.txt',
|
||||
'EXTENDING.txt',
|
||||
]
|
||||
|
||||
# Copy doc
|
||||
copy_tree("doc", target + "/doc",
|
||||
should_copy = lambda fn : fn in doc_files)
|
||||
|
||||
# Copy the game
|
||||
copy_tree(gamedir, target + "/game",
|
||||
should_copy = lambda fn : not fn.endswith(".mpg"))
|
||||
|
||||
copy_tree("common", target + "/common")
|
||||
|
||||
# copy_tree("dse", target + "/dse")
|
||||
|
||||
copy_tree("extras", target + "/extras",
|
||||
should_copy = lambda fn : not (fn.endswith(".rpyc") or fn.endswith(".rpyb")) )
|
||||
|
||||
copy_tree("scripts", target + "/scripts")
|
||||
|
||||
copy_tree("tools", target + "/tools", license=license)
|
||||
|
||||
def cp(x, license="", dos=True):
|
||||
copy_file(x, target + "/" + x, dos=dos)
|
||||
|
||||
cp("CHANGELOG.txt")
|
||||
cp("LICENSE.txt")
|
||||
cp("README_RENPY.txt")
|
||||
# cp("archive_images.bat")
|
||||
# cp("lint.bat")
|
||||
cp("run_game.py", license=license)
|
||||
# copy_file("run_game.py", target + "/run_game.pyw", license=license)
|
||||
# copy_file("run_game.py", target + "/run_dse.py", license=license)
|
||||
# copy_file("run_game.py", target + "/run_dse.pyw", license=license)
|
||||
# copy_file("run_game.rpyl", target + "/run_game.rpyl")
|
||||
# copy_file("run_dse.rpyl", target + "/run_dse.rpyl")
|
||||
# cp("archiver.py", license=license)
|
||||
# cp("build_exe.py", license=license)
|
||||
# cp("add_from.py", license=license)
|
||||
# cp("dump_text.py", license=license)
|
||||
# cp("renpy-mode.el")
|
||||
|
||||
os.mkdir(target + "/module")
|
||||
|
||||
module_files = [
|
||||
"README.txt",
|
||||
"_renpy.pyx",
|
||||
"_renpy.c",
|
||||
"core.c",
|
||||
"linmixer.py",
|
||||
"native_midi.h",
|
||||
"native_midi_common.c",
|
||||
"native_midi_common.h",
|
||||
"native_midi_mac.c",
|
||||
"native_midi_win32.c",
|
||||
"nativemidi.c",
|
||||
"nativemidi.pyx",
|
||||
"pss.c",
|
||||
"pss.h",
|
||||
"pysdlsound.c",
|
||||
"pysdlsound.pyx",
|
||||
"rwobject.c",
|
||||
"renpy.h",
|
||||
"setup.py",
|
||||
"winmixer.c",
|
||||
"winmixer.pyx",
|
||||
]
|
||||
|
||||
for i in module_files:
|
||||
cp("module/" + i)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
Steps one needs to take to make a Ren'Py distro.
|
||||
|
||||
0) Update the documentation. (This involves dump_styles, and perhaps
|
||||
copying in a new config.keymap)
|
||||
|
||||
1) Change the version in run_game.py
|
||||
|
||||
2) In cygwin, run "release.sh <version>"
|
||||
|
||||
3) Test run_game.exe
|
||||
4) Test console.exe
|
||||
|
||||
8) Release.
|
||||
@@ -0,0 +1,98 @@
|
||||
================
|
||||
Extending Ren'Py
|
||||
================
|
||||
|
||||
This document talks a bit about extending Ren'Py using Python
|
||||
modules. This is fairly advanced stuff, as several things change
|
||||
between python-in-Ren'Py and python modules. Still, python modules
|
||||
allow one to extend the very core of Ren'Py itself, which may come in
|
||||
handy one day. They also let you leave Ren'Py entirely, to write
|
||||
things like minigames in pygame directly.
|
||||
|
||||
It's assumed that if you're reading this, you have a decent amount of
|
||||
familiarity with programming in Python. If this isn't the case, then
|
||||
you should probably go and read the documentation on
|
||||
http://www.python.org/.
|
||||
|
||||
|
||||
Python Modules in Ren'Py
|
||||
========================
|
||||
|
||||
Python modules can be imported from Ren'Py code using the standard
|
||||
Python import statement. Ren'Py installs an import hook that will
|
||||
attempt to load python modules from the same places that it searches
|
||||
for all game data. This includes the game directory, and any archives
|
||||
listed in config.archives.
|
||||
|
||||
It's important to ensure that your modules do not conflict with
|
||||
standard python modules used by Ren'Py, or with the renpy modules
|
||||
themselves. This is best done by putting your modules in an unused
|
||||
package, such as local. Ren'Py respects the Python convention that a
|
||||
package is a directory with an __init__.py file in it, and extends
|
||||
this convention into .rpa files when necessary.
|
||||
|
||||
There are a number of differences between Python modules loaded by
|
||||
Ren'Py and python-in-renpy as found in the python blocks used in .rpy
|
||||
scripts. One of these differences is that the module names used are
|
||||
fairly different. Some of the commonly used modules have different
|
||||
from python-in-renpy (and hence the documentation). Some of these
|
||||
changes are:
|
||||
|
||||
* ui is renpy.ui
|
||||
* anim is renpy.display.anim
|
||||
* config is renpy.config
|
||||
* renpy.sound is renpy.audio.sound
|
||||
* renpy.music is renpy.audio.music
|
||||
* renpy is renpy.exports
|
||||
* The top-level namespace is renpy.store
|
||||
|
||||
It's important to deal with these name changes when writing python
|
||||
module code.
|
||||
|
||||
A second, and perhaps more important, change is that objects loaded
|
||||
from python modules do not, by default, participate in
|
||||
rollback. Because of this, we do not recommend returning dictionaries,
|
||||
lists, or objects from python module code, but instead we prefer
|
||||
returning immutable types (such as strings, integers, floats, and
|
||||
tuples).
|
||||
|
||||
If it is necessary to return an object that needs to participate in
|
||||
rollback, then one must construct objects of the appropriate types
|
||||
manually. Lists must be of type renpy.python.RevertableList,
|
||||
dictionaries of type renpy.python.RevertableDict, and object should be
|
||||
of a subclass of renpy.python.RevertableObject.
|
||||
|
||||
|
||||
Writing Pygame in Ren'Py
|
||||
========================
|
||||
|
||||
It's possible (if perhaps not recommended) to include pygame code in
|
||||
Ren'Py. One use of this would be to implement mini-games that would
|
||||
otherwise not fit into the Ren'Py framework. (But it may make more
|
||||
sense to implement a minigame as a Ren'Py widget.) It can be done by
|
||||
importing the appropriate pygame modules, and then calling pygame
|
||||
functions as desired.
|
||||
|
||||
Ren'Py initializes the pygame display to be 24 or 32 bits in depth, at
|
||||
the sizes chosen by screen_width and screen_height. The display is set
|
||||
to be a software surface. The display can be retrieved with
|
||||
pygame.display.get_surface().
|
||||
|
||||
The pygame mixer is not initialized, and is often not included as part
|
||||
of Ren'Py. Instead, we recommend using renpy.audio.sound and
|
||||
renpy.audio.music calls to play sound and music. When playing music,
|
||||
it is necessary to call renpy.audio.interact() after music is changed,
|
||||
and renpy.audio.periodic() on a regular basis (Ren'Py does it at 20Hz)
|
||||
to ensure that track updates occur when appropriate.
|
||||
|
||||
On returning to Ren'Py from a pygame interation that redraws the
|
||||
screen, it is necessary to call
|
||||
renpy.exports.force_full_redraw(). This will force the screen to be
|
||||
redrawn completely. If this is not done, Ren'Py may not know about
|
||||
areas of the screen drawn to directly with pygame.
|
||||
|
||||
|
||||
Implementing Ren'Py Widgets
|
||||
===========================
|
||||
|
||||
(This hasn't been written yet, bug PyTom if you really need it.)
|
||||
@@ -0,0 +1,14 @@
|
||||
Some notes about making sure things are imported in the proper order:
|
||||
|
||||
1) Every module should import 'renpy'.
|
||||
|
||||
2) It's okay for modules to 'import renpy.game as game'.
|
||||
|
||||
3) Those are all the imports that normal modules should do.
|
||||
|
||||
4) renpy/__init__.py should list all the modules in the system, a
|
||||
topologically sorted order, such that if a definition in a uses
|
||||
a definition in b, b comes before a. Please note that this only
|
||||
considers the top level of the file.
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
all:: reference.html example.html
|
||||
|
||||
reference.html: reference.xml preprocess.py stylesheet.xslt style.css styles.xml
|
||||
python preprocess.py reference.xml > reference.hi.xml
|
||||
xsltproc stylesheet.xslt reference.hi.xml > reference.html
|
||||
cp reference.html style.css ~/ab/website/renpy/devel/doc
|
||||
|
||||
|
||||
example.html: example.xml preprocess.py stylesheet.xslt style.css styles.xml ../demo2/script.rpy
|
||||
python preprocess.py example.xml > example.hi.xml
|
||||
xsltproc stylesheet.xslt example.hi.xml > example.html
|
||||
cp example.html style.css ~/ab/website/renpy/devel/doc
|
||||
@@ -0,0 +1,131 @@
|
||||
Releasing Ren'Py Games
|
||||
|
||||
PyTom <pytom@bishoujo.us>
|
||||
|
||||
Updated: 2005-11-21
|
||||
|
||||
|
||||
Okay, so you've finished creating a game using Ren'Py, and now you
|
||||
want to release it. Congratulations! This tech note will help you to
|
||||
package up your creation and send it into the world.
|
||||
|
||||
The first thing to do is to check to see if you have the latest
|
||||
version of Ren'Py. New versions of Ren'Py fix bugs in old versions. If
|
||||
you're not using the latest version, your game may have known bugs in
|
||||
it. At the very least, ask about what was fixed between versions if
|
||||
you insist on using an older version.
|
||||
|
||||
The next thing to do is to ensure that you actually have a finished
|
||||
and working game. Did you remember to run add_from? The easiest way to
|
||||
do this is to change into the tools/ directory, and run
|
||||
game_add_from.bat. That tool will add from clauses to all of the call
|
||||
statements in your program, ensuring that everything continues working
|
||||
after a game is reloaded. It will also create .bak files for each of
|
||||
the .rpy files it can find, so you'll probably want to delete those by
|
||||
hand once you've tested the changes.
|
||||
|
||||
You'll also want to run game_lint.bat, also found in the tools/
|
||||
directory. This is a tool that will check your Ren'Py game for subtle
|
||||
bugs. If it reports any bugs, you should fix them, and run lint.bat
|
||||
again until they're all fixed. If you've changed the name of the .exe,
|
||||
you'll probably need to edit lint.bat so that it runs. In general, you
|
||||
don't want to release a game with lint errors, or at least a good
|
||||
excuse for said errors. Many of the problems that are found this way
|
||||
are fairly subtle, and only manifest themselves on some of the
|
||||
platforms Ren'Py supports.
|
||||
|
||||
If you're not totally sure that everything is A-OK, you can send me
|
||||
the game and I will check it over in confidence. If there's enough
|
||||
demand, I'll put together a tool that will elide the game text, so
|
||||
just the code and structure is left.
|
||||
|
||||
I also ask that you add a Ren'Py credit into your game. I tend to use
|
||||
the line "Powered by Ren'Py.", but feel free to word it however you
|
||||
want.
|
||||
|
||||
Another thing to do is to change the icon of the exe file. The best
|
||||
way to do this is to create a .ico file containing 48x48, 32x32, and
|
||||
16x16 versions of your icon, and then to use XN Resource Explorer to
|
||||
go in and change the icon of the file. Just delete off the existing
|
||||
icon, and then do a command like "Import image resource..." (going
|
||||
from memory here) to add in the new icon. Save the modified
|
||||
executable.
|
||||
|
||||
Now, we have everything that goes into the final distribution. In
|
||||
fact, we have more than enough, as Ren'Py ships with a number of tools
|
||||
that aren't needed to run a game. So we need to decide which files to
|
||||
keep in various circumstances.
|
||||
|
||||
There are basically three different kinds of distributions you can
|
||||
make. The first is a windows executable distribution (abbreviated
|
||||
windows), that allows the user to run your game without having to
|
||||
download any additional software. The second kind is a
|
||||
python-dependent (python) distribution, which can run on any computer
|
||||
that supports python 2.3 and pygame. The final type of distribution
|
||||
that is supported is a cross platform distribution, which can run both
|
||||
on windows and on any computer that supports python.
|
||||
|
||||
To make a windows distribution, you need the files that I'll mark
|
||||
windows or all. To make a python distribution, you need the files that
|
||||
I'll mark python and all. And to make a cross-platform distribution,
|
||||
you'll need all the indicated files.
|
||||
|
||||
Anyway, the list of files that you may concievably want to include as
|
||||
part of your game are:
|
||||
|
||||
|
||||
common/ [all] - A directory containing common script files that help
|
||||
Ren'Py work.
|
||||
|
||||
lib/ [windows] - A directory containing the code and libraries needed
|
||||
to run Ren'Py on windows.
|
||||
|
||||
game/ [all] - The game directory containing your scripts and
|
||||
data. You probably do not want to distribute the saves/ directory
|
||||
inside here, so remove that. Be sure not to distribute
|
||||
game/saves/persistent, and be warned that it is recreated each time
|
||||
Ren'Py is run.
|
||||
|
||||
run_game.exe [windows] - The game executable that the user runs.
|
||||
|
||||
moonlight.py [python] - The main python script that runs your game.
|
||||
|
||||
python23.dll [windows] - The DLL containing the python interpreter. It
|
||||
may change versions in the future, in which case the numbers will
|
||||
change.
|
||||
|
||||
renpy/ [python] - Contains the python source code for Ren'Py.
|
||||
|
||||
module/ [python] - Contains the source code for the _renpy module.
|
||||
|
||||
LICENSE.txt [all] - The license for Ren'Py. You may want to integrate
|
||||
this into the license for your game as a whole.
|
||||
|
||||
README_RENPY.txt [all] - Instructions for running a Ren'Py game. You
|
||||
may want to integrate this into your own README.
|
||||
|
||||
Anyway, once you have all of the files, you can zip them up (perhaps
|
||||
adding in things like README and license files) and post them on the
|
||||
web. You've just released a Ren'Py game!
|
||||
|
||||
|
||||
For the record, the following are the files distributed with Ren'Py
|
||||
that should not be distributed with a game.
|
||||
|
||||
CHANGELOG.txt - The Ren'Py changelog.
|
||||
|
||||
console.exe - A console/debugging version of Ren'Py.
|
||||
|
||||
extras - Scripts that give additional features. Uninteresting as a
|
||||
whole, as to be used the scripts must be in the game/ directory.
|
||||
|
||||
scripts - Example scripts.
|
||||
|
||||
tools - Development tools.
|
||||
|
||||
game/saves - This is the directory containing the savegame info. It
|
||||
should be deleted before distribution, but please note that running
|
||||
Ren'Py recreates it automatically. It contains a file named persistent
|
||||
that stores the list of seen lines. Shipping a persistent file
|
||||
may lead to the option to skip seen text skipping text not seen by
|
||||
the end-user.
|
||||
@@ -0,0 +1,15 @@
|
||||
# With pygame surfaces and fill.
|
||||
|
||||
Menu mousemove: .1336, .1244, .1437
|
||||
"Save when I tell you to": .055, .066, .0476
|
||||
|
||||
|
||||
# Without fill.
|
||||
|
||||
Menu mousemove: .115, .129, .134
|
||||
"Save when I tell you to": .050, .057, .046
|
||||
|
||||
# With new surface code.
|
||||
|
||||
Menu mousemove: .02something
|
||||
"Save when I tell you to": .038, .040, .038
|
||||
@@ -0,0 +1,44 @@
|
||||
import xml.dom.minidom
|
||||
import sys
|
||||
|
||||
def escape(s):
|
||||
return s.replace("&", "&").replace("<", "<").replace('"', """)
|
||||
|
||||
def transform_children(node):
|
||||
|
||||
rv = [ ]
|
||||
|
||||
for n in node.childNodes:
|
||||
rv.append(transform(n))
|
||||
|
||||
return ''.join(rv)
|
||||
|
||||
|
||||
def transformElement(node):
|
||||
|
||||
attributes = ' '.join(['%s="%s"' % (k, escape(v)) for k,v in node.attributes.items()])
|
||||
|
||||
tag = node.tagName
|
||||
|
||||
|
||||
# Otherwise, the default.
|
||||
return "<%s %s>%s</%s>" % (node.tagName, attributes,
|
||||
transform_children(node), node.tagName)
|
||||
|
||||
|
||||
|
||||
def transform(node):
|
||||
|
||||
if node.nodeType == node.ELEMENT_NODE:
|
||||
return transformElement(node)
|
||||
else:
|
||||
return escape(str(node))
|
||||
|
||||
def main():
|
||||
|
||||
dom = xml.dom.minidom.parse(sys.argv[1])
|
||||
|
||||
print transform(dom.documentElement)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<doc>
|
||||
<title>Demo Script Example</title>
|
||||
|
||||
<p><a href="tutorial.html">Return to the tutorial.</a></p>
|
||||
|
||||
<example>
|
||||
<!-- include ../demo2/script.rpy -->
|
||||
</example>
|
||||
|
||||
<p><a href="tutorial.html">Return to the tutorial.</a></p>
|
||||
|
||||
</doc>
|
||||
@@ -0,0 +1,163 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<renpydoc>
|
||||
|
||||
<!-- (define-key xml-mode-map '(control return) 'tompy-xml-ctrlret) -->
|
||||
|
||||
<section title="Say Statement">
|
||||
|
||||
<rule name="statement">
|
||||
<alt>string</alt>
|
||||
<alt>string string</alt>
|
||||
<alt>identifier string</alt>
|
||||
</rule>
|
||||
|
||||
<formal>
|
||||
These three productions are the various forms of the say
|
||||
statement. In order, they are the 1-string form, the 2-string
|
||||
form, and the name-string form. We'll discuss individually
|
||||
what each one does.
|
||||
</formal><formal>
|
||||
The 1-string form calls the <f>say</f> function (found in the
|
||||
execution context) with the string.
|
||||
</formal><formal>
|
||||
The 2-string form also calls the <f>say</f> function, but this
|
||||
time it passes it the two strings. The strings are passed in the
|
||||
order in which they appear.
|
||||
</formal><formal>
|
||||
When a identifierstring say statement is executed, the first thing
|
||||
that happens is that the identifier is looked up in the execution
|
||||
context. It's an error if the name is not defined. If the name
|
||||
is bound to a string, both strings are passed to the <f>say</f>
|
||||
function, as if the 2-string form was used. Otherwise, the
|
||||
<f>say</f> method is called on the object that the identifier was
|
||||
bound to, with the string as the only argument.
|
||||
</formal>
|
||||
|
||||
<user>
|
||||
While the various say statements are very flexible, there are
|
||||
some conventions that we suspect users will use when writing
|
||||
games.
|
||||
</user><user>
|
||||
The 1-string form of the say statment is rendered to the user in
|
||||
a text box without any name associated with it. It's used to
|
||||
indicate the thoughts of the POV character, or to narrate
|
||||
actions that occur in the game.
|
||||
</user><user>
|
||||
The 2-string form is displayed with the first string as a name,
|
||||
and the second string as the line of dialogue said by the
|
||||
character with that name. It's used to indicate spoken dialogue,
|
||||
usually from characters that are minor enough to not have a
|
||||
character object associated with them. The 2-string form is
|
||||
rarely used directly, as normally the name-string or 1-string
|
||||
forms are preferred for dialogue from main characters.
|
||||
</user><user>
|
||||
Generally, when the name-string form is used, the first name
|
||||
refers to a character object. The name field of that character
|
||||
object is looked up, and used to display dialogue as if the
|
||||
2-string form was used. So the name-string form is what is used
|
||||
when we want a main character to say something. This form allows
|
||||
the direct use of the character's name before the dialogue,
|
||||
without requiring the quoting needed by the 2-string form.
|
||||
</user>
|
||||
|
||||
<example>
|
||||
"I was walking down the street one day, when I came across the postman."
|
||||
|
||||
me "Hey, any mail for me today?"
|
||||
|
||||
"Postman" "Yeah, a package came for you. But there's no return address."
|
||||
</example>
|
||||
|
||||
</section>
|
||||
|
||||
<subsection title="Image Display">
|
||||
|
||||
<formal>
|
||||
In Ren'Py, image display is controlled by a pair of image
|
||||
lists. The two image lists are the are called the master image
|
||||
list and the temporary image list. These lists contain python
|
||||
objects that are capable of drawing themselves to the
|
||||
screen. When the time comes to show something to the user, we
|
||||
iterate through the temporary image list, drawing things on
|
||||
the screen in the order in which they appear in the list. So
|
||||
the first thing in the list will be the background, and the
|
||||
thing that's drawn closest to the user will be the last thing
|
||||
in the list.
|
||||
</formal><formal>
|
||||
We can divide the execution of a Ren'Py program into periods
|
||||
where we are showing a screen to the user (for example, during
|
||||
the execution of a say statement) and periods where we are
|
||||
not. After a period of showing the screen to the user, the master image
|
||||
list is copied over the temporary image list.
|
||||
</formal>
|
||||
|
||||
<user>
|
||||
We have two image lists for two reasons. The first is the idea
|
||||
that only the temporary image list will include transitions,
|
||||
and once we're done performing the transition, we want it to
|
||||
be remove from the list. The second reason is an idea that
|
||||
(behind the scenes) the temporary list will also include
|
||||
interface elements (like boxes containing character
|
||||
dialog). We, in general, want these boxes to be shown to the
|
||||
user once and then removed from the screen.
|
||||
</user>
|
||||
|
||||
<rule name="image_name">
|
||||
<alt>identifier+</alt>
|
||||
</rule>
|
||||
|
||||
<formal>
|
||||
An image name consists of one or more identifiers. The first
|
||||
identifier is called the primary identifier of this image
|
||||
name, while the second and later identifier are called
|
||||
secondary identifers. An image name may not include a Ren'Py
|
||||
keyword.
|
||||
</formal><formal>
|
||||
Ren'Py maintains an image dictionary, which maps between image
|
||||
names and objects implementing images that can be show to the
|
||||
user.
|
||||
</formal>
|
||||
|
||||
<user>
|
||||
The idea behind primary identifiers is that we will normally
|
||||
want to display only one image of a character at a time. So we
|
||||
use an image's primary identifier to find other images in the
|
||||
master display list that have the same primary identifier.
|
||||
</user>
|
||||
|
||||
|
||||
<rule name="statement">
|
||||
<alt>"image" image_name "=" python_expression</alt>
|
||||
</rule>
|
||||
|
||||
<formal>
|
||||
This adds a new entry to the image dictionary. Specifically, the
|
||||
python_expression is evaluated to get something that's drawable,
|
||||
and then that object is stored in a tuple in the image
|
||||
dictionary.
|
||||
</formal>
|
||||
|
||||
<user>
|
||||
In general, the python expression will be a call to the image
|
||||
constructor, which loads in a new image from disk (or
|
||||
somewhere... read the documentation for the image constructor.)
|
||||
It's also possible to use another constructor, like the one for
|
||||
animation, but that will probably be more rare.
|
||||
</user><user>
|
||||
It's important to note that this doesn't actually load the image
|
||||
into ram, until close to the time when the image is actually
|
||||
needed.
|
||||
</user><user>
|
||||
In general, it makes sense to ensure that all image statements
|
||||
execure during game startup, before the call to
|
||||
startup_complete().
|
||||
</user>
|
||||
|
||||
<example>
|
||||
image woods = image("backgrounds/woods.jpg")
|
||||
image eileen red upset = image("eileen/red_upset.png")
|
||||
image eileen red happy = image("eileen/red_happy.png")
|
||||
</example>
|
||||
|
||||
</subsection>
|
||||
</renpydoc>
|
||||
@@ -0,0 +1,373 @@
|
||||
To write a game using Ren'Py, one must become familar with the Ren'Py
|
||||
script language. This file gives some basic concepts and a list of
|
||||
Ren'Py statements. A second file will give a list of python classes
|
||||
and functions that are intended to be used from Ren'Py code.
|
||||
|
||||
A Ren'Py script can first be seen as a long string of unicode
|
||||
characters. This string is first broken up into logical lines, then
|
||||
the lines are organized into blocks, and finally as a tree of
|
||||
statements. So we'll begin by defining some basic terms, and then
|
||||
we'll go on to give a list of statements Ren'Py understands.
|
||||
|
||||
Logical Lines:
|
||||
|
||||
Each file consists of one or more logical lines. The first logical
|
||||
line begins at the first character file, and subsequent logical lines
|
||||
begin immediately following the end of the previous line. Logical
|
||||
lines are normally ended by the first newline encountered. However,
|
||||
there are several cases that will cause a logical line to extend past
|
||||
a newline character:
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
A logical line containing a string must extend at least until the
|
||||
end of that string.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
A logical line parenthesis, brackets, or braces cannot end until
|
||||
each opening character ('(', ']', or '}') is matched with a closing
|
||||
character (')', ']', or '}'). The expression enclosed within is called
|
||||
a parenthetical expression.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
A newline immediately preceded by a backslash will not end a logical
|
||||
line. Instead, both characters will be treated as whitespace, and
|
||||
ignored.
|
||||
<li>
|
||||
</ul>
|
||||
|
||||
Logical lines are numbered by giving the number of the physical line
|
||||
in the file on which the logical line begins. (At most one logical
|
||||
line can exist on each physical line.)
|
||||
|
||||
If a logical line contains a hash mark in it ('#'), all characters
|
||||
from the hash mark to character before the next physical newline are
|
||||
considered to be part of a comment, and are ignored as if they were
|
||||
whitespace. If, after this step, a logical line consists entirely of
|
||||
whitespace, it is ignored.
|
||||
|
||||
Each logical line has an indentaion level. This is computed by first
|
||||
converting tabs to spaces (using 8-space tab stops), and then looking
|
||||
at the number of spaces preceding the first non-whitespace character
|
||||
on the line.
|
||||
|
||||
The following examples all consist of single logical lines with
|
||||
indentation level 0.
|
||||
|
||||
<example>
|
||||
me "How are you doing?"
|
||||
</example>
|
||||
|
||||
<example>
|
||||
"It's a question that I ask myself every day. Today, however, is
|
||||
the first time I've asked it of someone else.
|
||||
</example>
|
||||
|
||||
<example>
|
||||
$ my_list = [ 1, 2, 3, 4,
|
||||
5, 6, 7, 8 ]
|
||||
</example>
|
||||
|
||||
<example>
|
||||
$ a = 1 \
|
||||
+ 2 \
|
||||
+ 3
|
||||
</example>
|
||||
|
||||
Blocks:
|
||||
|
||||
If a logical line is followed by a logical line with greater
|
||||
indentation, the second logical line is considered to be part of the
|
||||
block of the first logical line. This block contains all logical lines
|
||||
with an indentation level equal to that of the second logical line,
|
||||
provided there are no intervening lines with an indentaion level that
|
||||
is less than that of the second logical line. If a line is encountered
|
||||
with an indentation level that is greater than that of the first
|
||||
logical line, but less than that of the second, the indentation is
|
||||
mismatched and an error is reported.
|
||||
|
||||
An example of a block (as part of an if statement) is:
|
||||
|
||||
<example>
|
||||
if happy:
|
||||
e "I'm feeling really happy today."
|
||||
e "It's like I'm the happiest person in the world."
|
||||
</example>
|
||||
|
||||
An example of an indentation mismatch is:
|
||||
|
||||
<example>
|
||||
else:
|
||||
e "I'm having a really bad day."
|
||||
e "I can't even get the indentation right."
|
||||
</example>
|
||||
|
||||
|
||||
One of the things we can parse is a string literal. String literals
|
||||
begin with a quote character (' or "), and end with a matching
|
||||
unescaped quote character. Strings use backslash (\) as an escape
|
||||
character. The following are suppored escape sequences:
|
||||
|
||||
<ul>
|
||||
<li>"\\" - Backslash</li>
|
||||
<li>"\"" - Double Quote</li>
|
||||
<li>"\'" - Single Quote</li>
|
||||
<li>"\n" - Newline</li>
|
||||
<li>"\ " - Space</li>
|
||||
</ul>
|
||||
|
||||
As the string is being parsed, but before escape processing is done,
|
||||
contiguous sequences of whitespace are converted to single space
|
||||
characters. To include more than single spaces, explicitly escape the
|
||||
desired whitespace.
|
||||
|
||||
<example>
|
||||
"I hate having to \"escape\" whitespace."
|
||||
</example>
|
||||
|
||||
Names and Numbers:
|
||||
|
||||
Names begin with a character in the set [a-zA-Z_], and may contain
|
||||
additional characters in the set [a-zA-Z0-9_]. A keyword in Ren'Py may
|
||||
not be used as a name.
|
||||
|
||||
Some example names are:
|
||||
|
||||
<example>
|
||||
eileen
|
||||
lucy
|
||||
molly
|
||||
crm_114
|
||||
_underscore_
|
||||
</example>
|
||||
|
||||
A dotted_name consists of one or more names, separated by dots '.'.
|
||||
|
||||
<example>
|
||||
eileen
|
||||
eileen.name
|
||||
eileen.address.street
|
||||
</example>
|
||||
|
||||
A number matches the characters [0-9.].
|
||||
|
||||
<example>
|
||||
1
|
||||
1234.
|
||||
3.14159
|
||||
.2
|
||||
</example>
|
||||
|
||||
|
||||
Python:
|
||||
|
||||
Ren'Py scripts may contain embedded python. There are three kinds of
|
||||
python things that can be mixed into Ren'Py: simple_expressions,
|
||||
python_expressions, and python_statements.
|
||||
|
||||
A simple_expression consists of either a dotted_name, string, or
|
||||
number optionally followed by a parenthetical expression, or just a
|
||||
single parenthetical expression.
|
||||
|
||||
Examples of simple_expressions are:
|
||||
|
||||
<example>
|
||||
eileen
|
||||
sample.image("foo.jpg", size=(100,200))
|
||||
"test"
|
||||
(1 + 2 + 3)
|
||||
</example>
|
||||
|
||||
Python_expressions occur in statements such as if and while, and
|
||||
extend from the current location on the logical line to just before
|
||||
the next ':' that is not contained within a parenthetical expression
|
||||
on the logical line.
|
||||
|
||||
Examples of expressions that are python_expressions but not
|
||||
simple_expressions are:
|
||||
|
||||
<example>
|
||||
1 + 2 + 3
|
||||
foo.bar().baz()
|
||||
</example>
|
||||
|
||||
Finally, python_statements simply assume that all remaining
|
||||
information on the line should be interpreted as python code.
|
||||
|
||||
|
||||
|
||||
Statements:
|
||||
|
||||
The parsing of each logical line is syntax-directed. When parsing a
|
||||
block, all of the logical lines in that block are parsed as
|
||||
statements. The logical lines at the top level of a file are
|
||||
considered to form a block. Here, we give the rules for parsing each
|
||||
kind of statement, as well as an overview of what each statement
|
||||
does. We'll first give some insight as to what a statement does when
|
||||
used with the standard library, and then we'll write a little about
|
||||
how it goes about accomplishing that task.
|
||||
|
||||
|
||||
|
||||
Say Statements:
|
||||
|
||||
<rule name="statement">
|
||||
<alt>string</alt>
|
||||
<alt>simple_expression string</alt>
|
||||
</rule>
|
||||
|
||||
Probably the most commonly used statement in a Ren'Py script is the
|
||||
say statement. It's so commonly used that we chose not to denote it
|
||||
with a keyword, but instead to simply make all bare strings or
|
||||
simple_expression string pairs into say statements.
|
||||
|
||||
Executing a say statement results in a line of dialogue or thought
|
||||
being displayed to the user. Ren'Py then waits for the user to click
|
||||
or otherwise dismiss the display before proceeding with execution of
|
||||
the program.
|
||||
|
||||
In the single string version of the statement, the string is taken as
|
||||
a thought or narration that should be displayed, unadorned, to the
|
||||
user. If the string is preceded by a simple_expression, the expression
|
||||
is first evaluated to yield a string, and that string is used to
|
||||
indicate to the user who is speaking the line of dialogue.
|
||||
|
||||
Ren'Py executes say statements by first evaluating the
|
||||
simple_expression, if present, and then calling the say function with
|
||||
the values of the two strings, or None if the simple_expression is not
|
||||
defined. Take a look at the documentation for the say function to see
|
||||
what happens from there, as behind the scenes it's a little more
|
||||
complicated that what we describe here.
|
||||
|
||||
Some example say statements are:
|
||||
|
||||
<example>
|
||||
"I was walking down the street one day, when I came across the postman."
|
||||
|
||||
me "Hey, any mail for me today?"
|
||||
|
||||
"Postman" "Yeah, a package came for you. But there's no return address."
|
||||
</example>
|
||||
|
||||
Here, we assume that 'me' is an expression that expands to the name of
|
||||
the main character.
|
||||
|
||||
|
||||
|
||||
Menu Statement:
|
||||
|
||||
<rule name="statement">
|
||||
<alt>"menu" name? ":" menu_block</alt>
|
||||
</rule>
|
||||
|
||||
<rule name="menu_item">
|
||||
<alt>string</alt>
|
||||
<alt>string ( "if" python_expression )? ":" block</alt>
|
||||
<alt>"set" simple_expression</alt>
|
||||
</rule>
|
||||
|
||||
The menu statement displays a menu to the user, waits for the user to
|
||||
provide a response, and then runs a block of code corresponding to the
|
||||
choice that the user made. In a visual novel game, menu statement are
|
||||
the main way in which the user can interact with the story.
|
||||
|
||||
Menu statements begin with the word "menu". This word can be followed
|
||||
by an optional name. If the name is provided, it's as if the menu
|
||||
statement was proceded by a label statement with that name. A menu
|
||||
statement must have a block associated with it, and each of the
|
||||
logical lines in this menu_block are parsed as menu_items.
|
||||
|
||||
There are three kinds of menu_items. The first is a logical line
|
||||
containing only a string. These menu_items are used to provide blocks
|
||||
of text in the menu that are not selectable. For example, they can be
|
||||
use to provide a prompt to the user.
|
||||
|
||||
The second kind of menu_item is a string, followed by an optional if
|
||||
clause, followed by a colon. This menu_item has a block of statements
|
||||
associated with it. It's used to indicate a choice on the menu. If the
|
||||
if clause is supplied, the choice is only presented to the user if the
|
||||
python_expression is true. If a choice is selected by the user, the
|
||||
statements in the block associated with the choice are executed before
|
||||
control continues after the menu statement.
|
||||
|
||||
The final menu_item is the word "set" followed by a
|
||||
simple_expression. The expression is evaluated to get a set of menu
|
||||
items to supress. This set is used to filter the menu choices. If a
|
||||
choice string is in the set, then the corresponding menu choice is not
|
||||
displayed to the user. If a set is defined, then when a choice has
|
||||
been made, the corresponding choice string is added to the set. This
|
||||
provides an easy way of having a menu where the user can select each
|
||||
item at most once.
|
||||
|
||||
Here's an example of a fairly complex menu, that demonstrates all
|
||||
these features.
|
||||
|
||||
<example>
|
||||
menu what_to_do:
|
||||
set what_to_do_set
|
||||
"What should we do today?"
|
||||
|
||||
"Go to the movies.":
|
||||
"We went to the movies."
|
||||
|
||||
"Go shopping.":
|
||||
"We went shopping, and the girls both bought swimsuits."
|
||||
$ have_swimsuits = True
|
||||
|
||||
"Go to the beach." if have_swimsuits:
|
||||
"We went to the beach together."
|
||||
</example>
|
||||
|
||||
The actual task of displaying a menu is implemented internally by a
|
||||
call to the menu python function.
|
||||
|
||||
|
||||
|
||||
Graphics Statements:
|
||||
|
||||
|
||||
Before we can discuss the graphics statements, we must first discuss
|
||||
the graphics model that Ren'Py uses. This will be covered in greater
|
||||
detail in the library reference, but we want to cover the fundamentals
|
||||
here to allow the graphics statements to be put in proper context.
|
||||
|
||||
First, let me define what it means to display a scene. The execution
|
||||
of a Ren'Py script can be roughly divided into periods when the script
|
||||
is executing code, and periods when the script is blocked wating for
|
||||
user input (as is the case when a menu or say statement
|
||||
executes). When Ren'Py is waiting for input, it is displaying a
|
||||
scene. When it gets the input it's waiting for and execution
|
||||
continues, we say it's finished displaying the scene.
|
||||
|
||||
The display in Ren'Py is controlled by three scene lists. These scene
|
||||
lists are called the master scene list, the overlay scene list, and
|
||||
the transient scene list.
|
||||
|
||||
The only scene list that is displayed to the user is the transient
|
||||
scene list. This list is ordered such that items that are at the end
|
||||
of the scene list are closest to the user. All of the items in the
|
||||
scene lists must be displayable--- capable of drawing themselves to
|
||||
the screen. See the library reference for the exact API displayable
|
||||
objects must implement.
|
||||
|
||||
When a scene is displayed, the transient scene list is extended by
|
||||
appending the overlay scene list to it. When display finishes, the
|
||||
transient scene list is replaced with a copy of the master scene
|
||||
list. The scene, show, and hide statements work by modifying both the
|
||||
master and transient scene lists.
|
||||
|
||||
Having three scene lists may seem like needless complexity, but it's
|
||||
useful in implementing transitions and other effects. When we want to
|
||||
fade in an image, we add that image to the master scene list, and that
|
||||
image wrapped in an object that handles the fade effect into the
|
||||
transient scene list. When the scene is then displayed, the image will
|
||||
fade it. The wrapped image on the transient list is then replaced
|
||||
with the image from master list, removing any overhead the fade effect
|
||||
can entail.
|
||||
|
||||
The transient list also helps when displaying UI elements, like
|
||||
dialogue or menus. Finally, the overlay list is used to display things
|
||||
that should always be presented to the user, like load and save
|
||||
buttons.
|
||||
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
|
||||
import inspect
|
||||
|
||||
sys.path.append('..')
|
||||
sys.path.append('../module')
|
||||
|
||||
import renpy
|
||||
renpy.import_all()
|
||||
|
||||
keywords = [
|
||||
r'\bimage\b',
|
||||
r'\bscene\b',
|
||||
r'\bshow\b',
|
||||
r'\bhide\b',
|
||||
r'\binit\b',
|
||||
r'\$',
|
||||
r'\blabel\b',
|
||||
r'\bmenu\b',
|
||||
r'\bset\b',
|
||||
r'\bif\b',
|
||||
r'\bwhile\b',
|
||||
r'\bjump\b',
|
||||
r'\blabel\b',
|
||||
r'\bcall\b',
|
||||
r'\breturn\b',
|
||||
r'\bfrom\b',
|
||||
r'\belif\b',
|
||||
r'\belse\b',
|
||||
r'\bpass\b',
|
||||
r'\bwith\b',
|
||||
r'\bat\b',
|
||||
r'\bpython\b',
|
||||
]
|
||||
|
||||
kwre = '|'.join(keywords)
|
||||
|
||||
def example(m):
|
||||
s = m.group(1)
|
||||
rv = ""
|
||||
pos = 0
|
||||
|
||||
while pos < len(s):
|
||||
|
||||
m = re.compile(r'(?s)"(([^"]|\\.)*)"').match(s, pos)
|
||||
if m:
|
||||
rv += '"<span class="string">%s</span>"' % m.group(1)
|
||||
pos = m.end()
|
||||
continue
|
||||
|
||||
m = re.compile(r"(?s)'(([^']|\\.)*)'").match(s, pos)
|
||||
if m:
|
||||
rv += '\'<span class="string">%s</span>\'' % m.group(1)
|
||||
pos = m.end()
|
||||
continue
|
||||
|
||||
m = re.compile(r"(?s)(#[^\n]+)").match(s, pos)
|
||||
if m:
|
||||
rv += '<span class="comment">%s</span>' % m.group(1)
|
||||
pos = m.end()
|
||||
continue
|
||||
|
||||
|
||||
m = re.compile(kwre).match(s, pos)
|
||||
if m:
|
||||
rv += '<span class="keyword">%s</span>' % m.group(0)
|
||||
pos = m.end()
|
||||
continue
|
||||
|
||||
|
||||
rv += s[pos]
|
||||
pos += 1
|
||||
|
||||
|
||||
return "<example>" + rv + "</example>"
|
||||
|
||||
def function(m):
|
||||
|
||||
name = m.group(1)
|
||||
|
||||
store = vars(renpy.store)
|
||||
renpy.store.renpy = renpy.exports
|
||||
|
||||
func = eval(name, store)
|
||||
|
||||
if isinstance(func, renpy.curry.Curry):
|
||||
if func.callable == renpy.curry.Curry:
|
||||
func = func.args[0]
|
||||
else:
|
||||
func = func.callable
|
||||
|
||||
doc = func.__doc__
|
||||
|
||||
if inspect.isclass(func):
|
||||
func = func.__init__
|
||||
if func.__doc__:
|
||||
doc += "\n" + func.__doc__
|
||||
|
||||
a, b, c, d = inspect.getargspec(func)
|
||||
args = inspect.formatargspec(a[1:], b, c, d)
|
||||
else:
|
||||
args = inspect.formatargspec(*inspect.getargspec(func))
|
||||
|
||||
args = re.sub(r"'\\x00\\x01.*?\\xff'", "im.ramp(0, 255)", args)
|
||||
|
||||
args = re.sub(r'<.*?>', '...', args)
|
||||
|
||||
docparas = []
|
||||
|
||||
for p in re.split(r'\n\s*\n', doc):
|
||||
p = p.strip()
|
||||
p = re.sub(r"\@param (\w+):", r'<param>\1</param> -', p)
|
||||
p = "<p>" + p + "</p>"
|
||||
|
||||
docparas.append(p)
|
||||
|
||||
doc = '\n'.join(docparas)
|
||||
|
||||
return '<function name="%(name)s" sig="%(args)s">%(doc)s</function>' % locals()
|
||||
|
||||
def include(m):
|
||||
|
||||
f = file(m.group(1))
|
||||
rv = f.read()
|
||||
f.close()
|
||||
return rv
|
||||
|
||||
class ret_none(object):
|
||||
def __getattr__(self, k):
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
f = file(sys.argv[1])
|
||||
s = f.read()
|
||||
f.close()
|
||||
|
||||
os.chdir("..")
|
||||
|
||||
|
||||
try:
|
||||
renpy.game.options = ret_none()
|
||||
renpy.main.main("dump_styles")
|
||||
except "foo":
|
||||
pass
|
||||
|
||||
os.chdir("doc")
|
||||
|
||||
s = re.sub(r"<!-- func (\S+) -->", function, s)
|
||||
s = re.sub(r"<!-- include (\S+) -->", include, s)
|
||||
s = re.sub(r"<!-- date -->", time.strftime("%04Y-%02m-%02d %02H:%02M"), s)
|
||||
s = re.sub(r"(?s)<example>(.*?)</example>", example, s)
|
||||
|
||||
print s
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,81 @@
|
||||
BODY {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
H1 {
|
||||
text-align: center;
|
||||
margin-top: 2em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
H2 {
|
||||
text-align: center;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
PRE.example {
|
||||
border: 2px black solid;
|
||||
margin-left: 10%;
|
||||
margin-right: 10%;
|
||||
background: #f0f0f0;
|
||||
padding: 1em;
|
||||
padding-bottom: 0em;
|
||||
}
|
||||
|
||||
DIV.rule {
|
||||
border: 2px black solid;
|
||||
margin-left: 10%;
|
||||
margin-right: 10%;
|
||||
margin-bottom: 1em;
|
||||
|
||||
background: #f0f0ff;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
SPAN.comment {
|
||||
color: #800000;
|
||||
}
|
||||
|
||||
SPAN.string {
|
||||
color: #006000;
|
||||
}
|
||||
|
||||
SPAN.keyword {
|
||||
color: #804000;
|
||||
}
|
||||
|
||||
TD.funcname {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
TD.funcsig {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
DIV.funcbody {
|
||||
margin-left: 5%;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
SPAN.param {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
P.prop {
|
||||
margin-left: 5%;
|
||||
}
|
||||
|
||||
SPAN.propname {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
DT {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
DT.code {
|
||||
font-family: monospace;
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||
|
||||
<!-- First, copy the element. -->
|
||||
<xsl:template match="*" priority="-1">
|
||||
<xsl:copy>
|
||||
<xsl:copy-of select="@*" />
|
||||
<xsl:apply-templates />
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<!-- Now, handle some elements specially. -->
|
||||
|
||||
<xsl:template match="doc">
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<title><xsl:value-of select="title" /></title>
|
||||
</head>
|
||||
<body>
|
||||
<xsl:apply-templates />
|
||||
</body>
|
||||
</html>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="toc">
|
||||
<ol>
|
||||
<xsl:for-each select="../h3">
|
||||
<li>
|
||||
<a>
|
||||
<xsl:attribute name="href">#<xsl:value-of select="." /></xsl:attribute>
|
||||
<xsl:value-of select="."/>
|
||||
</a>
|
||||
</li>
|
||||
</xsl:for-each>
|
||||
</ol>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="funcindex">
|
||||
<ul>
|
||||
<xsl:for-each select="//function">
|
||||
<xsl:sort select="@name" />
|
||||
<li>
|
||||
<a>
|
||||
<xsl:attribute name="href">#<xsl:value-of select="@name" /></xsl:attribute>
|
||||
<xsl:value-of select="@name"/>
|
||||
</a>
|
||||
</li>
|
||||
</xsl:for-each>
|
||||
</ul>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="defnindex">
|
||||
<ul>
|
||||
<xsl:for-each select="//defn">
|
||||
<xsl:sort select="@name" />
|
||||
<li>
|
||||
<a>
|
||||
<xsl:attribute name="href">#<xsl:value-of select="@name" /></xsl:attribute>
|
||||
<xsl:value-of select="@name"/>
|
||||
(<xsl:value-of select="@type"/>)
|
||||
</a>
|
||||
</li>
|
||||
</xsl:for-each>
|
||||
</ul>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="varindex">
|
||||
<ul>
|
||||
<xsl:for-each select="//var">
|
||||
<xsl:sort select="@name" />
|
||||
<li>
|
||||
<a>
|
||||
<xsl:attribute name="href">#<xsl:value-of select="@name" /></xsl:attribute>
|
||||
<xsl:value-of select="@name"/>
|
||||
</a>
|
||||
</li>
|
||||
</xsl:for-each>
|
||||
</ul>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="styleindex">
|
||||
<ul>
|
||||
<xsl:for-each select="//renpy_style">
|
||||
<xsl:sort select="@name" />
|
||||
<li>
|
||||
<a>
|
||||
<xsl:attribute name="href">#<xsl:value-of select="@name" /></xsl:attribute>
|
||||
<xsl:value-of select="@name"/>
|
||||
</a>
|
||||
</li>
|
||||
</xsl:for-each>
|
||||
</ul>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="propindex">
|
||||
<ul>
|
||||
<xsl:for-each select="//prop">
|
||||
<xsl:sort select="@name" />
|
||||
<li>
|
||||
<a>
|
||||
<xsl:attribute name="href">#<xsl:value-of select="@name" /></xsl:attribute>
|
||||
<xsl:value-of select="@name"/>
|
||||
</a>
|
||||
</li>
|
||||
</xsl:for-each>
|
||||
</ul>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="h3">
|
||||
<a>
|
||||
<xsl:attribute name="name"><xsl:value-of select="." /></xsl:attribute>
|
||||
<h3><xsl:apply-templates /></h3>
|
||||
</a>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="title">
|
||||
<h1><xsl:apply-templates /></h1>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="subtitle">
|
||||
<h2><xsl:apply-templates /></h2>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="def">
|
||||
<b><i><xsl:apply-templates /></i></b>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="example">
|
||||
<pre class="example"><xsl:apply-templates /></pre>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="rule">
|
||||
<div class="rule"><xsl:apply-templates /></div>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="function">
|
||||
<a><xsl:attribute name="name"><xsl:value-of select="@name" /></xsl:attribute></a>
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top" class="funcname"><xsl:value-of select="@name" /></td>
|
||||
<td class="funcsig"><xsl:value-of select="@sig" />:</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="funcbody">
|
||||
<xsl:apply-templates />
|
||||
</div>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="param">
|
||||
<span class="param"><xsl:apply-templates /></span>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="prop">
|
||||
<a><xsl:attribute name="name"><xsl:value-of select="@name" /></xsl:attribute></a>
|
||||
<p class="prop">
|
||||
<span class="propname"><xsl:value-of select="@name" /></span>
|
||||
--- <xsl:apply-templates />
|
||||
</p>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="renpy_style">
|
||||
<a><xsl:attribute name="name"><xsl:value-of select="@name" /></xsl:attribute></a>
|
||||
<dt><b><xsl:value-of select="@name" /></b>:</dt>
|
||||
<dd><xsl:apply-templates /></dd>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="renpy_style_inherits">
|
||||
(inherits from <b><xsl:apply-templates /></b>) <br />
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="var">
|
||||
<a><xsl:attribute name="name"><xsl:value-of select="@name" /></xsl:attribute></a>
|
||||
<dt class="var"><b><xsl:value-of select="@name" /></b> = <xsl:value-of select="@value" /></dt>
|
||||
<dd>
|
||||
<xsl:apply-templates />
|
||||
</dd>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="defn">
|
||||
<a><xsl:attribute name="name"><xsl:value-of select="@name" /></xsl:attribute></a>
|
||||
<dt class="var"><b><xsl:value-of select="@name" /></b></dt>
|
||||
<dd>
|
||||
<xsl:apply-templates />
|
||||
</dd>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
</xsl:stylesheet>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,9 @@
|
||||
init:
|
||||
image black = Solid((0, 0, 0, 255))
|
||||
|
||||
label main_menu:
|
||||
|
||||
$ renpy.renpy.style.write_docs("doc/styles.xml")
|
||||
$ renpy.renpy.style.write_hierarchy("doc/style_heirarchy.xml")
|
||||
$ raise "foo"
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# This program dumps all the text found in the script to the file text.txt.
|
||||
# If on windows, it also tries to show text.txt to the user.
|
||||
|
||||
import codecs
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import glob
|
||||
|
||||
import renpy
|
||||
|
||||
def process_block(block, out):
|
||||
|
||||
for fn, ln, text, child in block:
|
||||
|
||||
if text.startswith("$") or text.startswith("python"):
|
||||
continue
|
||||
|
||||
if text.startswith("init"):
|
||||
continue
|
||||
|
||||
if text.startswith("if") or text.startswith("while"):
|
||||
process_block(child, out)
|
||||
continue
|
||||
|
||||
for m in re.finditer(r'"((?:[^\\"]+|\\.)+)"|' +
|
||||
r"'((?:[^\\']+|\\.)+)'", text):
|
||||
|
||||
s = m.group(1) or m.group(2)
|
||||
|
||||
s = re.sub(r'\s+', ' ', s)
|
||||
s = re.sub(r'\\.', ' ', s)
|
||||
|
||||
s = re.sub(r'\{.*?\}', '', s)
|
||||
|
||||
print >>out, s.encode('utf-8')
|
||||
print >>out
|
||||
|
||||
process_block(child, out)
|
||||
|
||||
|
||||
def process(fn, out):
|
||||
|
||||
block = renpy.parser.group_logical_lines(renpy.parser.list_logical_lines(fn))
|
||||
|
||||
process_block(block, out)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
pattern = "game/*.rpy"
|
||||
|
||||
if len(sys.argv) >= 2:
|
||||
pattern = sys.argv[1]
|
||||
|
||||
files = glob.glob(pattern)
|
||||
files = [ i for i in files if not i.startswith("common/") ]
|
||||
|
||||
out = file("text.txt", "w")
|
||||
out.write(codecs.BOM_UTF8)
|
||||
|
||||
for fn in files:
|
||||
process(fn, out)
|
||||
|
||||
out.close()
|
||||
|
||||
try:
|
||||
os.startfile('text.txt')
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1 @@
|
||||
../../extras/fullscreen.rpy
|
||||
@@ -0,0 +1 @@
|
||||
../../extras/kanamode.rpy
|
||||
@@ -0,0 +1 @@
|
||||
../script.rpy
|
||||
@@ -0,0 +1,102 @@
|
||||
init -10:
|
||||
|
||||
$ config.searchpath.append('moonlight')
|
||||
|
||||
# Set up the size of the screen, and the window title.
|
||||
$ config.screen_width = 800
|
||||
$ config.screen_height = 600
|
||||
$ config.window_title = "A Ren'Py Extras Game"
|
||||
|
||||
# These are positions that can be used inside at clauses. We set
|
||||
# them up here so that they can be used throughout the program.
|
||||
$ left = Position(xpos=0.0, xanchor='left')
|
||||
$ right = Position(xpos=1.0, xanchor='right')
|
||||
$ center = Position()
|
||||
|
||||
# Likewise, we set up some transitions that we can use in with
|
||||
# clauses and statements.
|
||||
$ fade = Fade(.5, 0, .5) # Fade to black and back.
|
||||
$ dissolve = Dissolve(0.5)
|
||||
|
||||
$ wiperight = CropMove(1.0, "wiperight")
|
||||
$ wipeleft = CropMove(1.0, "wipeleft")
|
||||
$ wipeup = CropMove(1.0, "wipeup")
|
||||
$ wipedown = CropMove(1.0, "wipedown")
|
||||
|
||||
$ slideright = CropMove(1.0, "slideright")
|
||||
$ slideleft = CropMove(1.0, "slideleft")
|
||||
$ slideup = CropMove(1.0, "slideup")
|
||||
$ slidedown = CropMove(1.0, "slidedown")
|
||||
|
||||
$ slideawayright = CropMove(1.0, "slideawayright")
|
||||
$ slideawayleft = CropMove(1.0, "slideawayleft")
|
||||
$ slideawayup = CropMove(1.0, "slideawayup")
|
||||
$ slideawaydown = CropMove(1.0, "slideawaydown")
|
||||
|
||||
$ irisout = CropMove(1.0, "irisout")
|
||||
$ irisin = CropMove(1.0, "irisin")
|
||||
|
||||
# Now, we declare the images that are used in the program.
|
||||
|
||||
# Images.
|
||||
image black = Solid((0, 0, 0, 255))
|
||||
image white = Solid((255, 255, 255, 255))
|
||||
image yellow = Solid((255, 255, 200, 255))
|
||||
|
||||
# Opening Sequence.
|
||||
image bigbeach1 = Image("bigbeach1.jpg")
|
||||
image presents = Image("presents.png")
|
||||
|
||||
# Backgrounds.
|
||||
image beach1 = Image("beach1b.jpg")
|
||||
image beach1 mary = Image("beach1c.jpg")
|
||||
image beach1 title = Image("beach1a.jpg")
|
||||
|
||||
image beach2 = Image("beach2.jpg")
|
||||
image beach3 = Image("beach3.jpg")
|
||||
|
||||
image dawn1 = Image("dawn1.jpg")
|
||||
image dawn2 = Image("dawn2.jpg")
|
||||
|
||||
image library = Image("library.jpg")
|
||||
|
||||
# Ending 1.
|
||||
image transfer = Image("transfer.png")
|
||||
image moonpic = Image("moonpic.jpg")
|
||||
image nogirlpic = Image("nogirlpic.jpg")
|
||||
|
||||
|
||||
# Ending 3.
|
||||
image littlemary = Image("littlemary.jpg")
|
||||
|
||||
# Ending 4.
|
||||
image hospital1 = Image("hospital1.jpg")
|
||||
image hospital2 = Image("hospital2.jpg")
|
||||
image hospital3 = Image("hospital3.jpg")
|
||||
image heaven = Image("heaven.jpg")
|
||||
|
||||
# Endings Common
|
||||
image good_ending = Image("ending.jpg")
|
||||
image bad_ending = Image("badending.jpg")
|
||||
|
||||
# Mary.
|
||||
image mary dark confused smiling = Image("mary_dark_confused_smiling.png")
|
||||
image mary dark confused wistful = Image("mary_dark_confused_wistful.png")
|
||||
image mary dark crying = Image("mary_dark_crying.png")
|
||||
image mary dark laughing = Image("mary_dark_laughing.png")
|
||||
image mary dark sad = Image("mary_dark_sad.png")
|
||||
image mary dark smiling = Image("mary_dark_smiling.png")
|
||||
image mary dark vhappy = Image("mary_dark_vhappy.png")
|
||||
image mary dark wistful = Image("mary_dark_wistful.png")
|
||||
|
||||
image mary dawn confused smiling = Image("mary_dawn_confused_smiling.png")
|
||||
image mary dawn confused wistful = Image("mary_dawn_confused_wistful.png")
|
||||
image mary dawn crying = Image("mary_dawn_crying.png")
|
||||
image mary dawn laughing = Image("mary_dawn_laughing.png")
|
||||
image mary dawn sad = Image("mary_dawn_sad.png")
|
||||
image mary dawn smiling = Image("mary_dawn_smiling.png")
|
||||
image mary dawn vhappy = Image("mary_dawn_vhappy.png")
|
||||
image mary dawn wistful = Image("mary_dawn_wistful.png")
|
||||
|
||||
label start:
|
||||
jump example
|
||||
@@ -0,0 +1,16 @@
|
||||
# This file contains a minimal set of style changes needed to have
|
||||
# Ren'Py work with a game that's 640x480 in size.
|
||||
|
||||
init 1:
|
||||
# Change the screen width.
|
||||
$ config.screen_width = 640
|
||||
$ config.screen_height = 480
|
||||
|
||||
# Font sizes.
|
||||
$ style.default.size = 20
|
||||
$ style.button_text.size = 20
|
||||
$ style.file_picker_text.size = 14
|
||||
|
||||
# Change the size of the thumbnails in the file picker.
|
||||
$ library.thumbnail_width = 60
|
||||
$ library.thumbnail_height = 45
|
||||
@@ -0,0 +1,35 @@
|
||||
# This file adds a number of buttons to the lower-right hand corner of
|
||||
# the screen. Three of these buttons jump to the game menu, which
|
||||
# giving quick access to Load, Save, and Prefs. The fourth button
|
||||
# toggles skipping, to make that more convenient.
|
||||
|
||||
init:
|
||||
|
||||
# Give us some space on the right side of the screen.
|
||||
$ style.window.right_margin = 100
|
||||
|
||||
python:
|
||||
|
||||
def toggle_skipping():
|
||||
config.skipping = not config.skipping
|
||||
|
||||
def button_game_menu():
|
||||
|
||||
# to save typing
|
||||
ccinc = renpy.curried_call_in_new_context
|
||||
|
||||
ui.vbox(xpos=0.98, ypos=0.98, xanchor='right', yanchor='bottom')
|
||||
ui.textbutton("Skip", clicked=toggle_skipping)
|
||||
ui.textbutton("Save", clicked=ccinc("_game_menu_save"))
|
||||
ui.textbutton("Load", clicked=ccinc("_game_menu_load"))
|
||||
ui.textbutton("Prefs", clicked=ccinc("_game_menu_preferences"))
|
||||
ui.close()
|
||||
|
||||
|
||||
config.overlay_functions.append(button_game_menu)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# In this file, we have an example of replacing the default menu with
|
||||
# a custom one. This new menu consists of a series of centered
|
||||
# buttons, with dialogue appearing in a narration window.
|
||||
|
||||
# You can try this one out by dropping it right into the game
|
||||
# directory.
|
||||
|
||||
# The code that implements button menus.
|
||||
init:
|
||||
python:
|
||||
style.create('menu_button', 'button')
|
||||
style.create('menu_button_text', 'button_text')
|
||||
|
||||
def menu(menuitems):
|
||||
|
||||
narration = None
|
||||
|
||||
ui.keymousebehavior()
|
||||
|
||||
ui.window(style='menu_window')
|
||||
ui.vbox(xanchor='center', xpos=0.5)
|
||||
|
||||
for label, value in menuitems:
|
||||
if value is None:
|
||||
narration = label
|
||||
continue
|
||||
|
||||
ui.textbutton(label,
|
||||
style="menu_button",
|
||||
text_style="menu_button_text",
|
||||
clicked=ui.returns(value))
|
||||
|
||||
ui.close()
|
||||
|
||||
if narration:
|
||||
narrator(narration, interact=False)
|
||||
|
||||
rv = ui.interact()
|
||||
renpy.checkpoint()
|
||||
return rv
|
||||
|
||||
# Styles to make button menus look good.
|
||||
init 1:
|
||||
python hide:
|
||||
style.menu_window.background = None
|
||||
style.menu_window.yminimum = 0
|
||||
style.menu_window.ypos = 0.40
|
||||
style.menu_window.yanchor = 'center'
|
||||
style.menu_window.xfill = True
|
||||
|
||||
style.menu_button.background = Solid((0, 0, 255, 128))
|
||||
style.menu_button.xfill = True
|
||||
style.menu_button.top_padding = 5
|
||||
style.menu_button.bottom_margin = 5
|
||||
|
||||
style.menu_button_text.xpos = 0.5
|
||||
style.menu_button_text.xanchor = 'center'
|
||||
|
||||
style.menu_button_text.hover_color = (255, 255, 0, 255)
|
||||
style.menu_button_text.idle_color = (255, 255, 255, 255)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# This file contains code to do chinese word-wrapping. It's based
|
||||
# on what I gathered from the information found at:
|
||||
#
|
||||
# http://lemmasoft.renai.us/forums/viewtopic.php?p=9282#9282
|
||||
|
||||
init:
|
||||
|
||||
# You'll probably want to replace this with whatever you consider
|
||||
# to be punctuation. I've used unicode escapes for many of the
|
||||
# fullwidth characters, as I don't know how to type them otherwise.
|
||||
$ chinese_punctuation = u";,.?\uff1b\uff1a\uff0c\u3002\u3001\uff1f\uff01"
|
||||
|
||||
python hide:
|
||||
|
||||
def text_tokenizer(s, style):
|
||||
"""
|
||||
This functions is used to tokenize text. It's called when laying
|
||||
out a Text widget, and is given the string that is the text of the
|
||||
widget, and the style associated with the widget.
|
||||
|
||||
It's expected to yield some number of pairs. In each pair, the
|
||||
first element is the kind of token found, and the second element
|
||||
is the text corresponding to that token. The following token
|
||||
types are defined:
|
||||
|
||||
"newline" -- A newline, which when encountered starts a new line.
|
||||
|
||||
"word" -- A word of text. A line will never be broken inside of
|
||||
a word.
|
||||
|
||||
"space" -- A space. Spaces are always placed on the current line,
|
||||
and will never be placed as the start of a line.
|
||||
|
||||
"tag" -- A text tag. If encountered, the second element should be
|
||||
the name of the tag, without any enclosing braces.
|
||||
"""
|
||||
|
||||
regexp = r"""(?x)
|
||||
(?P<space>\ )
|
||||
| \{(?P<tag>[^{}]+)\}
|
||||
| (?P<untag>\{\{)
|
||||
| (?P<newline>\n)
|
||||
| (?P<word>[^ \n\{][%s]*)
|
||||
""" % chinese_punctuation
|
||||
|
||||
import re
|
||||
|
||||
for m in re.finditer(regexp, s):
|
||||
|
||||
if m.group('space'):
|
||||
yield 'space', m.group('space')
|
||||
elif m.group('word'):
|
||||
yield 'word', m.group('word')
|
||||
elif m.group('tag'):
|
||||
yield 'tag', m.group('tag')
|
||||
elif m.group('untag'):
|
||||
yield 'word', '{'
|
||||
elif m.group('newline'):
|
||||
yield 'newline', m.group('newline')
|
||||
|
||||
config.text_tokenizer = text_tokenizer
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# This function implements a commentary track, which is a special kind of
|
||||
# say statement that only displays if commentary is set to on in the
|
||||
# new preference that this mode creates. Making this unlockable is easy,
|
||||
# but is left as an exercise for the reader.
|
||||
#
|
||||
# Use it by putting in lines like:
|
||||
#
|
||||
# commentary "Hey! This is commentary!"
|
||||
|
||||
init -10:
|
||||
|
||||
python:
|
||||
|
||||
# The style for the commentary window.
|
||||
style.create('commentary_window', 'window', 'The commentary window.')
|
||||
|
||||
style.commentary_window.background = Solid((0, 0, 0, 192))
|
||||
style.commentary_window.ypos = 0.0
|
||||
style.commentary_window.yanchor = 'top'
|
||||
|
||||
|
||||
# Ensure that the commentary attribute exists.
|
||||
if persistent.commentary is None:
|
||||
persistent.commentary = False
|
||||
|
||||
|
||||
commentary = Character(None, window_style='commentary_window',
|
||||
condition = 'persistent.commentary')
|
||||
|
||||
python hide:
|
||||
|
||||
cp = _Preference('Commentary', 'commentary', [
|
||||
('Enabled', True, 'True'),
|
||||
('Disabled', False, 'True')
|
||||
], base=persistent)
|
||||
|
||||
library.preferences['prefs_right'].append(cp)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# This file contains code to do east asian word-wrapping. It's based
|
||||
# on what I gathered from the information found at:
|
||||
#
|
||||
# http://lemmasoft.renai.us/forums/viewtopic.php?p=9282#9282
|
||||
#
|
||||
# It was then further enhanced with the information found at:
|
||||
#
|
||||
# http://zoonek.free.fr/LaTeX/Omega-Japanese/doc.html
|
||||
#
|
||||
# I'd expect it to do a decent job with Chinese and Japanese, but
|
||||
# I have no idea how it does with Korean. If someone wants to clue
|
||||
# me in, I'd be happy to hear more about it.
|
||||
|
||||
init:
|
||||
python hide:
|
||||
|
||||
# These are characters for which line breaking is forbidden before them.
|
||||
# In our algorithm, they try to cling to the back of a word.
|
||||
not_before = ur'\!\"\%\)\,\-\.\:\;\?\]\}\u2010\u2019\u201d\u2030\u2032\u2033\u2103\u2212\u3001\u3002\u3005\u3009\u300b\u300d\u300f\u3011\u3015\u3017\u3041\u3043\u3045\u3047\u3049\u3063\u3083\u3085\u3087\u308e\u309b\u309c\u309d\u309e\u30a1\u30a3\u30a5\u30a7\u30a9\u30c3\u30e3\u30e5\u30e7\u30ee\u30f5\u30f6\u30fc\u30fd\u30fe\uff01\uff02\uff05\uff09\uff09\uff0c\uff0d\uff0e\uff1a\uff1b\uff1f\uff3d\uff5d\uff5d\uff61\uff63\uff9e\uff9f'
|
||||
|
||||
|
||||
# These are characters for which line breaking is forbidden after them.
|
||||
# In our algorithm, they try to cling to the front of a word.
|
||||
not_after = ur'\"\#\$\(\@\[\{\xa2\xa3\xa5\xa7\u2018\u201c\u266f\u3008\u300a\u300c\u300e\u3010\u3012\u3014\u3016\uff03\uff04\uff08\uff08\uff20\uff3b\uff5b\uff5b\uff62\uffe0\uffe1\uffe5'
|
||||
|
||||
|
||||
# These are ranges of characters that are treated as western. (And hence are always grouped
|
||||
# together as a word.
|
||||
western = ur'\'\w\u000a-\u0024f\uff10-\uff19\uff20-\uff2a\uff41-\uff5a'
|
||||
|
||||
regexp = ur"""(?x)
|
||||
(?P<space>[ \u200b])
|
||||
| \{(?P<tag>[^{}]+)\}
|
||||
| (?P<untag>\{\{)
|
||||
| (?P<newline>\n)
|
||||
| (?P<word> [%(not_after)s]*
|
||||
([^ \n\{\u200b%(not_before)s%(not_after)s%(western)s]|[%(western)s]+)
|
||||
[%(not_before)s]*
|
||||
)
|
||||
""" % locals()
|
||||
|
||||
import re
|
||||
|
||||
store._cjk_regexp = re.compile(regexp)
|
||||
|
||||
def text_tokenizer(s, style):
|
||||
"""
|
||||
This functions is used to tokenize text. It's called when laying
|
||||
out a Text widget, and is given the string that is the text of the
|
||||
widget, and the style associated with the widget.
|
||||
|
||||
It's expected to yield some number of pairs. In each pair, the
|
||||
first element is the kind of token found, and the second element
|
||||
is the text corresponding to that token. The following token
|
||||
types are defined:
|
||||
|
||||
"newline" -- A newline, which when encountered starts a new line.
|
||||
|
||||
"word" -- A word of text. A line will never be broken inside of
|
||||
a word.
|
||||
|
||||
"space" -- A space. Spaces are always placed on the current line,
|
||||
and will never be placed as the start of a line.
|
||||
|
||||
"tag" -- A text tag. If encountered, the second element should be
|
||||
the name of the tag, without any enclosing braces.
|
||||
"""
|
||||
|
||||
for m in store._cjk_regexp.finditer(s):
|
||||
|
||||
if m.group('space'):
|
||||
yield 'space', m.group('space')
|
||||
elif m.group('word'):
|
||||
yield 'word', m.group('word')
|
||||
elif m.group('tag'):
|
||||
yield 'tag', m.group('tag')
|
||||
elif m.group('untag'):
|
||||
yield 'word', '{'
|
||||
elif m.group('newline'):
|
||||
yield 'newline', m.group('newline')
|
||||
|
||||
config.text_tokenizer = text_tokenizer
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# This file contains code to automatically switch the game into
|
||||
# fullscreen mode.
|
||||
|
||||
# Please note: Fullscreen mode is broken on some platforms (mostly
|
||||
# virtual windows machines like vmware and virtual PC). So having
|
||||
# this code automatically execute could make your game hard to use
|
||||
# on those platforms.
|
||||
#
|
||||
# The way we work around this is to tell users of these platforms
|
||||
# to press 'f' to toggle fullscreen mode. If the game's running in
|
||||
# windowed mode, the problems don't seem to appear.
|
||||
#
|
||||
# You should probably put words to that effect in some sort of
|
||||
# README file.
|
||||
|
||||
# The first time this code runs, the game switches into fullscreen.
|
||||
# After that, it respects the user's preference.
|
||||
|
||||
init:
|
||||
python:
|
||||
if not persistent.set_fullscreen:
|
||||
persistent.set_fullscreen = True
|
||||
_preferences.fullscreen = True
|
||||
@@ -0,0 +1,262 @@
|
||||
# This extra implements an image gallery, complete with automatic
|
||||
# unlocking of the images that have been shown to the user. The images
|
||||
# are divided into pages, with a fixed number of images on each page.
|
||||
|
||||
# Right now, this is configured to used the images found in the demo.
|
||||
# The images are repeated quite a bit, so that we can get four pages
|
||||
# of them. You probably wouldn't do that if you were using this in a
|
||||
# real game.
|
||||
|
||||
# To see this in action, drop it into the game directory of the
|
||||
# demo.
|
||||
|
||||
# Configuration.
|
||||
init:
|
||||
|
||||
python hide:
|
||||
|
||||
|
||||
# The number of columns and rows of images to show in the
|
||||
# gallery.
|
||||
store.gallery_cols = 3
|
||||
store.gallery_rows = 4
|
||||
|
||||
# The size that each image should be scaled to, and that
|
||||
# thumbnails should be.
|
||||
store.gallery_width = 160
|
||||
store.gallery_height = 120
|
||||
|
||||
# The contents of each of the page. Each of these is a list of
|
||||
# tuples with the first element of the tuple being the image
|
||||
# filename and the second element being the name of the image
|
||||
# that will unlock the image with this filename. (That is,
|
||||
# the name that is used in show or scene statements, as a
|
||||
# string.)
|
||||
#
|
||||
# When displaying an image as a thumbnail, this code first
|
||||
# looks for the file thumbnail_<filename>. If that file
|
||||
# exists, it should be a gallery_width x gallery_height
|
||||
# thumbnail. Otherwise, a thumbnail is automatically
|
||||
# generated, but it may screw up the aspect ratio of the
|
||||
# image.
|
||||
#
|
||||
# You probably want to create thumbnails for most images, to
|
||||
# limit memory consumption.
|
||||
page1 = [
|
||||
( "whitehouse.jpg", "whitehouse" ),
|
||||
( "washington.jpg", "washington" ),
|
||||
( "carillon.jpg", "carillon" ),
|
||||
( "whitehouse.jpg", "whitehouse" ),
|
||||
( "washington.jpg", "washington" ),
|
||||
( "carillon.jpg", "carillon" ),
|
||||
( "whitehouse.jpg", "whitehouse" ),
|
||||
( "washington.jpg", "washington" ),
|
||||
( "carillon.jpg", "carillon" ),
|
||||
( "whitehouse.jpg", "whitehouse" ),
|
||||
( "washington.jpg", "washington" ),
|
||||
( "carillon.jpg", "carillon" ),
|
||||
]
|
||||
|
||||
page2 = [
|
||||
( "whitehouse.jpg", "whitehouse" ),
|
||||
( "washington.jpg", "washington" ),
|
||||
( "carillon.jpg", "carillon" ),
|
||||
( "whitehouse.jpg", "whitehouse" ),
|
||||
( "washington.jpg", "washington" ),
|
||||
( "carillon.jpg", "carillon" ),
|
||||
( "whitehouse.jpg", "whitehouse" ),
|
||||
( "washington.jpg", "washington" ),
|
||||
( "carillon.jpg", "carillon" ),
|
||||
( "whitehouse.jpg", "whitehouse" ),
|
||||
( "washington.jpg", "washington" ),
|
||||
( "carillon.jpg", "carillon" ),
|
||||
]
|
||||
|
||||
page3 = [
|
||||
( "whitehouse.jpg", "whitehouse" ),
|
||||
( "washington.jpg", "washington" ),
|
||||
]
|
||||
|
||||
page4 = [
|
||||
( "9a_happy.png", "eileen happy" ),
|
||||
( "9a_vhappy.png", "eileen vhappy" ),
|
||||
( "9a_concerned.png", "eileen concerned" ),
|
||||
]
|
||||
|
||||
# This is the actual list of gallery pages. It's a list
|
||||
# of tuples, with the first element being the name of the
|
||||
# page, the second being the contents of the page (one of
|
||||
# the lists created above), and the third image being the
|
||||
# image used as the background of the page.
|
||||
store.gallery_pages = [
|
||||
("Backgrounds 1", page1, "washington.jpg"),
|
||||
("Backgrounds 2", page2, "whitehouse.jpg"),
|
||||
("Backgrounds 3", page3, "carillon.jpg"),
|
||||
("Character Art", page4, "washington.jpg"),
|
||||
]
|
||||
|
||||
# A window containing the gallery page buttons.
|
||||
style.create('gallery_pages', 'default')
|
||||
style.gallery_pages.xpos = 0.99
|
||||
style.gallery_pages.xanchor='right'
|
||||
style.gallery_pages.ypos = 0.02
|
||||
style.gallery_pages.yanchor = 'top'
|
||||
|
||||
# A button that links to a gallery page.
|
||||
style.create('gallery_page_button', 'button')
|
||||
style.create('gallery_page_button_text', 'button_text')
|
||||
|
||||
# A button that returns us to from whence we came.
|
||||
style.create('gallery_return_button', 'button')
|
||||
style.create('gallery_return_button_text', 'button_text')
|
||||
|
||||
style.gallery_return_button.xpos = 0.99
|
||||
style.gallery_return_button.xanchor='right'
|
||||
style.gallery_return_button.ypos = 0.98
|
||||
style.gallery_return_button.yanchor = 'bottom'
|
||||
|
||||
|
||||
# The grid containing the gallery image buttons.
|
||||
style.create('gallery_grid', 'default')
|
||||
|
||||
# The style of the buttons in the gallery.
|
||||
style.create('gallery_button', 'default')
|
||||
|
||||
# Right now, the backgrounds are all solids, but in a more
|
||||
# professional version, the insensitive background would
|
||||
# probably be a placeholder image that indicates that a
|
||||
# picture has yet to be unlocked.
|
||||
style.gallery_button.insensitive_background = Solid((192, 192, 192, 255))
|
||||
style.gallery_button.idle_background = Solid((255, 255, 255, 255))
|
||||
style.gallery_button.hover_background = Solid((255, 255, 192, 255))
|
||||
|
||||
style.gallery_button.left_margin = 5
|
||||
style.gallery_button.right_margin = 5
|
||||
style.gallery_button.top_margin = 5
|
||||
style.gallery_button.bottom_margin = 5
|
||||
|
||||
style.gallery_button.left_padding = 5
|
||||
style.gallery_button.right_padding = 5
|
||||
style.gallery_button.top_padding = 5
|
||||
style.gallery_button.bottom_padding = 5
|
||||
|
||||
# The style of the images in the buttons in the gallery.
|
||||
style.create('gallery_button_image', 'default')
|
||||
|
||||
# The style of the images that are being shown to the user.
|
||||
style.create('gallery_image', 'image_placement')
|
||||
|
||||
# The transition used when switching gallery pages.
|
||||
store.gallery_transition = Dissolve(0.5)
|
||||
|
||||
python:
|
||||
|
||||
# The function that actually manages the display of the image
|
||||
# gallery.
|
||||
def gallery():
|
||||
|
||||
page = 0
|
||||
|
||||
while True:
|
||||
|
||||
images = gallery_pages[page][1]
|
||||
ui.image(gallery_pages[page][2])
|
||||
|
||||
# Show the names of the various gallery pages.
|
||||
|
||||
ui.window(style='gallery_pages')
|
||||
ui.vbox(focus="gallery_pages")
|
||||
|
||||
for i in range(0, len(gallery_pages)):
|
||||
if i == page:
|
||||
clicked = None
|
||||
else:
|
||||
clicked = ui.returns(("page", i))
|
||||
|
||||
ui.textbutton(gallery_pages[i][0],
|
||||
style='gallery_page_button',
|
||||
text_style='gallery_page_button_text',
|
||||
clicked=clicked)
|
||||
ui.close()
|
||||
|
||||
# Show the return button.
|
||||
ui.textbutton('Return',
|
||||
style='gallery_return_button',
|
||||
text_style='gallery_return_button_text',
|
||||
clicked=ui.returns(("return", None)))
|
||||
|
||||
# Show the grid for this page.
|
||||
ui.grid(gallery_cols, gallery_rows, style='gallery_grid')
|
||||
|
||||
# For each grid cell.
|
||||
for i in range(0, gallery_cols * gallery_rows):
|
||||
|
||||
# Fill empty space with nulls.
|
||||
if i >= len(images):
|
||||
ui.null()
|
||||
continue
|
||||
|
||||
# Otherwise, get the filename and spec and see if
|
||||
# we've unlocked it.
|
||||
filename, spec = images[i]
|
||||
|
||||
if spec:
|
||||
spec = spec.split()
|
||||
|
||||
if spec and tuple(spec) not in persistent._seen_images:
|
||||
filename = None
|
||||
clicked = None
|
||||
else:
|
||||
clicked = ui.returns(('show', filename))
|
||||
|
||||
# Create the button, containing the appropriate
|
||||
# image or a null if we haven't unlocked it yet.
|
||||
|
||||
ui.button(style='gallery_button', clicked=clicked)
|
||||
|
||||
if not filename:
|
||||
ui.null(width=gallery_width, height=gallery_height)
|
||||
else:
|
||||
|
||||
if renpy.loadable("thumbnail_" + filename):
|
||||
ui.image("thumbnail_" + filename,
|
||||
style="gallery_button_image")
|
||||
else:
|
||||
ui.add(im.Scale(filename,
|
||||
gallery_width,
|
||||
gallery_height))
|
||||
|
||||
ui.close()
|
||||
|
||||
# Interact with the user.
|
||||
renpy.transition(gallery_transition)
|
||||
cmd, arg = ui.interact(suppress_overlay=True, suppress_underlay=True)
|
||||
|
||||
# Process the user's commands.
|
||||
if cmd == "show":
|
||||
ui.add(Solid((0, 0, 0, 255)))
|
||||
ui.image(arg)
|
||||
ui.saybehavior()
|
||||
renpy.transition(gallery_transition)
|
||||
ui.interact(suppress_overlay=True, suppress_underlay=True)
|
||||
|
||||
if cmd == "page":
|
||||
page = arg
|
||||
|
||||
if cmd == "return":
|
||||
renpy.transition(gallery_transition)
|
||||
return
|
||||
|
||||
|
||||
library.main_menu.insert(2, ("CG Gallery", ui.jumps("gallery"), 'True'))
|
||||
|
||||
|
||||
label gallery:
|
||||
|
||||
$ gallery()
|
||||
|
||||
jump _main_menu
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
# This is an implementation of Kana mode, a mode in which multiple
|
||||
# lines of dialogue are shown at once, in a fullscreen window. A
|
||||
# single line of dialogue is placed onto the screen for each mouse
|
||||
# click. Calling the clear function clears the screen, ensuring that
|
||||
# the next line will appear at the top of the sceen.
|
||||
#
|
||||
# An example of using Kana mode follows the code that implements it.
|
||||
|
||||
|
||||
# This init block contains all of the code needed to implement
|
||||
# Kana mode. You probably don't want to change this... unless you
|
||||
# do. But all of the customization is found below.
|
||||
init -100:
|
||||
python:
|
||||
|
||||
# This is a list of KanaCharacter, what tuples.
|
||||
kana_display_list = [ ]
|
||||
|
||||
# Spacings used.
|
||||
kana_vspacing = 0
|
||||
kana_hspacing = 0
|
||||
|
||||
def clear(arg=None):
|
||||
global kana_display_list
|
||||
kana_display_list = [ ]
|
||||
|
||||
def kana_show():
|
||||
|
||||
if not kana_display_list:
|
||||
return
|
||||
|
||||
ui.window(style='say_window')
|
||||
ui.vbox(kana_vspacing)
|
||||
|
||||
for i, (char, what) in enumerate(kana_display_list):
|
||||
text = char.show(what, i == len(kana_display_list) - 1)
|
||||
|
||||
ui.close()
|
||||
|
||||
# Magic to support auto-forward mode.
|
||||
b = ui.saybehavior()
|
||||
b.set_afm_length(text.get_simple_length())
|
||||
|
||||
ui.interact()
|
||||
renpy.checkpoint()
|
||||
|
||||
|
||||
# Characters and the narrator should be instances of this
|
||||
# object.
|
||||
class KanaCharacter(object):
|
||||
|
||||
def __init__(self, who,
|
||||
what_prefix='"',
|
||||
what_suffix='"',
|
||||
who_style='say_label',
|
||||
what_style='say_dialogue',
|
||||
**properties):
|
||||
|
||||
self.who = who
|
||||
self.what_prefix = what_prefix
|
||||
self.what_suffix = what_suffix
|
||||
self.who_style = who_style
|
||||
self.what_style = what_style
|
||||
self.properties = properties
|
||||
|
||||
def __call__(self, what):
|
||||
kana_display_list.append((self, what))
|
||||
kana_show()
|
||||
|
||||
def show(self, what, slow):
|
||||
ui.hbox(kana_hspacing)
|
||||
ui.text(self.who, style=self.who_style,
|
||||
**self.properties)
|
||||
|
||||
rv = ui.text(self.what_prefix + what + self.what_suffix,
|
||||
style=self.what_style, slow=slow)
|
||||
|
||||
ui.close()
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
# This section updates the styles that are used by the Kana mode stuff
|
||||
# so that they work well with Kana mode. The user may want to change
|
||||
# these so that they look more appropriate.
|
||||
init:
|
||||
|
||||
# The space between lines of dialogue.
|
||||
$ kana_vspacing = 10
|
||||
$ kana_hspacing = 10
|
||||
|
||||
$ style.say_window.background = Solid((0, 0, 0, 96))
|
||||
$ style.say_window.xfill = True
|
||||
$ style.say_window.yfill = True
|
||||
$ style.say_window.xmargin = 0
|
||||
$ style.say_window.ymargin = 0
|
||||
$ style.say_window.xpadding = 20
|
||||
$ style.say_window.ypadding = 20
|
||||
|
||||
$ style.say_label.minwidth = 100
|
||||
$ style.say_label.textalign = 1.0
|
||||
|
||||
# This just makes it look nicer.
|
||||
$ style.say_dialogue.rest_indent = 9
|
||||
|
||||
# Adjust the menu to match.
|
||||
$ style.menu_window.background = Solid((0, 0, 0, 96))
|
||||
$ style.menu_window.xfill = True
|
||||
$ style.menu_window.yfill = True
|
||||
$ style.menu_window.xmargin = 0
|
||||
$ style.menu_window.ymargin = 0
|
||||
$ style.menu_window.xpadding = 20
|
||||
$ style.menu_window.ypadding = 20
|
||||
|
||||
$ style.menu.xpos = 110
|
||||
|
||||
# And now, the example. This example can't be simply run, but instead
|
||||
# should serve as a guide to how to get Kana mode working in your own
|
||||
# game.
|
||||
|
||||
# In the init block, declare the characters as KanaCharacters. We also
|
||||
# want to declare the narrator as a special KanaCharacter.
|
||||
init:
|
||||
$ p = KanaCharacter("")
|
||||
$ g = KanaCharacter("Girl:", color=(255, 128, 128, 255))
|
||||
$ narrator = KanaCharacter("",
|
||||
what_prefix='',
|
||||
what_suffix='',
|
||||
what_style='say_thought')
|
||||
|
||||
|
||||
# Now, the actual script of the example. (An excerpt from Moonlight
|
||||
# Walks.) Notice how we place calls to clear in places where the
|
||||
# we want the screen to be cleared.
|
||||
|
||||
# This text is here to serve as an example, and shouldn't be used
|
||||
# in your game.
|
||||
label example:
|
||||
|
||||
show beach2
|
||||
show mary dark wistful
|
||||
|
||||
$ clear()
|
||||
|
||||
p "What can you tell me about your parents?"
|
||||
|
||||
g "Papa and Mama both came across the ocean as settlers when they
|
||||
were just children."
|
||||
|
||||
g "Papa fought in the war. When it was over, he married Mama, and
|
||||
they used his pension to move here from the mainland, and to
|
||||
build us a house."
|
||||
|
||||
g "Together, they farmed the land, and eventually they had
|
||||
children."
|
||||
|
||||
g "I had an older sister and a younger sister. I was the middle
|
||||
child."
|
||||
|
||||
$ clear()
|
||||
|
||||
show mary dark sad
|
||||
|
||||
"She paused for a second to collect her thoughts before
|
||||
continuing. This part was taking a strain on her."
|
||||
|
||||
g "When I was ten, an epidemic hit the island."
|
||||
|
||||
g "We came down with it, and so did all of the other families."
|
||||
|
||||
g "My family was too sick to move, but our neighbors, the Millers,
|
||||
sent some of their boys to get help from the mainland."
|
||||
|
||||
"I remember thinking that Miller was the last name of my aunt and
|
||||
uncle, and wondering if they could be related to me."
|
||||
|
||||
g "I don't know what happened to them, but I never heard from them
|
||||
again."
|
||||
|
||||
show mary dark crying
|
||||
|
||||
g "It lasted a week, and then it was over. My sisters... my
|
||||
parents... they all..."
|
||||
|
||||
g "Now I'm the only one left."
|
||||
|
||||
$ clear()
|
||||
|
||||
p "I'm sorry."
|
||||
|
||||
"I didn't know what else to say to a girl that had lost her
|
||||
family, and was obviously broken up about it."
|
||||
|
||||
show mary dark sad
|
||||
|
||||
"She nodded in response, wiped her tears, and we once again
|
||||
started walking in silence."
|
||||
|
||||
menu menu_1:
|
||||
"Show me the example again.":
|
||||
jump example
|
||||
|
||||
"I'm done. Let me go.":
|
||||
return
|
||||
@@ -0,0 +1,27 @@
|
||||
# This file gives an example of how the various entries on the main
|
||||
# and game menus can be repositioned on the screen. Unlike the other
|
||||
# extras, we don't intend this to be used as-is, but instead to be
|
||||
# copied and changed to change the positions of buttons on the screen.
|
||||
|
||||
# Buttons should not overlap, and certain configurations may make
|
||||
# buttons inaccessible with keyboard navigation.
|
||||
|
||||
init:
|
||||
|
||||
python hide:
|
||||
|
||||
library.main_menu_positions = {
|
||||
'Start Game' : dict(xpos=400, ypos=400, xanchor='left', yanchor='top'),
|
||||
'Continue Game' : dict(xpos=450, ypos=430, xanchor='left', yanchor='top'),
|
||||
'Preferences' : dict(xpos=500, ypos=460, xanchor='left', yanchor='top'),
|
||||
'Quit Game' : dict(xpos=550, ypos=490, xanchor='left', yanchor='top'),
|
||||
}
|
||||
|
||||
library.game_menu_positions = {
|
||||
'Return' : dict(xpos=400, ypos=400, xanchor='left', yanchor='top'),
|
||||
'Preferences' : dict(xpos=450, ypos=430, xanchor='left', yanchor='top'),
|
||||
'Save Game' : dict(xpos=500, ypos=460, xanchor='left', yanchor='top'),
|
||||
'Load Game' : dict(xpos=550, ypos=490, xanchor='left', yanchor='top'),
|
||||
'Main Menu' : dict(xpos=600, ypos=520, xanchor='left', yanchor='top'),
|
||||
'Quit' : dict(xpos=650, ypos=550, xanchor='left', yanchor='top'),
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
# This file replaces most of the game menu navigation with overlay
|
||||
# buttons that jump directly to various parts of the game menu.
|
||||
# Right clicking hides and shows the buttons, rather than calling
|
||||
# up the game menu directly.
|
||||
|
||||
init:
|
||||
python hide:
|
||||
|
||||
# overlay_menu is an object storing information about the
|
||||
# overlay menu state.
|
||||
store.overlay_menu = object()
|
||||
overlay_menu.shown = False
|
||||
|
||||
# overlay_menu is also a new layer, containing the overlay
|
||||
# menu.
|
||||
config.layers.append("overlay_menu")
|
||||
config.overlay_layers.append("overlay_menu")
|
||||
|
||||
|
||||
# This function actually draws the overlay menu.
|
||||
def overlay_menu_func():
|
||||
if overlay_menu.shown:
|
||||
|
||||
ui.layer("overlay_menu")
|
||||
|
||||
ui.vbox(xpos=1.0, xanchor="right", ypos=0.75, yanchor="bottom")
|
||||
|
||||
def button(label, target):
|
||||
ui.textbutton(label, clicked=renpy.curried_call_in_new_context(target))
|
||||
|
||||
button("Load Game", "_game_menu_load")
|
||||
button("Save Game", "_game_menu_save")
|
||||
button("Preferences", "_game_menu_preferences")
|
||||
|
||||
ui.close()
|
||||
ui.close()
|
||||
|
||||
|
||||
config.overlay_functions.append(overlay_menu_func)
|
||||
|
||||
|
||||
# This function toggles the visibility of the overlay menu.
|
||||
def overlay_menu_toggle():
|
||||
shown = not overlay_menu.shown
|
||||
overlay_menu.shown = shown
|
||||
|
||||
# How long should the transitions take?
|
||||
trans_delay = 0.5
|
||||
|
||||
# These transitions assume that the menu is placed on the
|
||||
# right side of the screen. This indicator gives the
|
||||
# fraction of the screen that participates in transitions.
|
||||
|
||||
trans_frac = 0.75
|
||||
|
||||
if shown:
|
||||
|
||||
trans = CropMove(trans_delay,
|
||||
"custom",
|
||||
startcrop=(trans_frac, 0.0, 0.0, 1.0),
|
||||
startpos=(1.0, 0.0),
|
||||
endcrop=(trans_frac, 0.0, 1.0-trans_frac, 1.0),
|
||||
endpos=(trans_frac, 0.0),
|
||||
topnew=True)
|
||||
|
||||
renpy.transition(trans, 'overlay_menu')
|
||||
|
||||
else:
|
||||
|
||||
trans = CropMove(trans_delay,
|
||||
"custom",
|
||||
endcrop=(trans_frac, 0.0, 0.0, 1.0),
|
||||
endpos=(1.0, 0.0),
|
||||
startcrop=(trans_frac, 0.0, 1.0-trans_frac, 1.0),
|
||||
startpos=(trans_frac, 0.0),
|
||||
topnew=False)
|
||||
|
||||
renpy.transition(trans, 'overlay_menu')
|
||||
|
||||
|
||||
renpy.restart_interaction()
|
||||
|
||||
|
||||
# Add a new underlay that handles the overlay menu toggle.
|
||||
config.underlay.append(renpy.Keymap(game_menu = overlay_menu_toggle))
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# This file implements quick save and load functionality. The
|
||||
# quicksave functionality adds a button to the overlay that is
|
||||
# displayed over the game. When the button is clicked, the game is
|
||||
# saved in a special quicksave slot. It also adds a quick load button
|
||||
# to the main menu. When that button is clicked, the quicksave game is
|
||||
# loaded.
|
||||
|
||||
# This is called when the quick save button is clicked.
|
||||
label _quick_save:
|
||||
call _enter_menu_without_scene from __call__enter_menu_4
|
||||
|
||||
# Save the game.
|
||||
$ renpy.save('quicksave', _('Quick Save'))
|
||||
|
||||
# Tell the user that we saved the game.
|
||||
$ ui.add(Solid((0, 0, 0, 128)))
|
||||
$ ui.text(_('The game has been saved using quick save.'),
|
||||
xpos=0.5, xanchor='center', ypos=0.5, yanchor='center')
|
||||
|
||||
$ ui.saybehavior()
|
||||
$ ui.interact(suppress_overlay=True, suppress_underlay=True)
|
||||
|
||||
return
|
||||
|
||||
# This is called when the quick load button is clicked, to load the
|
||||
# game.
|
||||
label _quick_load:
|
||||
$ renpy.load('quicksave')
|
||||
return
|
||||
|
||||
init -1:
|
||||
|
||||
python hide:
|
||||
|
||||
# Add the quick save button in as an overlay function.
|
||||
def quick_save_button():
|
||||
ui.textbutton(_("Quick Save"),
|
||||
xpos=0.98, ypos=0.02,
|
||||
xanchor='right', yanchor='top',
|
||||
clicked=renpy.curried_call_in_new_context('_quick_save'))
|
||||
|
||||
config.overlay_functions.append(quick_save_button)
|
||||
|
||||
# Add the quick load function to the main menu.
|
||||
library.main_menu.insert(1, ('Quick Load',
|
||||
ui.jumps("_quick_load"),
|
||||
'renpy.can_load("quicksave")'))
|
||||
@@ -0,0 +1,229 @@
|
||||
# This is the annoying readback mode. I implemented this because
|
||||
# people asked for it, not because I like it. I'd prefer you use
|
||||
# rollback, which is better in every way.
|
||||
|
||||
# To use readback, drop this file into your game directory. Readback
|
||||
# will take over when rollback no longer works, so you'll also need to
|
||||
# either reduce config.hard_rollback_limit or set
|
||||
# config.rollback_enabled to false.
|
||||
|
||||
init -100:
|
||||
$ config.hard_rollback_limit = 1
|
||||
|
||||
# Readback works by replacing the default Character and Menu objects
|
||||
# with ones that record what is said in a readback buffer. The user
|
||||
# can then go back, and what has been said will be shown to them
|
||||
# again. Images and the like will not change... if you want that, use
|
||||
# rollback. Heck, use rollback anyway. :-P
|
||||
|
||||
# The number of lines of readback can be changed by assigning an
|
||||
# integer to the readback_limit variable. But leaving it None will
|
||||
# leave the entire game in the buffer. As this is actually fairly
|
||||
# memory-efficent, you should probably leave it as None.
|
||||
|
||||
# Readback uses two styles, readback_dialogue and
|
||||
# readback_thought. Change them to change the color and look of
|
||||
# read-back text.
|
||||
|
||||
# If you want to add your own message, as narration, to the readback
|
||||
# buffer, you can do it by calling the readback function or having
|
||||
# the readback character say it:
|
||||
|
||||
# $ readback('A message for readbackers only.')
|
||||
# readback "A similar message."
|
||||
|
||||
# Note: When using Readback, you can no longer use a string for a
|
||||
# character's name. All dialogue must be routed through Character
|
||||
# objects, if it is to show up in the readback buffer. This limitation
|
||||
# will be fixed in a future version of Ren'Py.
|
||||
|
||||
init -100:
|
||||
|
||||
python:
|
||||
|
||||
# The limit of the number of readbacks to keep. None means no limit.
|
||||
readback_limit = None
|
||||
|
||||
# Set this to true to print out the contents of the readback
|
||||
# buffer when it is saved.
|
||||
readback_debug = False
|
||||
|
||||
# Readback styles.
|
||||
style.create('readback_dialogue', 'say_dialogue', '')
|
||||
style.create('readback_thought', 'say_thought', '')
|
||||
|
||||
style.readback_dialogue.color = (255, 128, 128, 255)
|
||||
style.readback_thought.color = (255, 128, 128, 255)
|
||||
|
||||
|
||||
# No user-servicable parts below this line. ######################
|
||||
|
||||
# The readback buffer is a doubly-linked list of readback
|
||||
# objects.
|
||||
class Readback(object):
|
||||
def __init__(self, obj, args):
|
||||
self.older = None
|
||||
self.newer = None
|
||||
self.obj = obj
|
||||
self.args = args
|
||||
|
||||
def show(self):
|
||||
self.obj.readback(*self.args)
|
||||
|
||||
|
||||
readback_oldest = None
|
||||
readback_newest = None
|
||||
readback_count = 0
|
||||
|
||||
|
||||
# This saves a readback entry to the readback buffer.
|
||||
def readback_save(obj, *args):
|
||||
|
||||
store.readback_count += 1
|
||||
|
||||
if readback_limit and readback_count > readback_limit:
|
||||
store.readback_count -= 1
|
||||
readback_oldest.newer.older = None
|
||||
store.readback_oldest = readback_oldest.newer
|
||||
|
||||
rb = Readback(obj, args)
|
||||
|
||||
|
||||
if readback_newest:
|
||||
readback_newest.newer = rb
|
||||
else:
|
||||
store.readback_oldest = rb
|
||||
|
||||
rb.older = readback_newest
|
||||
|
||||
store.readback_newest = rb
|
||||
|
||||
|
||||
if readback_debug:
|
||||
|
||||
print "---- Readback Buffer ----"
|
||||
|
||||
rb = readback_oldest
|
||||
while rb:
|
||||
print rb.obj, rb.args
|
||||
rb = rb.newer
|
||||
|
||||
|
||||
# The rest of this file is replacing the default objects and
|
||||
# functions with versions that save things in the readback
|
||||
# buffer.
|
||||
|
||||
# Save the old character object.
|
||||
readback_OldCharacter = Character
|
||||
readback_oldmenu = menu
|
||||
|
||||
class Character(readback_OldCharacter):
|
||||
|
||||
def __init__(self, who,
|
||||
readback_style='readback_dialogue',
|
||||
**kwargs):
|
||||
|
||||
readback_OldCharacter.__init__(self, who, **kwargs)
|
||||
self.readback_style = readback_style
|
||||
|
||||
def __call__(self, what, **kwargs):
|
||||
|
||||
if not self.check_condition():
|
||||
return
|
||||
|
||||
readback_OldCharacter.__call__(self, what, **kwargs)
|
||||
|
||||
def store_readback(self, who, what):
|
||||
readback_save(self, who, what)
|
||||
|
||||
def readback(self, who, what):
|
||||
self.function(who, what,
|
||||
who_style=self.who_style,
|
||||
what_style=self.readback_style,
|
||||
window_style=self.window_style,
|
||||
interact=False,
|
||||
all_at_once=True,
|
||||
**self.properties)
|
||||
|
||||
class Sayer(object):
|
||||
def __call__(self, who, what):
|
||||
renpy.display_say(who, what)
|
||||
readback_save(self, who, what)
|
||||
|
||||
def readback(self, who, what):
|
||||
renpy.display_say(who, what,
|
||||
what_style='readback_dialogue',
|
||||
all_at_once=True,
|
||||
interact=False)
|
||||
|
||||
|
||||
narrator = Character(None, what_style='say_thought')
|
||||
say = Sayer()
|
||||
|
||||
def readback(what):
|
||||
narrator.store_readback(None, what)
|
||||
|
||||
def menu(menuitems):
|
||||
rv = readback_oldmenu(menuitems)
|
||||
|
||||
text = '\n'.join([ l for l, v in menuitems
|
||||
if v is None or v == rv ])
|
||||
|
||||
readback(text)
|
||||
|
||||
return rv
|
||||
|
||||
# This stuff is involved in entering the readback mode.
|
||||
|
||||
def readback_mode():
|
||||
# Try rollback, first.
|
||||
renpy.rollback()
|
||||
|
||||
# If we made it here, we're into readback mode. So let's
|
||||
# go there now.
|
||||
|
||||
renpy.call_in_new_context("readback")
|
||||
|
||||
# Add in the readback function.
|
||||
config.underlay.append(renpy.Keymap(rollback=readback_mode))
|
||||
|
||||
|
||||
# This label is called in a new context, when the user succesfully
|
||||
# enters Readback mode.
|
||||
label readback:
|
||||
|
||||
# If we have an empty readback buffer, go home.
|
||||
if not readback_newest:
|
||||
return
|
||||
|
||||
python hide:
|
||||
|
||||
rb = readback_newest
|
||||
|
||||
while True:
|
||||
|
||||
rb.show()
|
||||
|
||||
ui.add(renpy.Keymap(rollback=lambda : "older",
|
||||
rollforward=lambda : "newer",
|
||||
dismiss=lambda : "dismiss",
|
||||
))
|
||||
|
||||
res = ui.interact()
|
||||
|
||||
if res == "newer":
|
||||
rb = rb.newer
|
||||
if not rb:
|
||||
break
|
||||
|
||||
elif res == "older":
|
||||
if rb.older:
|
||||
rb = rb.older
|
||||
|
||||
elif res == "dismiss":
|
||||
break
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
# This extra demonstrates the two window say mode. In this mode, the
|
||||
# character's name is placed in a different window than the line said
|
||||
# by that character.
|
||||
#
|
||||
# You can use this by including this file in your game directory. You
|
||||
# then need to change the definitions of Character and
|
||||
# DynamicCharacter object to set function=two_window_say. For example:
|
||||
#
|
||||
# $ e = Character('Eileen', color=(200, 255, 200, 255),
|
||||
# function=two_window_say)
|
||||
#
|
||||
# Once this is done, everything that is said by that character will be
|
||||
# divided across two windows.
|
||||
#
|
||||
# You'll probably also want to customize the styles given below,
|
||||
# especially by setting style.who_window.background to something
|
||||
# a little more attractive.
|
||||
|
||||
init -100:
|
||||
|
||||
python:
|
||||
|
||||
style.create('two_window_say_position', 'default',
|
||||
'(position) Used to get the position of the two windows in two window say mode.')
|
||||
|
||||
style.two_window_say_position.ypos = 1.0
|
||||
style.two_window_say_position.yanchor = 'bottom'
|
||||
|
||||
style.create('who_window', 'default',
|
||||
'(window) The style used for the window containing the who label when a Character uses the two_window_say function.')
|
||||
|
||||
style.who_window.background = Solid((0, 0, 255, 128))
|
||||
style.who_window.xminimum = 150
|
||||
style.who_window.xmargin = 10
|
||||
style.who_window.xpadding = 10
|
||||
style.who_window.ymargin = 5
|
||||
style.who_window.ypadding = 5
|
||||
|
||||
|
||||
def two_window_say(who, what,
|
||||
who_style='say_label',
|
||||
what_style='say_dialogue',
|
||||
window_style='say_window',
|
||||
who_window_style='who_window',
|
||||
who_prefix='',
|
||||
who_suffix=': ',
|
||||
what_prefix='',
|
||||
what_suffix='',
|
||||
interact=True,
|
||||
slow=True,
|
||||
**properties):
|
||||
|
||||
if interact:
|
||||
ui.saybehavior()
|
||||
|
||||
if who is not None:
|
||||
who = who_prefix + who + who_suffix
|
||||
|
||||
what = what_prefix + what + what_suffix
|
||||
|
||||
ui.vbox(style='two_window_say_position')
|
||||
|
||||
if who is not None:
|
||||
ui.window(style=who_window_style)
|
||||
ui.text(who, style=who_style, **properties)
|
||||
|
||||
ui.window(style=window_style)
|
||||
ui.text(what, style=what_style, slow=slow)
|
||||
|
||||
ui.close()
|
||||
|
||||
if interact:
|
||||
ui.interact()
|
||||
renpy.checkpoint()
|
||||
@@ -0,0 +1,72 @@
|
||||
# This extra contains a basic implementation of voice support. Right
|
||||
# now, voice is given its own toggle, and can either be turned on or
|
||||
# turned off. In the future, we'll probably provide some way of
|
||||
# toggling it on or off for individual characters.
|
||||
#
|
||||
# To use it, place a voice "<wavfile>" line before each voiced line of
|
||||
# dialogue.
|
||||
#
|
||||
# voice "e_1001.wav"
|
||||
# e "Voice support lets you add the spoken word to your games."
|
||||
#
|
||||
# Normally, a voice is cancelled at the start of the next
|
||||
# interaction. If you want a voice to span interactions, call
|
||||
# voice_sustain.
|
||||
#
|
||||
# voice "e_1002.wav"
|
||||
# e "Voice sustain is a technique that allows the same voice file.."
|
||||
#
|
||||
# $ voice_sustain()
|
||||
# e "...to play for two lines of dialogue."
|
||||
|
||||
init -440:
|
||||
|
||||
python:
|
||||
|
||||
# Ensure the voice preference exists.
|
||||
if persistent.voice is None:
|
||||
persistent.voice = True
|
||||
|
||||
_voice = object()
|
||||
_voice.play = None
|
||||
_voice.sustain = False
|
||||
|
||||
# Call this to specify the voice file that will be played for
|
||||
# the user.
|
||||
def voice(file):
|
||||
_voice.play = file
|
||||
|
||||
# Call this to specify that the currently playing voice file
|
||||
# should be sustained through the current interaction.
|
||||
def voice_sustain(ignored=""):
|
||||
_voice.sustain = True
|
||||
|
||||
python hide:
|
||||
|
||||
renpy.sound.set_mixer(2, "voice")
|
||||
library.has_voice = True
|
||||
library.sample_voice = None
|
||||
|
||||
vp = _VolumePreference('Voice Volume',
|
||||
'voice',
|
||||
'library.has_voice',
|
||||
'library.sample_voice')
|
||||
|
||||
library.preferences['prefs_right'].insert(1, vp)
|
||||
|
||||
# This is called on each interaction, to ensure that the
|
||||
# appropriate voice file is played for the user.
|
||||
def voice_interact():
|
||||
|
||||
if not persistent.voice:
|
||||
return
|
||||
|
||||
if _voice.play and not config.skipping:
|
||||
renpy.sound.play(_voice.play, channel=2)
|
||||
elif not _voice.sustain:
|
||||
renpy.sound.stop(channel=2)
|
||||
|
||||
_voice.play = None
|
||||
_voice.sustain = False
|
||||
|
||||
config.start_interact_callbacks.append(voice_interact)
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"start_a170b500": {
|
||||
"area": [
|
||||
53,
|
||||
120,
|
||||
426,
|
||||
120
|
||||
]
|
||||
},
|
||||
"start_f41f55d7": {
|
||||
"area": [
|
||||
106,
|
||||
60,
|
||||
426,
|
||||
120
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,478 +0,0 @@
|
||||
################################################################################
|
||||
## Initialization
|
||||
################################################################################
|
||||
|
||||
## The init offset statement causes the initialization statements in this file
|
||||
## to run before init statements in any other file.
|
||||
init offset = -2
|
||||
|
||||
## Calling gui.init resets the styles to sensible default values, and sets the
|
||||
## width and height of the game.
|
||||
init python:
|
||||
gui.init(1280, 720)
|
||||
|
||||
## Enable checks for invalid or unstable properties in screens or transforms
|
||||
define config.check_conflicting_properties = True
|
||||
|
||||
|
||||
################################################################################
|
||||
## GUI Configuration Variables
|
||||
################################################################################
|
||||
|
||||
|
||||
## Colors ######################################################################
|
||||
##
|
||||
## The colors of text in the interface.
|
||||
|
||||
## An accent color used throughout the interface to label and highlight
|
||||
## text.
|
||||
define gui.accent_color = "#00b8c3"
|
||||
|
||||
## The color used for a text button when it is neither selected nor hovered.
|
||||
define gui.idle_color = "#888888"
|
||||
|
||||
## The small color is used for small text, which needs to be
|
||||
## brighter/darker to achieve the same effect.
|
||||
define gui.idle_small_color = "#aaaaaa"
|
||||
|
||||
## The color that is used for buttons and bars that are hovered.
|
||||
define gui.hover_color = Color(gui.accent_color).tint(.6)
|
||||
|
||||
## The color used for a text button when it is selected but not focused.
|
||||
## A button is selected if it is the current screen or preference value.
|
||||
define gui.selected_color = "#ffffff"
|
||||
|
||||
## The color used for a text button when it cannot be selected.
|
||||
define gui.insensitive_color = "#55555580"
|
||||
|
||||
## Colors used for the portions of bars that are not filled in. These are
|
||||
## not used directly, but are used when re-generating bar image files.
|
||||
define gui.muted_color = "#004e49"
|
||||
define gui.hover_muted_color = "#006e75"
|
||||
|
||||
## The colors used for dialogue and menu choice text.
|
||||
define gui.text_color = "#ffffff"
|
||||
define gui.interface_text_color = "#ffffff"
|
||||
|
||||
|
||||
## Fonts and Font Sizes ########################################################
|
||||
|
||||
## The font used for in-game text.
|
||||
define gui.text_font = "DejaVuSans.ttf"
|
||||
|
||||
## The font used for character names.
|
||||
define gui.name_text_font = "DejaVuSans.ttf"
|
||||
|
||||
## The font used for out-of-game text.
|
||||
define gui.interface_text_font = "DejaVuSans.ttf"
|
||||
|
||||
## The size of normal dialogue text.
|
||||
define gui.text_size = gui.scale(22)
|
||||
|
||||
## The size of character names.
|
||||
define gui.name_text_size = gui.scale(30)
|
||||
|
||||
## The size of text in the game's user interface.
|
||||
define gui.interface_text_size = gui.scale(22)
|
||||
|
||||
## The size of labels in the game's user interface.
|
||||
define gui.label_text_size = gui.scale(24)
|
||||
|
||||
## The size of text on the notify screen.
|
||||
define gui.notify_text_size = gui.scale(16)
|
||||
|
||||
## The size of the game's title.
|
||||
define gui.title_text_size = gui.scale(50)
|
||||
|
||||
|
||||
## Main and Game Menus #########################################################
|
||||
|
||||
## The images used for the main and game menus.
|
||||
define gui.main_menu_background = "gui/main_menu.png"
|
||||
define gui.game_menu_background = "gui/game_menu.png"
|
||||
|
||||
|
||||
## Dialogue ####################################################################
|
||||
##
|
||||
## These variables control how dialogue is displayed on the screen one line
|
||||
## at a time.
|
||||
|
||||
## The height of the textbox containing dialogue.
|
||||
define gui.textbox_height = gui.scale(185)
|
||||
|
||||
## The placement of the textbox vertically on the screen. 0.0 is the top, 0.5 is
|
||||
## center, and 1.0 is the bottom.
|
||||
define gui.textbox_yalign = 1.0
|
||||
|
||||
|
||||
## The placement of the speaking character's name, relative to the textbox.
|
||||
## These can be a whole number of pixels from the left or top, or 0.5 to center.
|
||||
define gui.name_xpos = gui.scale(240)
|
||||
define gui.name_ypos = gui.scale(0)
|
||||
|
||||
## The horizontal alignment of the character's name. This can be 0.0 for
|
||||
## left-aligned, 0.5 for centered, and 1.0 for right-aligned.
|
||||
define gui.name_xalign = 0.0
|
||||
|
||||
## The width, height, and borders of the box containing the character's
|
||||
## name, or None to automatically size it.
|
||||
define gui.namebox_width = None
|
||||
define gui.namebox_height = None
|
||||
|
||||
## The borders of the box containing the character's name, in left, top,
|
||||
## right, bottom order.
|
||||
define gui.namebox_borders = Borders(5, 5, 5, 5)
|
||||
|
||||
## If True, the background of the namebox will be tiled, if False, the background
|
||||
## of the namebox will be scaled.
|
||||
define gui.namebox_tile = False
|
||||
|
||||
|
||||
## The placement of dialogue relative to the textbox. These can be a whole
|
||||
## number of pixels relative to the left or top side of the textbox, or 0.5
|
||||
## to center.
|
||||
define gui.dialogue_xpos = gui.scale(268)
|
||||
define gui.dialogue_ypos = gui.scale(50)
|
||||
|
||||
## The maximum width of dialogue text, in pixels.
|
||||
define gui.dialogue_width = gui.scale(744)
|
||||
|
||||
## The horizontal alignment of the dialogue text. This can be 0.0 for
|
||||
## left-aligned, 0.5 for centered, and 1.0 for right-aligned.
|
||||
define gui.dialogue_text_xalign = 0.0
|
||||
|
||||
|
||||
## Buttons #####################################################################
|
||||
##
|
||||
## These variables, along with the image files in gui/button, control aspects
|
||||
## of how buttons are displayed.
|
||||
|
||||
## The width and height of a button, in pixels. If None, Ren'Py computes a size.
|
||||
define gui.button_width = None
|
||||
define gui.button_height = None
|
||||
|
||||
## The borders on each side of the button, in left, top, right, bottom order.
|
||||
define gui.button_borders = Borders(gui.scale(4), gui.scale(4), gui.scale(4), gui.scale(4))
|
||||
|
||||
## If True, the background image will be tiled. If False, the background image
|
||||
## will be linearly scaled.
|
||||
define gui.button_tile = False
|
||||
|
||||
## The font used by the button.
|
||||
define gui.button_text_font = gui.interface_text_font
|
||||
|
||||
## The size of the text used by the button.
|
||||
define gui.button_text_size = gui.interface_text_size
|
||||
|
||||
## The color of button text in various states.
|
||||
define gui.button_text_idle_color = gui.idle_color
|
||||
define gui.button_text_hover_color = gui.hover_color
|
||||
define gui.button_text_selected_color = gui.selected_color
|
||||
define gui.button_text_insensitive_color = gui.insensitive_color
|
||||
|
||||
## The horizontal alignment of the button text. (0.0 is left, 0.5 is center,
|
||||
## 1.0 is right).
|
||||
define gui.button_text_xalign = 0.0
|
||||
|
||||
|
||||
## These variables override settings for different kinds of buttons. Please
|
||||
## see the gui documentation for the kinds of buttons available, and what
|
||||
## each is used for.
|
||||
##
|
||||
## These customizations are used by the default interface:
|
||||
|
||||
define gui.radio_button_borders = Borders(gui.scale(18), gui.scale(4), gui.scale(4), gui.scale(4))
|
||||
|
||||
define gui.check_button_borders = Borders(gui.scale(18), gui.scale(4), gui.scale(4), gui.scale(4))
|
||||
|
||||
define gui.confirm_button_text_xalign = 0.5
|
||||
|
||||
define gui.page_button_borders = Borders(gui.scale(10), gui.scale(4), gui.scale(10), gui.scale(4))
|
||||
|
||||
define gui.quick_button_borders = Borders(gui.scale(10), gui.scale(4), gui.scale(10), gui.scale(0))
|
||||
define gui.quick_button_text_size = gui.scale(14)
|
||||
define gui.quick_button_text_idle_color = gui.idle_small_color
|
||||
define gui.quick_button_text_selected_color = gui.accent_color
|
||||
|
||||
## You can also add your own customizations, by adding properly-named variables.
|
||||
## For example, you can uncomment the following line to set the width of a
|
||||
## navigation button.
|
||||
|
||||
# define gui.navigation_button_width = 250
|
||||
|
||||
|
||||
## Choice Buttons ##############################################################
|
||||
##
|
||||
## Choice buttons are used in the in-game menus.
|
||||
|
||||
define gui.choice_button_width = gui.scale(790)
|
||||
define gui.choice_button_height = None
|
||||
define gui.choice_button_tile = False
|
||||
define gui.choice_button_borders = Borders(gui.scale(100), gui.scale(5), gui.scale(100), gui.scale(5))
|
||||
define gui.choice_button_text_font = gui.text_font
|
||||
define gui.choice_button_text_size = gui.text_size
|
||||
define gui.choice_button_text_xalign = 0.5
|
||||
define gui.choice_button_text_idle_color = "#cccccc"
|
||||
define gui.choice_button_text_hover_color = "#ffffff"
|
||||
define gui.choice_button_text_insensitive_color = "#444444"
|
||||
|
||||
|
||||
## File Slot Buttons ###########################################################
|
||||
##
|
||||
## A file slot button is a special kind of button. It contains a thumbnail
|
||||
## image, and text describing the contents of the save slot. A save slot
|
||||
## uses image files in gui/button, like the other kinds of buttons.
|
||||
|
||||
## The save slot button.
|
||||
define gui.slot_button_width = gui.scale(276)
|
||||
define gui.slot_button_height = gui.scale(206)
|
||||
define gui.slot_button_borders = Borders(gui.scale(10), gui.scale(10), gui.scale(10), gui.scale(10))
|
||||
define gui.slot_button_text_size = gui.scale(14)
|
||||
define gui.slot_button_text_xalign = 0.5
|
||||
define gui.slot_button_text_idle_color = gui.idle_small_color
|
||||
define gui.slot_button_text_selected_idle_color = gui.selected_color
|
||||
define gui.slot_button_text_selected_hover_color = gui.hover_color
|
||||
|
||||
## The width and height of thumbnails used by the save slots.
|
||||
define config.thumbnail_width = gui.scale(256)
|
||||
define config.thumbnail_height = gui.scale(144)
|
||||
|
||||
## The number of columns and rows in the grid of save slots.
|
||||
define gui.file_slot_cols = 3
|
||||
define gui.file_slot_rows = 2
|
||||
|
||||
|
||||
## Positioning and Spacing #####################################################
|
||||
##
|
||||
## These variables control the positioning and spacing of various user interface
|
||||
## elements.
|
||||
|
||||
## The position of the left side of the navigation buttons, relative
|
||||
## to the left side of the screen.
|
||||
define gui.navigation_xpos = gui.scale(40)
|
||||
|
||||
## The vertical position of the skip indicator.
|
||||
define gui.skip_ypos = gui.scale(10)
|
||||
|
||||
## The vertical position of the notify screen.
|
||||
define gui.notify_ypos = gui.scale(45)
|
||||
|
||||
## The spacing between menu choices.
|
||||
define gui.choice_spacing = gui.scale(22)
|
||||
|
||||
## Buttons in the navigation section of the main and game menus.
|
||||
define gui.navigation_spacing = gui.scale(4)
|
||||
|
||||
## Controls the amount of spacing between preferences.
|
||||
define gui.pref_spacing = gui.scale(10)
|
||||
|
||||
## Controls the amount of spacing between preference buttons.
|
||||
define gui.pref_button_spacing = gui.scale(0)
|
||||
|
||||
## The spacing between file page buttons.
|
||||
define gui.page_spacing = gui.scale(0)
|
||||
|
||||
## The spacing between file slots.
|
||||
define gui.slot_spacing = gui.scale(10)
|
||||
|
||||
## The position of the main menu text.
|
||||
define gui.main_menu_text_xalign = 1.0
|
||||
|
||||
|
||||
## Frames ######################################################################
|
||||
##
|
||||
## These variables control the look of frames that can contain user interface
|
||||
## components when an overlay or window is not present.
|
||||
|
||||
## Generic frames.
|
||||
define gui.frame_borders = Borders(gui.scale(4), gui.scale(4), gui.scale(4), gui.scale(4))
|
||||
|
||||
## The frame that is used as part of the confirm screen.
|
||||
define gui.confirm_frame_borders = Borders(gui.scale(40), gui.scale(40), gui.scale(40), gui.scale(40))
|
||||
|
||||
## The frame that is used as part of the skip screen.
|
||||
define gui.skip_frame_borders = Borders(gui.scale(16), gui.scale(5), gui.scale(50), gui.scale(5))
|
||||
|
||||
## The frame that is used as part of the notify screen.
|
||||
define gui.notify_frame_borders = Borders(gui.scale(16), gui.scale(5), gui.scale(40), gui.scale(5))
|
||||
|
||||
## Should frame backgrounds be tiled?
|
||||
define gui.frame_tile = False
|
||||
|
||||
|
||||
## Bars, Scrollbars, and Sliders ###############################################
|
||||
##
|
||||
## These control the look and size of bars, scrollbars, and sliders.
|
||||
##
|
||||
## The default GUI only uses sliders and vertical scrollbars.
|
||||
## All of the other bars are only used in creator-written screens.
|
||||
|
||||
## The height of horizontal bars, scrollbars, and sliders. The width of
|
||||
## vertical bars, scrollbars, and sliders.
|
||||
define gui.bar_size = gui.scale(25)
|
||||
define gui.scrollbar_size = gui.scale(12)
|
||||
define gui.slider_size = gui.scale(25)
|
||||
|
||||
## True if bar images should be tiled. False if they should be linearly scaled.
|
||||
define gui.bar_tile = False
|
||||
define gui.scrollbar_tile = False
|
||||
define gui.slider_tile = False
|
||||
|
||||
## Horizontal borders.
|
||||
define gui.bar_borders = Borders(gui.scale(4), gui.scale(4), gui.scale(4), gui.scale(4))
|
||||
define gui.scrollbar_borders = Borders(gui.scale(4), gui.scale(4), gui.scale(4), gui.scale(4))
|
||||
define gui.slider_borders = Borders(gui.scale(4), gui.scale(4), gui.scale(4), gui.scale(4))
|
||||
|
||||
## Vertical borders.
|
||||
define gui.vbar_borders = Borders(gui.scale(4), gui.scale(4), gui.scale(4), gui.scale(4))
|
||||
define gui.vscrollbar_borders = Borders(gui.scale(4), gui.scale(4), gui.scale(4), gui.scale(4))
|
||||
define gui.vslider_borders = Borders(gui.scale(4), gui.scale(4), gui.scale(4), gui.scale(4))
|
||||
|
||||
## What to do with unscrollable scrollbars in the gui. "hide" hides them, while
|
||||
## None shows them.
|
||||
define gui.unscrollable = "hide"
|
||||
|
||||
|
||||
## History #####################################################################
|
||||
##
|
||||
## The history screen displays dialogue that the player has already dismissed.
|
||||
|
||||
## The number of blocks of dialogue history Ren'Py will keep.
|
||||
define config.history_length = 250
|
||||
|
||||
## The height of a history screen entry, or None to make the height variable
|
||||
## at the cost of performance.
|
||||
define gui.history_height = gui.scale(140)
|
||||
|
||||
## The position, width, and alignment of the label giving the name of the
|
||||
## speaking character.
|
||||
define gui.history_name_xpos = gui.scale(155)
|
||||
define gui.history_name_ypos = 0
|
||||
define gui.history_name_width = gui.scale(155)
|
||||
define gui.history_name_xalign = 1.0
|
||||
|
||||
## The position, width, and alignment of the dialogue text.
|
||||
define gui.history_text_xpos = gui.scale(170)
|
||||
define gui.history_text_ypos = gui.scale(2)
|
||||
define gui.history_text_width = gui.scale(740)
|
||||
define gui.history_text_xalign = 0.0
|
||||
|
||||
|
||||
## NVL-Mode ###################################################################
|
||||
##
|
||||
## The NVL-mode screen displays the dialogue spoken by NVL-mode characters.
|
||||
|
||||
## The borders of the background of the NVL-mode background window.
|
||||
define gui.nvl_borders = Borders(0, gui.scale(10), 0, gui.scale(20))
|
||||
|
||||
## The maximum number of NVL-mode entries Ren'Py will display. When more
|
||||
## entries than this are to be show, the oldest entry will be removed.
|
||||
define gui.nvl_list_length = 6
|
||||
|
||||
## The height of an NVL-mode entry. Set this to None to have the entries
|
||||
## dynamically adjust height.
|
||||
define gui.nvl_height = gui.scale(115)
|
||||
|
||||
## The spacing between NVL-mode entries when gui.nvl_height is None, and
|
||||
## between NVL-mode entries and an NVL-mode menu.
|
||||
define gui.nvl_spacing = gui.scale(10)
|
||||
|
||||
## The position, width, and alignment of the label giving the name of the
|
||||
## speaking character.
|
||||
define gui.nvl_name_xpos = gui.scale(430)
|
||||
define gui.nvl_name_ypos = 0
|
||||
define gui.nvl_name_width = gui.scale(150)
|
||||
define gui.nvl_name_xalign = 1.0
|
||||
|
||||
## The position, width, and alignment of the dialogue text.
|
||||
define gui.nvl_text_xpos = gui.scale(450)
|
||||
define gui.nvl_text_ypos = gui.scale(8)
|
||||
define gui.nvl_text_width = gui.scale(590)
|
||||
define gui.nvl_text_xalign = 0.0
|
||||
|
||||
## The position, width, and alignment of nvl_thought text (the text said by
|
||||
## the nvl_narrator character.)
|
||||
define gui.nvl_thought_xpos = gui.scale(240)
|
||||
define gui.nvl_thought_ypos = gui.scale(0)
|
||||
define gui.nvl_thought_width = gui.scale(780)
|
||||
define gui.nvl_thought_xalign = 0.0
|
||||
|
||||
## The position of nvl menu_buttons.
|
||||
define gui.nvl_button_xpos = gui.scale(450)
|
||||
define gui.nvl_button_xalign = 0.0
|
||||
|
||||
|
||||
## Localization ################################################################
|
||||
|
||||
## This controls where a line break is permitted. The default is suitable for
|
||||
## most languages. A list of available values can be found at
|
||||
## https://www.renpy.org/doc/html/style_properties.html#style-property-language
|
||||
|
||||
define gui.language = "unicode"
|
||||
|
||||
|
||||
################################################################################
|
||||
## Mobile devices
|
||||
################################################################################
|
||||
|
||||
init python:
|
||||
|
||||
## This increases the size of the quick buttons to make them easier to
|
||||
## touch on tablets and phones.
|
||||
@gui.variant
|
||||
def touch():
|
||||
|
||||
gui.quick_button_borders = Borders(gui.scale(40), gui.scale(14), gui.scale(40), gui.scale(0))
|
||||
|
||||
## This changes the size and spacing of various GUI elements to ensure
|
||||
## they are easily visible on phones.
|
||||
@gui.variant
|
||||
def small():
|
||||
|
||||
## Font sizes.
|
||||
gui.text_size = gui.scale(30)
|
||||
gui.name_text_size = gui.scale(36)
|
||||
gui.notify_text_size = gui.scale(25)
|
||||
gui.interface_text_size = gui.scale(30)
|
||||
gui.button_text_size = gui.scale(30)
|
||||
gui.label_text_size = gui.scale(34)
|
||||
|
||||
## Adjust the location of the textbox.
|
||||
gui.textbox_height = gui.scale(240)
|
||||
gui.name_xpos = gui.scale(80)
|
||||
gui.dialogue_xpos = gui.scale(90)
|
||||
gui.dialogue_width = gui.scale(1100)
|
||||
|
||||
## Change the size and spacing of various things.
|
||||
gui.slider_size = gui.scale(36)
|
||||
|
||||
gui.choice_button_width = gui.scale(1240)
|
||||
gui.choice_button_text_size = gui.scale(30)
|
||||
|
||||
gui.navigation_spacing = gui.scale(20)
|
||||
gui.pref_button_spacing = gui.scale(10)
|
||||
|
||||
gui.history_height = gui.scale(190)
|
||||
gui.history_text_width = gui.scale(690)
|
||||
|
||||
gui.quick_button_text_size = gui.scale(20)
|
||||
|
||||
## File button layout.
|
||||
gui.file_slot_cols = 2
|
||||
gui.file_slot_rows = 2
|
||||
|
||||
## NVL-mode.
|
||||
gui.nvl_height = gui.scale(170)
|
||||
|
||||
gui.nvl_name_width = gui.scale(305)
|
||||
gui.nvl_name_xpos = gui.scale(325)
|
||||
|
||||
gui.nvl_text_width = gui.scale(915)
|
||||
gui.nvl_text_xpos = gui.scale(345)
|
||||
gui.nvl_text_ypos = gui.scale(5)
|
||||
|
||||
gui.nvl_thought_width = gui.scale(1240)
|
||||
gui.nvl_thought_xpos = gui.scale(20)
|
||||
|
||||
gui.nvl_button_width = gui.scale(1240)
|
||||
gui.nvl_button_xpos = gui.scale(20)
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,30 +0,0 @@
|
||||
init -100 python in gui:
|
||||
|
||||
def scale(n):
|
||||
return int(n)
|
||||
|
||||
init 100 python in gui:
|
||||
|
||||
if not renpy.mobile:
|
||||
from store import config
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.join(config.renpy_base, "launcher", "game"))
|
||||
|
||||
from gui7.parameters import GuiParameters
|
||||
from gui7.images import ImageGenerator
|
||||
from gui7 import generate_gui
|
||||
|
||||
p = GuiParameters(
|
||||
config.gamedir, config.gamedir,
|
||||
config.screen_width, config.screen_height,
|
||||
accent_color, "#000000", False, None,
|
||||
True, False, False, "gui")
|
||||
|
||||
p.skip_backup = True
|
||||
|
||||
generate_gui(p)
|
||||
|
||||
_skip_backup = True
|
||||
_gui_images()
|
||||
@@ -1,213 +0,0 @@
|
||||
## This file contains options that can be changed to customize your
|
||||
## game.
|
||||
##
|
||||
## 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.
|
||||
|
||||
|
||||
## Basics ######################################################################
|
||||
|
||||
## A human-readable name of the game. This is used to set the default
|
||||
## window title, and shows up in the interface and error reports.
|
||||
##
|
||||
## The _() surrounding the string marks it as eligible for translation.
|
||||
|
||||
define config.name = _("Ren'Py 7 Default GUI")
|
||||
|
||||
|
||||
## Determines if the title given above is shown on the main menu
|
||||
## screen. Set this to False to hide the title.
|
||||
|
||||
define gui.show_name = True
|
||||
|
||||
|
||||
## The version of the game.
|
||||
|
||||
define config.version = "1.0"
|
||||
|
||||
|
||||
## Text that is placed on the game's about screen. Place the text between
|
||||
## the triple-quotes, and leave a blank line between paragraphs.
|
||||
|
||||
define gui.about = _p("""
|
||||
""")
|
||||
|
||||
|
||||
## A short name for the game used for executables and directories in the
|
||||
## built distribution. This must be ASCII-only, and must not contain
|
||||
## spaces, colons, or semicolons.
|
||||
|
||||
define build.name = "gui"
|
||||
|
||||
|
||||
## Sounds and music ############################################################
|
||||
|
||||
## These three variables control, among other things, which mixers are
|
||||
## shown to the player by default. Setting one of these to False will
|
||||
## hide the appropriate mixer.
|
||||
|
||||
define config.has_sound = True
|
||||
define config.has_music = True
|
||||
define config.has_voice = True
|
||||
|
||||
|
||||
## To allow the user to play a test sound on the sound or voice channel,
|
||||
## uncomment a line below and use it to set a sample sound to play.
|
||||
|
||||
# define config.sample_sound = "sample-sound.ogg"
|
||||
# define config.sample_voice = "sample-voice.ogg"
|
||||
|
||||
|
||||
## Uncomment the following line to set an audio file that will be played
|
||||
## while the player is at the main menu. This file will continue playing
|
||||
## into the game, until it is stopped or another file is played.
|
||||
|
||||
# define config.main_menu_music = "main-menu-theme.ogg"
|
||||
|
||||
|
||||
## Transitions #################################################################
|
||||
##
|
||||
## These variables set transitions that are used when certain events occur.
|
||||
## Each variable should be set to a transition, or None to indicate that
|
||||
## no transition should be used.
|
||||
|
||||
## Entering or exiting the game menu.
|
||||
|
||||
define config.enter_transition = dissolve
|
||||
define config.exit_transition = dissolve
|
||||
|
||||
|
||||
## Between screens of the game menu.
|
||||
|
||||
define config.intra_transition = dissolve
|
||||
|
||||
|
||||
## A transition that is used after a game has been loaded.
|
||||
|
||||
define config.after_load_transition = None
|
||||
|
||||
|
||||
## Used when entering the main menu after the game has ended.
|
||||
|
||||
define config.end_game_transition = None
|
||||
|
||||
|
||||
## A variable to set the transition used when the game starts
|
||||
## does not exist. Instead, use a with statement after showing
|
||||
## the initial scene.
|
||||
|
||||
|
||||
## Window management ###########################################################
|
||||
##
|
||||
## This controls when the dialogue window is displayed. If "show", it is always
|
||||
## displayed. If "hide", it is only displayed when dialogue is present. If
|
||||
## "auto", the window is hidden before scene statements and shown again
|
||||
## once dialogue is displayed.
|
||||
##
|
||||
## After the game has started, this can be changed with the "window show",
|
||||
## "window hide", and "window auto" statements.
|
||||
|
||||
define config.window = "auto"
|
||||
|
||||
|
||||
## Transitions used to show and hide the dialogue window
|
||||
|
||||
define config.window_show_transition = Dissolve(.2)
|
||||
define config.window_hide_transition = Dissolve(.2)
|
||||
|
||||
|
||||
## Preference defaults #########################################################
|
||||
|
||||
## Controls the default text speed. The default, 0, is infinite, while any
|
||||
## other number is the number of characters per second to type out.
|
||||
|
||||
default preferences.text_cps = 0
|
||||
|
||||
|
||||
## The default auto-forward delay. Larger numbers lead to longer waits,
|
||||
## with 0 to 30 being the valid range.
|
||||
|
||||
default preferences.afm_time = 15
|
||||
|
||||
|
||||
## Save directory ##############################################################
|
||||
##
|
||||
## Controls the platform-specific place Ren'Py will place the save
|
||||
## files for this game. The save files will be placed in:
|
||||
##
|
||||
## Windows: %APPDATA\RenPy\<config.save_directory>
|
||||
##
|
||||
## Macintosh: $HOME/Library/RenPy/<config.save_directory>
|
||||
##
|
||||
## Linux: $HOME/.renpy/<config.save_directory>
|
||||
##
|
||||
## This generally should not be changed, and if it is, should always be a
|
||||
## literal string, not an expression.
|
||||
|
||||
define config.save_directory = "gui-7"
|
||||
|
||||
|
||||
## Icon ########################################################################
|
||||
##
|
||||
## The icon displayed on the taskbar or dock.
|
||||
|
||||
define config.window_icon = "gui/window_icon.png"
|
||||
|
||||
|
||||
## Build configuration #########################################################
|
||||
##
|
||||
## This section controls how Ren'Py turns your project into distribution
|
||||
## files.
|
||||
|
||||
init python:
|
||||
|
||||
## The following functions take file patterns. File patterns are case-
|
||||
## insensitive, and matched against the path relative to the base
|
||||
## directory, with and without a leading /. If multiple patterns match,
|
||||
## the first is used.
|
||||
##
|
||||
## In a pattern:
|
||||
##
|
||||
## / is the directory separator.
|
||||
##
|
||||
## * matches all characters, except the directory separator.
|
||||
##
|
||||
## ** matches all characters, including the directory separator.
|
||||
##
|
||||
## For example, "*.txt" matches txt files in the base directory, "game/**.ogg"
|
||||
## matches ogg files in the game directory or any of its subdirectories, and
|
||||
## "**.psd" matches psd files anywhere in the project.
|
||||
|
||||
## Classify files as None to exclude them from the built distributions.
|
||||
|
||||
build.classify('**~', None)
|
||||
build.classify('**.bak', None)
|
||||
build.classify('**/.**', None)
|
||||
build.classify('**/#**', None)
|
||||
build.classify('**/thumbs.db', None)
|
||||
|
||||
## To archive files, classify them as 'archive'.
|
||||
|
||||
# build.classify('game/**.png', 'archive')
|
||||
# build.classify('game/**.jpg', 'archive')
|
||||
|
||||
## Files matching documentation patterns are duplicated in a mac app
|
||||
## build, so they appear in both the app and the zip file.
|
||||
|
||||
build.documentation('*.html')
|
||||
build.documentation('*.txt')
|
||||
|
||||
|
||||
## A Google Play license key is required to perform in-app purchases.
|
||||
## It can be found in the Google Play developer console,
|
||||
## under "Monetize" > "Monetization Setup" > "Licensing".
|
||||
|
||||
# define build.google_play_key = "..."
|
||||
|
||||
|
||||
## The username and project name associated with an itch.io project,
|
||||
## separated by a slash.
|
||||
|
||||
# define build.itch_project = "renpytom/test-project"
|
||||
@@ -1,33 +0,0 @@
|
||||
# The script of the game goes in this file.
|
||||
|
||||
# Declare characters used by this game. The color argument colorizes the
|
||||
# name of the character.
|
||||
|
||||
define e = Character("Eileen")
|
||||
|
||||
|
||||
# The game starts here.
|
||||
|
||||
label start:
|
||||
|
||||
# Show a background. This uses a placeholder by default, but you can
|
||||
# add a file (named either "bg room.png" or "bg room.jpg") to the
|
||||
# images directory to show it.
|
||||
|
||||
scene bg room
|
||||
|
||||
# This shows a character sprite. A placeholder is used, but you can
|
||||
# replace it by adding a file named "eileen happy.png" to the images
|
||||
# directory.
|
||||
|
||||
show eileen happy
|
||||
|
||||
# These display lines of dialogue.
|
||||
|
||||
e "You've created a new Ren'Py game."
|
||||
|
||||
e "Once you add a story, pictures, and music, you can release it to the world!"
|
||||
|
||||
# This ends the game.
|
||||
|
||||
return
|
||||
@@ -1,160 +0,0 @@
|
||||
testcase default:
|
||||
|
||||
"Start"
|
||||
pause .6
|
||||
|
||||
# Test rollback
|
||||
"Hello, World."
|
||||
"Back"
|
||||
|
||||
# Test history.
|
||||
click
|
||||
click
|
||||
"History"
|
||||
pause .6
|
||||
|
||||
# Test Help.
|
||||
"Help"
|
||||
"Keyboard"
|
||||
|
||||
# Test About
|
||||
"About"
|
||||
|
||||
# Test Preferences
|
||||
"Preferences"
|
||||
|
||||
"Left"
|
||||
"Right"
|
||||
"Disable"
|
||||
|
||||
"Unseen Text"
|
||||
"Unseen Text"
|
||||
|
||||
"After Choices"
|
||||
"After Choices"
|
||||
|
||||
"Transitions"
|
||||
"Transitions"
|
||||
|
||||
"Mute All"
|
||||
"Mute All"
|
||||
|
||||
"Save"
|
||||
"Load"
|
||||
|
||||
"Return"
|
||||
pause .6
|
||||
|
||||
run Jump("test")
|
||||
|
||||
"In testcase code."
|
||||
|
||||
# menu_1
|
||||
click until "Choice A"
|
||||
|
||||
|
||||
# input
|
||||
type "Test User\n"
|
||||
"Name: Test User"
|
||||
|
||||
# Nvl
|
||||
click
|
||||
click
|
||||
click
|
||||
|
||||
click
|
||||
click
|
||||
click until "NVL 6"
|
||||
|
||||
# NVL Menu.
|
||||
"Choice B"
|
||||
|
||||
$ renpy.unlink_save("1-1")
|
||||
"Save"
|
||||
|
||||
pause .6
|
||||
|
||||
"Save slot 1"
|
||||
|
||||
pause .6
|
||||
|
||||
"Save slot 1"
|
||||
"Yes"
|
||||
|
||||
"Main Menu"
|
||||
"Yes"
|
||||
|
||||
"Load"
|
||||
"Load slot 1"
|
||||
|
||||
pause .5
|
||||
|
||||
"Save"
|
||||
|
||||
pause .5
|
||||
|
||||
|
||||
"Load"
|
||||
"Load slot 1"
|
||||
"No"
|
||||
|
||||
"Return"
|
||||
|
||||
# Done.
|
||||
pause .5
|
||||
|
||||
"Done."
|
||||
"Quit"
|
||||
|
||||
|
||||
define nvle = Character("Eileen", kind=nvl)
|
||||
|
||||
|
||||
label test:
|
||||
|
||||
"In testcase code."
|
||||
|
||||
|
||||
menu menu_1:
|
||||
"This is a menu."
|
||||
|
||||
"Choice A":
|
||||
pass
|
||||
|
||||
"Choice B":
|
||||
pass
|
||||
|
||||
label after_menu_1:
|
||||
|
||||
|
||||
$ name = renpy.input("What is your name?")
|
||||
"Name: [name]"
|
||||
|
||||
nvle "NVL 1"
|
||||
nvle "NVL 2"
|
||||
nvle "NVL 3"
|
||||
|
||||
nvl clear
|
||||
|
||||
nvle "NVL 4"
|
||||
nvle "NVL 5"
|
||||
nvle "NVL 6"
|
||||
|
||||
|
||||
$ menu = nvl_menu
|
||||
|
||||
|
||||
menu menu_2:
|
||||
"This is a menu."
|
||||
|
||||
"Choice A":
|
||||
pass
|
||||
|
||||
"Choice B":
|
||||
pass
|
||||
|
||||
label after_menu_2:
|
||||
|
||||
"Done."
|
||||
|
||||
return
|
||||
@@ -1 +0,0 @@
|
||||
{ "type" : "hidden" }
|
||||
@@ -0,0 +1,134 @@
|
||||
;;; Comments beginning with ";;;" are commentary added by PyTom to
|
||||
;;; try to help you understand this file.
|
||||
;;;
|
||||
;;; This installer expects that you will have supplied LICENSE.txt
|
||||
;;; and README.txt files in the root directory of your game.
|
||||
;;;
|
||||
;;; To use this, place it in the root directory of your game,
|
||||
;;; right-click on it, and choose "Compile NSIS script..."
|
||||
|
||||
;;; First up, change the settings below to match your game.
|
||||
|
||||
;;; The exe used to run your game.
|
||||
!define EXE "moonlight.exe"
|
||||
|
||||
;;; The exe containing the installer. This will be created in the directory
|
||||
;;; above the root directory containing your game.
|
||||
!define INSTALLER_EXE "moonlight-1.0ni.exe"
|
||||
|
||||
;;; The name and version.
|
||||
!define PRODUCT_NAME "Moonlight Walks"
|
||||
!define PRODUCT_VERSION "1.0"
|
||||
|
||||
;;; The following settings are only shown to the user in Add/Remove programs
|
||||
;;; but you'll still want to use them.
|
||||
|
||||
!define PRODUCT_WEB_SITE "http://www.bishoujo.us/moonlight/"
|
||||
!define PRODUCT_PUBLISHER "American Bishoujo"
|
||||
|
||||
;;; Ignore this next block of stuff. It's mostly boilerplate.
|
||||
!include "MUI.nsh"
|
||||
!define MUI_ABORTWARNING
|
||||
|
||||
;;; Change this to change the compression scheme.
|
||||
SetCompressor lzma
|
||||
|
||||
;;; You can change these to customize the bitmaps and icons used for
|
||||
;;; your installer and uninstaller. Bitmaps should be 150x57.
|
||||
|
||||
; !define MUI_HEADERIMAGE
|
||||
; !define MUI_HEADERIMAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Header\nsis.bmp"
|
||||
; !define MUI_HEADERIMAGE_UNBITMAP "${NSISDIR}\Contrib\Graphics\Header\nsis.bmp"
|
||||
!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
|
||||
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
|
||||
|
||||
|
||||
;;; This is the sequencing of the pages that does the actual installation.
|
||||
;;; you can comment pages out, if you want to.
|
||||
|
||||
; Welcome page
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
|
||||
; License page
|
||||
!insertmacro MUI_PAGE_LICENSE "LICENSE.txt"
|
||||
|
||||
; Directory page
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
; Instfiles page
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
; Finish page
|
||||
!define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\README.txt"
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
; Uninstaller pages
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
;;; Okay, that's it for the commentary. You're on your own from here...
|
||||
;;; but you probably won't need to touch anything below this point.
|
||||
|
||||
; Various other defines.
|
||||
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
|
||||
!define PRODUCT_UNINST_ROOT_KEY "HKLM"
|
||||
|
||||
; Language files
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
|
||||
; Reserve files
|
||||
!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS
|
||||
|
||||
|
||||
Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
|
||||
OutFile "..\${INSTALLER_EXE}"
|
||||
InstallDir "$PROGRAMFILES\${PRODUCT_NAME}"
|
||||
|
||||
Section "!${PRODUCT_NAME}" SEC01
|
||||
SetOutPath "$INSTDIR"
|
||||
SetOverwrite ifnewer
|
||||
|
||||
File /r /x renpy /x *.py /x *.pyw /x installer.nsi /x persistent *.*
|
||||
CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
|
||||
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk" "$INSTDIR\${EXE}"
|
||||
CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" "$INSTDIR\${EXE}"
|
||||
SectionEnd
|
||||
|
||||
Section -AdditionalIcons
|
||||
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall.lnk" "$INSTDIR\uninst.exe"
|
||||
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\README.lnk" "$INSTDIR\README.txt"
|
||||
SectionEnd
|
||||
|
||||
Section -Post
|
||||
WriteUninstaller "$INSTDIR\uninst.exe"
|
||||
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
|
||||
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
|
||||
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\${EXE}"
|
||||
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
|
||||
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
|
||||
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
|
||||
SectionEnd
|
||||
|
||||
|
||||
Function un.onUninstSuccess
|
||||
HideWindow
|
||||
MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer."
|
||||
FunctionEnd
|
||||
|
||||
Function un.onInit
|
||||
MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Are you sure you want to completely remove $(^Name) and all of its components?" IDYES +2
|
||||
Abort
|
||||
FunctionEnd
|
||||
|
||||
Section Uninstall
|
||||
|
||||
Delete "$SMPROGRAMS\${PRODUCT_NAME}\README.lnk"
|
||||
Delete "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall.lnk"
|
||||
Delete "$DESKTOP\${PRODUCT_NAME}.lnk"
|
||||
Delete "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk"
|
||||
|
||||
RMDir "$SMPROGRAMS\${PRODUCT_NAME}"
|
||||
RMDir /r "$INSTDIR"
|
||||
|
||||
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
|
||||
SetAutoClose true
|
||||
SectionEnd
|
||||
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# The path to a copy of Atom that's been installed globally, if one exists.
|
||||
# This is overidden by RENPY_ATOM, if set. If either is given, that is used
|
||||
# and no special handing of the .atom directory is performed.
|
||||
ATOM = None
|
||||
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import platform
|
||||
import shutil
|
||||
|
||||
import renpy
|
||||
|
||||
|
||||
class Editor(renpy.editor.Editor):
|
||||
|
||||
has_projects = True
|
||||
|
||||
def get_atom(self):
|
||||
"""
|
||||
Returns the path to the atom executable, if None. Also takes care
|
||||
of setting up the .atom directory if it's not available.
|
||||
"""
|
||||
|
||||
atom = os.environ.get("RENPY_ATOM", ATOM)
|
||||
|
||||
if atom is not None:
|
||||
return atom
|
||||
|
||||
DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||
DIR = os.path.join(renpy.exports.fsdecode(DIR), "atom")
|
||||
|
||||
if renpy.windows:
|
||||
atom = os.path.join(DIR, "atom-windows", "atom.exe")
|
||||
elif renpy.macintosh:
|
||||
atom = os.path.join(DIR, "Atom.app", "Contents", "Resources", "app", "atom.sh")
|
||||
else:
|
||||
atom = os.path.join(DIR, "atom-linux-" + platform.machine(), "atom")
|
||||
|
||||
default_dot_atom = os.path.join(DIR, "default-dot-atom")
|
||||
dot_atom = os.path.join(DIR, ".atom")
|
||||
|
||||
if not os.path.exists(dot_atom) and os.path.exists(default_dot_atom):
|
||||
shutil.copytree(default_dot_atom, dot_atom)
|
||||
|
||||
return atom
|
||||
|
||||
def begin(self, new_window=False, **kwargs):
|
||||
self.args = [ ]
|
||||
|
||||
def open(self, filename, line=None, **kwargs):
|
||||
|
||||
if line:
|
||||
filename = "{}:{}".format(filename, line)
|
||||
|
||||
self.args.append(filename)
|
||||
|
||||
def open_project(self, project):
|
||||
self.args.append(project)
|
||||
|
||||
def end(self, **kwargs):
|
||||
|
||||
atom = self.get_atom()
|
||||
|
||||
self.args.reverse()
|
||||
|
||||
args = [ atom ] + self.args
|
||||
args = [ renpy.exports.fsencode(i) for i in args ]
|
||||
|
||||
subprocess.Popen(args)
|
||||
|
||||
|
||||
def main():
|
||||
e = Editor()
|
||||
e.begin()
|
||||
|
||||
for i in sys.argv[1:]:
|
||||
e.open(i)
|
||||
|
||||
e.end()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||