Compare commits
141 Commits
blit
..
renpy-4.8.9
| Author | SHA1 | Date | |
|---|---|---|---|
| 24136d7de1 | |||
| 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,52 @@
|
||||
Copyright 2004-2005 PyTom <pytom@bishoujo.us>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
(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.
|
||||
|
||||
|
||||
|
||||
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.
|
||||
|
||||
These packages may include:
|
||||
|
||||
Python (Python License)
|
||||
http://www.python.org
|
||||
|
||||
Pygame (GNU LGPL)
|
||||
http://www.pygame.org
|
||||
|
||||
SDL (GNU LGPL)
|
||||
http://www.libsdl.org
|
||||
|
||||
SDL_mixer (GNU LGPL)
|
||||
http://www.libsdl.org/projects/SDL_mixer
|
||||
|
||||
SDL_ttf (GNU LGPL)
|
||||
http://www.libsdl.org/projects/SDL_ttf
|
||||
|
||||
ctypes (MIT)
|
||||
http://starship.python.net/crew/theller/ctypes/
|
||||
|
||||
|
||||
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.
|
||||
@@ -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
|
||||
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 = 32
|
||||
|
||||
def randpadding():
|
||||
|
||||
plen = random.randint(1, padding_max)
|
||||
|
||||
rv = ""
|
||||
|
||||
for i in range(0, plen):
|
||||
rv += chr(random.randint(1, 255))
|
||||
|
||||
return rv
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print "Usage: %s <file-prefix> <files ...>" % sys.argv[0]
|
||||
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,31 @@
|
||||
#!/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' ]
|
||||
|
||||
setup(name="RenPy",
|
||||
windows=programs,
|
||||
console=[ "archiver.py", "add_from.py", "console.py", "dump_text.py", "run_dse.py" ],
|
||||
zipfile='lib/renpy.zip',
|
||||
)
|
||||
@@ -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.
|
||||
@@ -0,0 +1,60 @@
|
||||
# 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 -1:
|
||||
|
||||
# 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.
|
||||
$ vpunch = Move((0, 10), (0, -10), .10, bounce=True, repeat=True, delay=.25)
|
||||
$ hpunch = Move((15, 0), (-15, 0), .10, bounce=True, repeat=True, delay=.25)
|
||||
|
||||
# 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,437 @@
|
||||
# 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' ),
|
||||
]
|
||||
|
||||
# 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
|
||||
|
||||
# A small amount of padding.
|
||||
library.padding = 2
|
||||
|
||||
# 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
|
||||
|
||||
######################################################################
|
||||
# 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")))
|
||||
|
||||
ui.window(style='gm_root_window')
|
||||
ui.null()
|
||||
|
||||
if not screen:
|
||||
return
|
||||
|
||||
ui.window(style='gm_nav_window')
|
||||
ui.vbox(focus='gm_nav')
|
||||
|
||||
for key, label, clicked, enabled in library.game_menu:
|
||||
|
||||
disabled = False
|
||||
|
||||
if not eval(enabled):
|
||||
disabled = True
|
||||
clicked = None
|
||||
|
||||
_button_factory(label, "gm_nav",
|
||||
selected=(key==screen),
|
||||
disabled=disabled,
|
||||
clicked=clicked)
|
||||
|
||||
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)
|
||||
|
||||
# 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(padding=library.padding)
|
||||
|
||||
if not library.disable_thumbnails:
|
||||
ui.null(width=library.thumbnail_width,
|
||||
height=library.thumbnail_height)
|
||||
|
||||
ui.text(name + ". ", style='file_picker_old')
|
||||
ui.text(_("Empty Slot."), style='file_picker_empty_slot')
|
||||
ui.close()
|
||||
|
||||
# This renders a slot with a file in it, in the file picker.
|
||||
def _render_savefile(name, info, newest):
|
||||
|
||||
image, extra = info
|
||||
|
||||
ui.button(style='file_picker_entry',
|
||||
clicked=ui.returns(("return", (name, True))))
|
||||
|
||||
ui.hbox(padding=library.padding)
|
||||
|
||||
if not library.disable_thumbnails:
|
||||
ui.add(image)
|
||||
|
||||
if name == newest:
|
||||
ui.text(name + ". ", style='file_picker_new')
|
||||
else:
|
||||
ui.text(name + ". ", style='file_picker_old')
|
||||
|
||||
|
||||
ui.text(extra, style='file_picker_extra_info')
|
||||
|
||||
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()
|
||||
|
||||
# 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)
|
||||
|
||||
ui.window(style='file_picker_window')
|
||||
ui.vbox() # whole thing.
|
||||
|
||||
if not library.disable_file_pager:
|
||||
|
||||
# Draw the navigation.
|
||||
ui.hbox(padding=library.padding * 10, 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)
|
||||
|
||||
# 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, nav=True):
|
||||
|
||||
_game_nav(screen)
|
||||
|
||||
ui.window(style='yesno_window')
|
||||
ui.vbox(library.padding * 10, xpos=0.5, xanchor='center', ypos=0.5, yanchor='center')
|
||||
|
||||
_label_factory(message, "yesno", xpos=0.5, xanchor='center')
|
||||
|
||||
ui.grid(5, 1, xfill=True)
|
||||
|
||||
# The extra nulls are because we want equal whitespace surrounding
|
||||
# the two buttons. It should work as long as we have xfill=True
|
||||
ui.null()
|
||||
_button_factory("Yes", 'yesno', clicked=ui.returns(True), xpos=0.5, xanchor='center')
|
||||
ui.null()
|
||||
_button_factory("No", 'yesno', clicked=ui.returns(False), xpos=0.5, xanchor='center')
|
||||
ui.null()
|
||||
|
||||
ui.close()
|
||||
ui.close()
|
||||
|
||||
return _game_interact()
|
||||
|
||||
def _show_exception(title, 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()
|
||||
|
||||
def _quit_prompt(screen="quit"):
|
||||
|
||||
def prompt():
|
||||
return _yesno_prompt(screen, "Are you sure you want to quit the game?")
|
||||
|
||||
if renpy.invoke_in_new_context(prompt):
|
||||
renpy.quit()
|
||||
else:
|
||||
return
|
||||
|
||||
def _mainmenu_prompt(screen="mainmenu"):
|
||||
|
||||
def prompt():
|
||||
return _yesno_prompt(screen, "Are you sure you want to return to the main menu?\nThis will end your game.")
|
||||
|
||||
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.
|
||||
label _enter_menu:
|
||||
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
|
||||
|
||||
# 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:
|
||||
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.
|
||||
|
||||
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 a new game will end your current game.\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,200 @@
|
||||
# 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.
|
||||
|
||||
# Used to store library settings.
|
||||
library = object()
|
||||
|
||||
# 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 = { }
|
||||
|
||||
# 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]
|
||||
else:
|
||||
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():
|
||||
renpy.screenshot("screenshot.bmp")
|
||||
|
||||
def invoke_game_menu():
|
||||
renpy.play(library.enter_sound)
|
||||
renpy.call_in_new_context('_game_menu')
|
||||
|
||||
def toggle_skipping():
|
||||
config.skipping = not config.skipping
|
||||
|
||||
# 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,
|
||||
game_menu = invoke_game_menu,
|
||||
hide_windows = renpy.curried_call_in_new_context("_hide_windows")
|
||||
)
|
||||
|
||||
config.underlay = [ km ]
|
||||
|
||||
|
||||
# The skip indicator.
|
||||
python hide:
|
||||
|
||||
def skip_indicator():
|
||||
|
||||
if config.allow_skipping and library.skip_indicator:
|
||||
|
||||
ui.conditional("config.skipping")
|
||||
ui.text(_("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
|
||||
|
||||
|
||||
|
||||
# Random nice things to have.
|
||||
init:
|
||||
$ centered = Character(None, what_style="centered_text", window_style="centered_window")
|
||||
image text = renpy.ParameterizedText(style="centered_text")
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# 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:
|
||||
|
||||
# The contents of the main menu.
|
||||
library.main_menu = [
|
||||
( "Start Game", "start" ),
|
||||
( "Continue Game", ui.jumps("_load_screen") ),
|
||||
( "Preferences", ui.jumps("_prefs_screen") ),
|
||||
( "Quit Game", ui.jumps("_quit") ),
|
||||
]
|
||||
|
||||
# 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
|
||||
|
||||
$ 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))
|
||||
ui.keymousebehavior()
|
||||
|
||||
ui.window(style='mm_root_window')
|
||||
ui.fixed()
|
||||
|
||||
ui.window(style='mm_menu_window')
|
||||
ui.vbox()
|
||||
|
||||
for text, clicked in library.main_menu:
|
||||
|
||||
if isinstance(clicked, basestring):
|
||||
clicked = ui.jumpsoutofcontext(clicked)
|
||||
|
||||
_button_factory(text, "mm", clicked=clicked)
|
||||
|
||||
ui.close()
|
||||
ui.close()
|
||||
|
||||
store._result = ui.interact(suppress_overlay = True,
|
||||
suppress_underlay = True)
|
||||
|
||||
# Computed jump to the appropriate label.
|
||||
jump _main_menu
|
||||
@@ -0,0 +1,128 @@
|
||||
# This file contains code to manage the playing of music. It's here,
|
||||
# in an rpy file, because we now optionally let the user override all
|
||||
# of this to implement his or her own music system, using calls to
|
||||
# various functions found in audio.
|
||||
|
||||
init -1000:
|
||||
python hide:
|
||||
|
||||
# config.debug_sound = True
|
||||
|
||||
# This becomes renpy.music_start.
|
||||
def music_start(filename, loops=-1, fadeout=None):
|
||||
"""
|
||||
This starts music playing. If a music track is already
|
||||
playing, this pauses that music track in favor of this
|
||||
one.
|
||||
|
||||
@param filename: The file that the music will be played from.
|
||||
This is relative to the game directory, and must be a real
|
||||
file (so it cannot be stored in an archive).
|
||||
|
||||
@param loops: The number of times the music will loop
|
||||
after it finishes playing for the first time. This is
|
||||
made somewhat less accurate by rollback and loading. If
|
||||
this number is less than zero, the song will loop
|
||||
forever.
|
||||
|
||||
@param fadeout: If this parameter is not None, it is
|
||||
interpreted as a time in seconds, which gives how long
|
||||
it will take to fade out the currently playing music.
|
||||
"""
|
||||
|
||||
if loops >= 0:
|
||||
loops += 1
|
||||
|
||||
ctx = renpy.context()
|
||||
ctx._music_name = filename
|
||||
ctx._music_loops = loops
|
||||
|
||||
if fadeout and audio.music_enabled() and not config.skipping:
|
||||
audio.music_fadeout(fadeout)
|
||||
|
||||
def music_stop(fadeout=None):
|
||||
"""
|
||||
This stops the currently playing music track.
|
||||
|
||||
@param fadeout: If this parameter is not None, it is
|
||||
interpreted as a time in seconds, which gives how long
|
||||
it will take to fade out the currently playing music.
|
||||
"""
|
||||
|
||||
ctx = renpy.context()
|
||||
ctx._music_name = None
|
||||
ctx._music_loops = None
|
||||
|
||||
if fadeout and audio.music_enabled() and not config.skipping:
|
||||
audio.music_fadeout(fadeout)
|
||||
|
||||
renpy.music_start = music_start
|
||||
renpy.music_stop = music_stop
|
||||
|
||||
# This is called once for each interaction, to ensure that the
|
||||
# music is appropriate for that interaction.
|
||||
def music_interact():
|
||||
|
||||
if not audio.music_enabled():
|
||||
return
|
||||
|
||||
ctx = renpy.context()
|
||||
|
||||
if not hasattr(ctx, '_music_name'):
|
||||
ctx._music_name = None
|
||||
ctx._music_loops = None
|
||||
|
||||
playing, queued = audio.music_filenames()
|
||||
|
||||
# If music is disabled, ensure that it is stopped.
|
||||
if not _preferences.music:
|
||||
if playing:
|
||||
audio.music_stop()
|
||||
|
||||
return
|
||||
|
||||
# If we do not match what is playing, stop what's currently
|
||||
# playing and get ready to play something else.
|
||||
if ctx._music_name != playing:
|
||||
|
||||
# If we're not playing anything, immediately start the
|
||||
# new track by calling music_end_event.
|
||||
if not playing:
|
||||
config.music_end_event()
|
||||
|
||||
# Otherwise, we will start a fade to the new music if no
|
||||
# such fade is already in progress, and we're not skipping.
|
||||
elif not audio.music_fading() and not config.skipping:
|
||||
audio.music_fadeout(config.fade_music)
|
||||
|
||||
# This is called whenever a track of music ends, or also from the
|
||||
# above when we want to start a new track when nothing else is
|
||||
# playing.
|
||||
def music_end_event():
|
||||
|
||||
if not _preferences.music:
|
||||
return
|
||||
|
||||
ctx = renpy.context()
|
||||
playing, queued = audio.music_filenames()
|
||||
|
||||
if not hasattr(ctx, '_music_name'):
|
||||
ctx._music_name = None
|
||||
ctx._music_loops = None
|
||||
|
||||
if not ctx._music_name:
|
||||
return
|
||||
|
||||
if not playing and ctx._music_loops:
|
||||
ctx._music_loops -= 1
|
||||
audio.music_play(ctx._music_name)
|
||||
|
||||
if not queued and ctx._music_loops:
|
||||
ctx._music_loops -= 1
|
||||
audio.music_queue(ctx._music_name)
|
||||
|
||||
config.interact_callbacks.append(music_interact)
|
||||
config.music_end_event = music_end_event
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
# 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 = { }
|
||||
|
||||
# If true, the preference choices will be arraigned in an
|
||||
# hbox.
|
||||
library.hbox_pref_choices = False
|
||||
|
||||
class _Preference(object):
|
||||
"""
|
||||
This is a class that's used to represent a preference that
|
||||
may be shown to the user.
|
||||
"""
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
ui.window(style='prefs_pref')
|
||||
ui.vbox()
|
||||
|
||||
_label_factory(self.name, "prefs")
|
||||
|
||||
cur = getattr(self.base, self.field)
|
||||
|
||||
if library.hbox_pref_choices:
|
||||
ui.hbox(style='prefs_hbox')
|
||||
|
||||
for name, value in values:
|
||||
|
||||
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 _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
|
||||
|
||||
def render_preference(self):
|
||||
|
||||
if not renpy.eval(self.cond):
|
||||
return
|
||||
|
||||
ui.window(style='prefs_pref')
|
||||
ui.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()
|
||||
|
||||
|
||||
|
||||
|
||||
python hide:
|
||||
|
||||
# Enablers for some preferences.
|
||||
library.has_music = True
|
||||
library.has_sound = True
|
||||
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)
|
||||
|
||||
pc3 = _PreferenceSpinner('Text Speed (CPS)', 'text_cps',
|
||||
0, 500, 10, 'library.has_cps',
|
||||
render=cps_render)
|
||||
|
||||
def afm_render(n):
|
||||
if n == 0:
|
||||
return "Disabled"
|
||||
else:
|
||||
return str(n)
|
||||
|
||||
pc4 = _PreferenceSpinner('Auto Forward Time', 'afm_time',
|
||||
0, 60, 1, 'library.has_afm',
|
||||
render=afm_render)
|
||||
|
||||
# Right
|
||||
|
||||
pr1 = _Preference('Music', 'music', [
|
||||
('Enabled', True, 'library.has_music'),
|
||||
('Disabled', False, 'library.has_music'),
|
||||
])
|
||||
|
||||
pr2 = _Preference('Sound Effects', 'sound', [
|
||||
('Enabled', True, 'library.has_sound'),
|
||||
('Disabled', False, 'library.has_sound'),
|
||||
])
|
||||
|
||||
library.preferences['prefs_left'] = [ pl1, pl2 ]
|
||||
library.preferences['prefs_center'] = [ pc1, pc2, pc3, pc4 ]
|
||||
library.preferences['prefs_right'] = [ pr1, pr2 ]
|
||||
|
||||
|
||||
|
||||
label _prefs_screen:
|
||||
|
||||
python hide:
|
||||
|
||||
_game_nav("prefs")
|
||||
|
||||
ui.window(style='prefs_window')
|
||||
ui.fixed()
|
||||
|
||||
|
||||
for style, prefs in library.preferences.iteritems():
|
||||
|
||||
ui.vbox(library.padding * 3, style=style)
|
||||
for i in prefs:
|
||||
i.render_preference()
|
||||
ui.close()
|
||||
|
||||
ui.close()
|
||||
|
||||
_game_interact()
|
||||
|
||||
jump _prefs_screen
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,488 @@
|
||||
# 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.create('default', None,
|
||||
'The default style that all styles inherit from.')
|
||||
|
||||
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)
|
||||
|
||||
# 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, 128)
|
||||
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 = 0
|
||||
style.default.ypos = 0
|
||||
style.default.xanchor = 'left'
|
||||
style.default.yanchor = 'top'
|
||||
style.default.xmaximum = None
|
||||
style.default.ymaximum = None
|
||||
|
||||
# Sound properties.
|
||||
style.default.sound = None
|
||||
|
||||
# The base style for the large windows.
|
||||
style.create('window', 'default',
|
||||
'(window, placement) The base style for the windows that contain dialogue, thoughts, and menus.')
|
||||
|
||||
style.window.background = 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'
|
||||
|
||||
# This style controls the default placement of images on the screen.
|
||||
|
||||
style.create('image_placement', 'default',
|
||||
'This style is used to control the default placement of images on the screen.')
|
||||
|
||||
style.image_placement.xpos = 0.5
|
||||
style.image_placement.ypos = 1.0
|
||||
style.image_placement.xanchor = 'center'
|
||||
style.image_placement.yanchor = 'bottom'
|
||||
|
||||
# Styles that are used for dialogue.
|
||||
|
||||
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.say_label.bold = True
|
||||
|
||||
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, position) The default style for windows containing dialogue and thoughts.')
|
||||
|
||||
|
||||
# Styles that are used for menus.
|
||||
|
||||
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.menu_choice.hover_color = (255, 255, 0, 255) # yellow
|
||||
style.menu_choice.activate_color = (255, 255, 0, 255) # yellow
|
||||
style.menu_choice.idle_color = (0, 255, 255, 255) # cyan
|
||||
|
||||
style.create('menu_window', 'window',
|
||||
'(window, position) The default style for windows containing a menu.')
|
||||
|
||||
# Styles that are used by input widgets.
|
||||
style.create('input_text', 'default',
|
||||
'(text) The style used for the text of an input box.')
|
||||
|
||||
style.input_text.color = (255, 255, 0, 255)
|
||||
|
||||
style.create('input_prompt', 'default',
|
||||
'(text) The style used for the prompt of an input box.')
|
||||
|
||||
style.create('input_window', 'window',
|
||||
'(window, position) The style used for the window of an input box.')
|
||||
|
||||
# Styles used by centered.
|
||||
style.create('centered_window', 'default',
|
||||
'(window) The style that is used for a "window" containing centered text.')
|
||||
|
||||
style.create('centered_text', 'default',
|
||||
'(text) The style used for centered text.')
|
||||
|
||||
style.centered_window.xpos = 0.5
|
||||
style.centered_window.xanchor = 'center'
|
||||
style.centered_window.xfill = False
|
||||
|
||||
style.centered_window.ypos = 0.5
|
||||
style.centered_window.yanchor = 'center'
|
||||
style.centered_window.yfill = False
|
||||
|
||||
style.centered_window.xpadding = 10
|
||||
|
||||
style.centered_text.textalign = 0.5
|
||||
style.centered_text.xpos = 0.5
|
||||
style.centered_text.ypos = 0.5
|
||||
style.centered_text.xanchor = 'center'
|
||||
style.centered_text.yanchor = 'center'
|
||||
|
||||
# Styles that are used by imagemaps
|
||||
style.create('imagemap', 'image_placement',
|
||||
'(position) The style that is used for imagemaps.')
|
||||
|
||||
style.create('imagemap_button', 'default',
|
||||
'(window, sound, hover) The style that is used for buttons inside imagemaps.')
|
||||
|
||||
# Styles that are used by imagebutttons.
|
||||
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.')
|
||||
|
||||
|
||||
# Styles that are used by all other Buttons.
|
||||
style.create('button', 'default',
|
||||
'(window, sound, hover) The default style used for buttons in the main and game menus.')
|
||||
|
||||
style.button.xpos = 0.5
|
||||
style.button.xanchor = 'center'
|
||||
|
||||
style.create('button_text', 'default',
|
||||
'(text, hover) The default style used for the label of a button.')
|
||||
|
||||
style.button_text.xpos = 0.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)
|
||||
|
||||
# Selected 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.')
|
||||
|
||||
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.create('bar', 'default',
|
||||
'(bar) The style that is used by default for bars.')
|
||||
|
||||
style.bar.left_bar = Solid(bright_cyan)
|
||||
style.bar.right_bar = Solid((0, 0, 0, 128))
|
||||
|
||||
# Styles that are used when laying out the main menu.
|
||||
style.create('mm_root_window', 'default',
|
||||
'(window) The style used for the root window of the main menu. This is primarily used to set a background for the main menu.')
|
||||
|
||||
style.mm_root_window.background = Solid((0, 0, 0, 255))
|
||||
style.mm_root_window.xfill = True
|
||||
style.mm_root_window.yfill = True
|
||||
|
||||
style.create('mm_menu_window', 'default',
|
||||
'(window, position) A window that contains the choices in the main menu. Change this to change the placement of these choices on the main menu screen.')
|
||||
|
||||
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'
|
||||
|
||||
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.')
|
||||
|
||||
# Styles that are used to lay out the game menu.
|
||||
|
||||
style.create('gm_root_window', 'default',
|
||||
'(window) The style used for the root window of the game menu. This is primarily used to change the background of the game menu.')
|
||||
|
||||
style.gm_root_window.background = Solid((0, 0, 0, 255))
|
||||
style.gm_root_window.xfill = True
|
||||
style.gm_root_window.yfill = True
|
||||
|
||||
style.create('gm_nav_window', 'default',
|
||||
'(window, position) The style used by a window containing buttons that allow the user to navigate through the different screens of the game menu.')
|
||||
|
||||
style.gm_nav_window.xpos = 0.9
|
||||
style.gm_nav_window.xanchor = 'right'
|
||||
style.gm_nav_window.ypos = 0.95
|
||||
style.gm_nav_window.yanchor = 'bottom'
|
||||
|
||||
|
||||
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_window', 'default',
|
||||
'(window, position) A window containing the file picker that is used to choose slots for loading and saving.')
|
||||
|
||||
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.create('file_picker_navbox', 'default',
|
||||
'(position) The position of the naviation (next/previous) buttons in the file picker.')
|
||||
|
||||
style.file_picker_navbox.xmargin = 10
|
||||
|
||||
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',
|
||||
'(position) The style of the grid containing the file picker entries.')
|
||||
|
||||
style.file_picker_grid.xfill = True
|
||||
|
||||
style.create('file_picker_entry', 'button',
|
||||
'(window, hover) The style that is used for each of the slots in the file picker.')
|
||||
|
||||
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.create('file_picker_text', 'default',
|
||||
'(text) A base style for all text that is displayed in the file picker.')
|
||||
|
||||
style.file_picker_text.size = 18
|
||||
style.file_picker_text.color = dark_cyan
|
||||
style.file_picker_text.hover_color = bright_cyan
|
||||
|
||||
style.create('file_picker_new', 'file_picker_text',
|
||||
'(text) The style that is applied to the new indicator in the file picker.')
|
||||
|
||||
style.create('file_picker_old', 'file_picker_text',
|
||||
'(text) The style that is applied to the old indicator in the file pciker.')
|
||||
|
||||
style.file_picker_new.hover_color = bright_red
|
||||
style.file_picker_new.activate_color = bright_red
|
||||
style.file_picker_new.idle_color = dark_red
|
||||
style.file_picker_new.minwidth = 30
|
||||
style.file_picker_old.minwidth = 30
|
||||
|
||||
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('yesno_label', 'default',
|
||||
'(text, position) The style used for the prompt in a yes/no dialog.')
|
||||
|
||||
style.yesno_label.color = green
|
||||
style.yesno_label.textalign = 0.5
|
||||
|
||||
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('yesno_window', 'default',
|
||||
'(window) The style of a window containing a yes/no dialogue.')
|
||||
|
||||
style.yesno_window.xfill = True
|
||||
style.yesno_window.yminimum = 0.5
|
||||
style.yesno_window.xmargin = .1
|
||||
|
||||
# Preferences
|
||||
|
||||
|
||||
style.create('prefs_pref', 'default',
|
||||
'(window, position) A window containing an individual preference.')
|
||||
|
||||
style.prefs_pref.xpos = 0.5
|
||||
style.prefs_pref.xanchor = 'center'
|
||||
style.prefs_pref.bottom_margin = 10
|
||||
|
||||
|
||||
|
||||
style.create('prefs_label', 'default',
|
||||
'(text, position) The style that is applied to the label of a block of preferences.')
|
||||
|
||||
style.prefs_label.xpos = 0.5
|
||||
style.prefs_label.xanchor = "center"
|
||||
style.prefs_label.color = green
|
||||
|
||||
style.create('prefs_hbox', 'default',
|
||||
'(position) If library.hbox_pref_choices is True, the style of the hbox containing the choices.')
|
||||
|
||||
style.prefs_hbox.xpos = 0.5
|
||||
style.prefs_hbox.xanchor = 'center'
|
||||
|
||||
style.create('prefs_button', 'button',
|
||||
'(window, hover) The style of an unselected preferences button.')
|
||||
|
||||
style.prefs_button.xpos = 0.5
|
||||
style.prefs_button.xanchor = 'center'
|
||||
|
||||
style.create('prefs_button_text', 'button_text',
|
||||
'(text, hover) The style of the text of an unselected preferences button.')
|
||||
|
||||
style.create('prefs_selected_button', 'selected_button',
|
||||
'(window, hover) The style of a selected preferences button.')
|
||||
|
||||
style.prefs_selected_button.xpos = 0.5
|
||||
style.prefs_selected_button.xanchor = 'center'
|
||||
|
||||
style.create('prefs_selected_button_text', 'selected_button_text',
|
||||
'(text, hover) The style of the text of a selected preferences button.')
|
||||
|
||||
style.create('prefs_window', 'default',
|
||||
'(window, position) A window containing all preferences.')
|
||||
|
||||
style.prefs_window.xfill=True
|
||||
style.prefs_window.ypadding = 0.05
|
||||
|
||||
style.create('prefs_left', 'default',
|
||||
'(position) The position of the left column of preferences.')
|
||||
|
||||
style.prefs_left.xanchor = 'center'
|
||||
style.prefs_left.xpos = 1.0 / 6.0
|
||||
|
||||
style.create('prefs_center', 'default',
|
||||
'(position) The position of the center column of preferences.')
|
||||
|
||||
style.prefs_center.xanchor = 'center'
|
||||
style.prefs_center.xpos = 3.0 / 6.0
|
||||
|
||||
style.create('prefs_right', 'default',
|
||||
'(position) The position of the right column of preferences.')
|
||||
|
||||
style.prefs_right.xanchor = 'center'
|
||||
style.prefs_right.xpos = 5.0 / 6.0
|
||||
|
||||
style.create('prefs_spinner', 'default',
|
||||
'(position) The position of the prefs spinner.')
|
||||
|
||||
style.prefs_spinner.xpos = 0.5
|
||||
style.prefs_spinner.xanchor = 'center'
|
||||
|
||||
style.create('prefs_spinner_label', 'prefs_label',
|
||||
'(text) This is the style that displays the value of a preference spinner.')
|
||||
|
||||
style.prefs_spinner_label.minwidth = 100
|
||||
style.prefs_spinner_label.textalign = 0.5
|
||||
|
||||
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.')
|
||||
|
||||
|
||||
|
||||
|
||||
# The skip indicator.
|
||||
|
||||
style.create('skip_indicator', 'default',
|
||||
'(text, position) The style of the text that is used to indicate that skipping is in progress.')
|
||||
|
||||
style.skip_indicator.xpos = 10
|
||||
style.skip_indicator.ypos = 10
|
||||
|
||||
|
||||
# Styles used by internal error messages.
|
||||
|
||||
style.create('error_window', 'default',
|
||||
'(window) The style of the window containing internal 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.create('error_title', 'default',
|
||||
'(text) The style of the text containing the title of an error message.')
|
||||
|
||||
style.error_title.color = (255, 128, 128, 255)
|
||||
|
||||
style.create('error_body', 'default',
|
||||
'(text) The style of the body of an error message.')
|
||||
|
||||
style.error_body.color = (128, 128, 255, 255)
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
#!/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
|
||||
|
||||
dirname = os.path.dirname(sys.argv[0])
|
||||
|
||||
if dirname:
|
||||
os.chdir(dirname)
|
||||
|
||||
# Add the path to the module.
|
||||
sys.path.append("module")
|
||||
|
||||
def main():
|
||||
|
||||
name = os.path.basename(sys.argv[0])
|
||||
|
||||
if name.find(".") != -1:
|
||||
name = name[:name.find(".")]
|
||||
|
||||
if name.find("_") != -1:
|
||||
name = name[name.find("_") + 1:]
|
||||
|
||||
if os.path.isdir(name):
|
||||
game = name
|
||||
else:
|
||||
game = "game"
|
||||
|
||||
op = optparse.OptionParser()
|
||||
op.add_option('--game', dest='game', default=game,
|
||||
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 |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 94 KiB |
@@ -1,332 +1,160 @@
|
||||
#!/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):
|
||||
|
||||
print source, "->", dest
|
||||
|
||||
sf = file(source, "rb")
|
||||
df = file(dest, "wb")
|
||||
|
||||
df.write(license)
|
||||
|
||||
data = sf.read()
|
||||
if dest.endswith(".txt") or dest.endswith(".py") or dest.endswith(".rpy") or dest.endswith(".bat"):
|
||||
if dos:
|
||||
data = dosify(data)
|
||||
|
||||
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)
|
||||
|
||||
def zip_rapt_symbols(destination):
|
||||
"""
|
||||
Zips up the rapt symbols.
|
||||
"""
|
||||
for dirpath, dirnames, filenames in os.walk(source):
|
||||
|
||||
import zipfile
|
||||
if "/saves" 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 "/CVS" 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)
|
||||
if "/.svn" in dirpath:
|
||||
continue
|
||||
|
||||
zf.close()
|
||||
reldir = dirpath[len(source):]
|
||||
dstrel = dest + "/" + reldir
|
||||
|
||||
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.
|
||||
"""
|
||||
for i in dirnames:
|
||||
if i == "CVS":
|
||||
continue
|
||||
|
||||
# True if we want to copy the line.
|
||||
copy = True
|
||||
if i == ".svn":
|
||||
continue
|
||||
|
||||
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)
|
||||
copy_file(dirpath + "/" + i, dstrel + "/" + i, license=license)
|
||||
|
||||
|
||||
if os.path.exists(dn):
|
||||
os.unlink(dn)
|
||||
|
||||
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\n"
|
||||
|
||||
for l in lf:
|
||||
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',
|
||||
]
|
||||
|
||||
# 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.startswith(".") and not fn.endswith("~") and not fn.endswith(".mpg"))
|
||||
|
||||
copy_tree("common", target + "/common",
|
||||
should_copy = lambda fn : not fn.startswith(".") and not fn.endswith("~"))
|
||||
|
||||
copy_tree("dse", target + "/dse",
|
||||
should_copy = lambda fn : not fn.startswith(".") and not fn.endswith("~"))
|
||||
|
||||
copy_tree("extras", target + "/extras",
|
||||
should_copy = lambda fn : not fn.startswith(".") and not fn.endswith("~"))
|
||||
|
||||
copy_tree("scripts", target + "/scripts",
|
||||
should_copy = lambda fn : not fn.startswith(".") and not fn.endswith("~"))
|
||||
|
||||
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)
|
||||
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")
|
||||
|
||||
cp("module/README.txt")
|
||||
cp("module/_renpy.pyx")
|
||||
cp("module/_renpy.c")
|
||||
cp("module/core.c")
|
||||
cp("module/renpy.h")
|
||||
cp("module/setup.py")
|
||||
cp("module/setup_mac.py")
|
||||
cp("module/setup_win32.py")
|
||||
|
||||
|
||||
|
||||
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,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,107 @@
|
||||
Releasing Ren'Py Games
|
||||
|
||||
PyTom <pytom@bishoujo.us>
|
||||
|
||||
Updated: 2005-05-28
|
||||
|
||||
|
||||
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? 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.
|
||||
|
||||
You'll also want to run the lint.bat tool. 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.
|
||||
|
||||
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 PE 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.
|
||||
|
||||
If you haven't done so already, you may want to rename the
|
||||
executable. This will also allow you to rename the game directory. In
|
||||
the case of moonlight walks, the executable was named moonlight.exe
|
||||
and the game directory was moonlight. The python file run_game.py was
|
||||
renamed moonlight.py. I'll use the name moonlight as a placeholder for
|
||||
the name of your game throughout the rest of this document.
|
||||
|
||||
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.
|
||||
|
||||
moonlight/ [all] - The game directory containing your scripts and
|
||||
data. You probably do not want to distribute the saves/ directory
|
||||
inside here, so remove that first.
|
||||
|
||||
moonlight.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.
|
||||
|
||||
|
||||
If you're making a python or cross-platform distribution, remember
|
||||
that non-windows platforms have case-sensitive filenames. If you need
|
||||
help testing, I can help with that. (This caveat also goes for data
|
||||
files, like images and music.)
|
||||
|
||||
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!
|
||||
@@ -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,158 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
|
||||
import inspect
|
||||
|
||||
sys.path.append('..')
|
||||
sys.path.append('../module')
|
||||
import renpy
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
f = file(sys.argv[1])
|
||||
s = f.read()
|
||||
f.close()
|
||||
|
||||
os.chdir("..")
|
||||
|
||||
|
||||
try:
|
||||
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>
|
||||
@@ -0,0 +1,24 @@
|
||||
4 - With Ren'Py 4.8.8
|
||||
---------------------
|
||||
|
||||
Added a static check to ensure that the label referred to by an event
|
||||
exists.
|
||||
|
||||
3
|
||||
-
|
||||
|
||||
Added old_day_planner.
|
||||
|
||||
Now, the day planners will not let the user continue unless every
|
||||
period has been assigned a valid value.
|
||||
|
||||
|
||||
2 - With Ren'Py 4.8.7
|
||||
---------------------
|
||||
|
||||
Added from clauses to every call.
|
||||
|
||||
1
|
||||
-
|
||||
|
||||
Initial release.
|
||||
@@ -0,0 +1,149 @@
|
||||
# This contains code for the new day planner. You probably
|
||||
# don't want to change this file, but it might make sense to
|
||||
# change many of the variables or styles defined here from
|
||||
# other files.
|
||||
|
||||
init -100:
|
||||
|
||||
python:
|
||||
|
||||
# A window placed behind the day planner (empty).
|
||||
style.create('dp_window', 'window')
|
||||
style.dp_window.ypos = 120
|
||||
style.dp_window.yanchor = 'top'
|
||||
|
||||
# The grid containing the three choices in the day planner.
|
||||
style.create('dp_grid', 'default')
|
||||
style.dp_grid.xfill = True
|
||||
|
||||
# Windows containing the groups of choices in the day planner.
|
||||
style.create('dp_choice', 'default')
|
||||
style.dp_choice.xpos = 0.5
|
||||
style.dp_choice.xanchor = 'center'
|
||||
|
||||
# Action buttons.
|
||||
style.create('dp_button', 'button')
|
||||
style.create('dp_selected_button', 'selected_button')
|
||||
style.create('dp_button_text', 'button_text')
|
||||
style.create('dp_selected_button_text', 'selected_button_text')
|
||||
|
||||
style.create('dp_done_button', 'button')
|
||||
style.create('dp_done_button_text', 'button_text')
|
||||
|
||||
style.dp_done_button.ypos = 0.95
|
||||
style.dp_done_button.yanchor = 'bottom'
|
||||
|
||||
# Labels.
|
||||
style.create('dp_label', 'default')
|
||||
style.dp_label.xpos = 0.5
|
||||
style.dp_label.xanchor = 'center'
|
||||
|
||||
# The amount of padding between the label and the action
|
||||
# in a choice.
|
||||
dp_padding = 12
|
||||
|
||||
# The amount of padding between the choices and done.
|
||||
dp_done_padding = 32
|
||||
|
||||
# The title of the done button.
|
||||
dp_done_title = "Done Planning Day"
|
||||
|
||||
python:
|
||||
|
||||
# A list of the periods of the day that are present in the
|
||||
# day planner. This can be changed at runtime.
|
||||
dp_period_names = [ "Morning", "Afternoon", "Evening" ]
|
||||
|
||||
# A list of variables that will take the selected choices
|
||||
# for each of the periods named above.
|
||||
dp_period_vars = [ "morning_act",
|
||||
"afternoon_act",
|
||||
"evening_act", ]
|
||||
|
||||
# A map from the period names to a list of the activities
|
||||
# available for that period. This may be conputed before
|
||||
# each call to day_planner. Each entry is a tuple, where
|
||||
# the first element is the name shown to the user, and the
|
||||
# second is the name assigned to the variable for that
|
||||
# period.
|
||||
dp_period_acts = {
|
||||
'Morning': [
|
||||
( 'Attend Class', "class" ),
|
||||
( 'Cut Class', "cut"),
|
||||
],
|
||||
|
||||
'Afternoon': [
|
||||
( 'Study', "study" ),
|
||||
( 'Hang Out', "hang" ),
|
||||
],
|
||||
|
||||
'Evening' : [
|
||||
( 'Exercise', "exercise" ),
|
||||
( 'Play Games', "play" ),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
|
||||
# We assume that the various period variables have been assigned
|
||||
# default values by this point.
|
||||
label day_planner:
|
||||
|
||||
call show_stats from _call_show_stats_1
|
||||
|
||||
python hide:
|
||||
|
||||
renpy.choice_for_skipping()
|
||||
|
||||
ui.window(style='dp_window')
|
||||
ui.vbox(dp_done_padding)
|
||||
|
||||
ui.grid(len(dp_period_names), 1, xfill=True, style='dp_grid')
|
||||
|
||||
# True iff every period has a valid value.
|
||||
can_continue = True
|
||||
|
||||
for period, var in zip(dp_period_names, dp_period_vars):
|
||||
|
||||
ui.window(style='dp_choice')
|
||||
ui.vbox()
|
||||
|
||||
_label_factory(period, "dp")
|
||||
ui.null(height=dp_padding)
|
||||
|
||||
valid_value = False
|
||||
|
||||
for label, value in dp_period_acts[period]:
|
||||
|
||||
def clicked(var=var, value=value):
|
||||
setattr(store, var, value)
|
||||
return True
|
||||
|
||||
valid_value |= getattr(store, var) == value
|
||||
|
||||
_button_factory(label, "dp",
|
||||
selected = getattr(store, var) == value,
|
||||
clicked=clicked)
|
||||
|
||||
can_continue &= valid_value
|
||||
|
||||
ui.close()
|
||||
|
||||
ui.close()
|
||||
|
||||
if valid_value:
|
||||
clicked = lambda : False
|
||||
else:
|
||||
clicked = None
|
||||
|
||||
_button_factory(dp_done_title, "dp_done",
|
||||
clicked = clicked )
|
||||
|
||||
ui.close()
|
||||
|
||||
|
||||
if ui.interact():
|
||||
jump day_planner
|
||||
|
||||
return
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
# The Ren'Py/DSE event dispatcher. This file contains the code that
|
||||
# actually supports the running of events. Specifically, it contains
|
||||
# code that determines which events are available, which events can
|
||||
# run, and to actually run the events that should be run during a
|
||||
# given period.
|
||||
|
||||
# This isn't really intended to be user-changable.
|
||||
|
||||
init -100:
|
||||
python:
|
||||
|
||||
# A list of all of the events that the system knowns about,
|
||||
# it's filtered to determine which events should run when.
|
||||
all_events = [ ]
|
||||
|
||||
# The base class for events. When constructed, an event
|
||||
# automatically adds itself to all_events.
|
||||
#
|
||||
# The first parameter for this is a unique name for the event,
|
||||
# which is also used as the label that is called when the
|
||||
# event executes.
|
||||
#
|
||||
# All other parameters are expressions. These expressions can
|
||||
# be either strings, or objects having an eval method. Many
|
||||
# interesting objects are given below.
|
||||
#
|
||||
# Keyword arguments are also kept on the object. Currently,
|
||||
# there is one useful keyword argument, priority. This
|
||||
# controls the order in which events are in the event list.
|
||||
# (Events with lower priority number are evaluated first. If a
|
||||
# priority is not specified, it's 100.)
|
||||
class event(object):
|
||||
|
||||
def __repr__(self):
|
||||
return '<event ' + self.name + '>'
|
||||
|
||||
def __init__(self, name, *args, **kwargs):
|
||||
|
||||
self.name = name
|
||||
|
||||
exprs = [ ]
|
||||
|
||||
for i in args:
|
||||
if isinstance(i, basestring):
|
||||
exprs.append(event.evaluate(i))
|
||||
else:
|
||||
exprs.append(i)
|
||||
|
||||
self.exprs = exprs
|
||||
|
||||
self.priority = kwargs.get('priority', 100)
|
||||
|
||||
all_events.append(self)
|
||||
|
||||
# Checks to see if this event is valid. It's calles with
|
||||
# a list of events that have already checked out to be
|
||||
# True, and returns True if this event checks out.
|
||||
def check(self, valid):
|
||||
|
||||
for i in self.exprs:
|
||||
if not i.eval(self.name, valid):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
# The base class for all of the event checks given below.
|
||||
class event_check(object):
|
||||
|
||||
def __invert__(self):
|
||||
return event.false(self)
|
||||
|
||||
def __and__(self, other):
|
||||
return event.and_op(self, other)
|
||||
|
||||
def __or__(self, other):
|
||||
return event.or_op(self, other)
|
||||
|
||||
|
||||
# This evaluates the expression given as an argument, and the
|
||||
# returns true if it evaluates to true.
|
||||
class evaluate(event_check):
|
||||
|
||||
def __init__(self, expr):
|
||||
self.expr = expr
|
||||
|
||||
def eval(self, name, valid):
|
||||
return eval(self.expr)
|
||||
|
||||
# If present as a condition to an event, an object of this
|
||||
# type ensures that the event will only execute once.
|
||||
class once(event_check):
|
||||
def eval(self, name, valid):
|
||||
return name not in events_executed
|
||||
|
||||
# Returns True if no event of higher priority can execute,
|
||||
# and false otherwise. In general, solo events should be
|
||||
# the lowest priority, and are run if nothing else is.
|
||||
class solo(event_check):
|
||||
def eval(self, name, valid):
|
||||
|
||||
# True if valid is empty.
|
||||
return not valid
|
||||
|
||||
# Returns True if the given events have happend already,
|
||||
# at any time in the game. False if at least one hasn't
|
||||
# happened yet.
|
||||
class happened(event_check):
|
||||
def __init__(self, *events):
|
||||
self.events = events
|
||||
|
||||
def eval(self, name, valid):
|
||||
for i in self.events:
|
||||
if i not in events_executed:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
# Evaluates its argument, and returns true if it is false
|
||||
# and vice-versa.
|
||||
class false(event_check):
|
||||
def __init__(self, cond):
|
||||
self.cond = cond
|
||||
|
||||
def eval(self, name, valid):
|
||||
return not self.cond.eval(name, valid)
|
||||
|
||||
# Handles the and operator.
|
||||
class and_op(event_check):
|
||||
|
||||
def __init__(self, *args):
|
||||
self.args = args
|
||||
|
||||
def eval(self, name, valid):
|
||||
for i in self.args:
|
||||
if not i.eval(name, valid):
|
||||
return False
|
||||
return True
|
||||
|
||||
# Handles the or operator.
|
||||
class or_op(event_check):
|
||||
|
||||
def __init__(self, *args):
|
||||
self.args = args
|
||||
|
||||
def eval(self, name, valid):
|
||||
for i in self.args:
|
||||
if i.eval(name, valid):
|
||||
return True
|
||||
return False
|
||||
|
||||
# Returns True if all of the events given as arguments have
|
||||
# happened, or False otherwise.
|
||||
class depends(event_check):
|
||||
def __init__(self, *events):
|
||||
self.events = events
|
||||
|
||||
def eval(self, name, valid):
|
||||
for i in self.events:
|
||||
if i not in events_executed_yesterday:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
|
||||
# The number of periods to skip.
|
||||
skip_periods = 0
|
||||
|
||||
# This returns True if the current period should be skipped,
|
||||
# or False if the current period should execute. If it returns
|
||||
# True, it decrements skip_periods.
|
||||
def check_skip_period():
|
||||
|
||||
global skip_periods
|
||||
|
||||
if skip_periods:
|
||||
skip_periods -= 1
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
# This should be called when the game starts. It inits the various
|
||||
# data structures used by the events.
|
||||
label events_init:
|
||||
|
||||
# Stores the events that have been executed.
|
||||
$ events_executed = { }
|
||||
|
||||
# Stores the events that have been executed before today.
|
||||
# (Where today is ended by a call to events_end_day.)
|
||||
$ events_executed_yesterday = { }
|
||||
|
||||
return
|
||||
|
||||
# This should called at the end of a (game) day, to let things
|
||||
# like depends_yesterday to work.
|
||||
label events_end_day:
|
||||
|
||||
$ skip_periods = 0
|
||||
|
||||
python hide:
|
||||
|
||||
# We can't skip between days.
|
||||
skip_periods = 0
|
||||
|
||||
for k in events_executed:
|
||||
events_executed_yesterday[k] = True
|
||||
|
||||
return
|
||||
|
||||
# This is called once per period, to determine, and then execute, the
|
||||
# events that should be run for that period.
|
||||
label events_run_period:
|
||||
|
||||
$ events = [ ]
|
||||
|
||||
python hide:
|
||||
|
||||
for i in all_events:
|
||||
if i.check(events):
|
||||
events.append(i.name)
|
||||
|
||||
while not check_skip_period() and events:
|
||||
|
||||
$ _event = events.pop(0)
|
||||
$ events_executed[_event] = True
|
||||
|
||||
call expression _event from call_expression_event_1
|
||||
|
||||
return
|
||||
|
||||
# If this is jumped to, it will end the current period immediately,
|
||||
# and return control to the main program.
|
||||
label events_end_period:
|
||||
|
||||
$ skip_period = 1
|
||||
|
||||
return
|
||||
|
||||
# If this is jumped to, it will end the current period and skip
|
||||
# the next period. Use this if, say, the user goes on a date that
|
||||
# takes up two time slots.
|
||||
label events_skip_period:
|
||||
|
||||
$ skip_period = 2
|
||||
return
|
||||
|
||||
|
||||
init 100:
|
||||
python:
|
||||
|
||||
# Sort all events on priority. Schwartzian transform time.
|
||||
all_events = [ (i.priority, i) for i in all_events ]
|
||||
all_events.sort()
|
||||
all_events = [ b for a, b in all_events ]
|
||||
|
||||
python hide:
|
||||
|
||||
for i in all_events:
|
||||
if not renpy.has_label(i.name):
|
||||
raise Exception("'%s' is defined as an event somewhere in the game, but no label named '%s' was defined anywhere." % (i.name, i.name))
|
||||
|
||||
@@ -0,0 +1,746 @@
|
||||
# This file contains the events that will be part of the game. It's
|
||||
# expected that the user will add and remove events as appropriate
|
||||
# for this game.
|
||||
|
||||
|
||||
# Some characters that are used in events in the game.
|
||||
init:
|
||||
$ t = Character('Teacher')
|
||||
$ gg = Character('Glasses Girl', color=(192, 255, 192, 255))
|
||||
$ sg = Character('Sporty Girl', color=(255, 255, 192, 255))
|
||||
$ bg = Character('Both Girls')
|
||||
|
||||
# First up, we define some simple events for the various actions, that
|
||||
# are run only if no higher-priority event is about to occur.
|
||||
|
||||
init:
|
||||
$ event("class", "act == 'class'", event.solo(), priority=200)
|
||||
$ event("cut", "act == 'cut'", event.solo(), priority=200)
|
||||
$ event("study", "act == 'study'", event.solo(), priority=200)
|
||||
$ event("hang", "act == 'hang'", event.solo(), priority=200)
|
||||
$ event("exercise", "act == 'exercise'", event.solo(), priority=200)
|
||||
$ event("play", "act == 'play'", event.solo(), priority=200)
|
||||
|
||||
label class:
|
||||
|
||||
"I make it to class just in time, and proceed to listen to the
|
||||
teacher droning on about a wide range of topics, none of which
|
||||
are remotely interesting."
|
||||
|
||||
return
|
||||
|
||||
label cut:
|
||||
|
||||
"I cut class, and spend the morning goofing off instead."
|
||||
$ intelligence -= 10
|
||||
|
||||
return
|
||||
|
||||
label study:
|
||||
|
||||
"I head on down to the library, and start reading about the topics
|
||||
I should have been reading about in class."
|
||||
|
||||
$ intelligence += 10
|
||||
return
|
||||
|
||||
label hang:
|
||||
|
||||
"I spend the afternoon hanging out with my friends, killing
|
||||
some time."
|
||||
|
||||
return
|
||||
|
||||
label exercise:
|
||||
|
||||
"I decide to go out for a run through the town, to keep myself in
|
||||
shape."
|
||||
|
||||
$ strength += 10
|
||||
return
|
||||
|
||||
label play:
|
||||
|
||||
"I pop a DVD into my video game console, and spend the evening
|
||||
rolling small cities up into balls."
|
||||
|
||||
$ strength -= 10
|
||||
return
|
||||
|
||||
|
||||
# Below here are special events that are triggered when certain
|
||||
# conditions are true.
|
||||
|
||||
# This is an introduction event, that runs once when we first go
|
||||
# to class.
|
||||
|
||||
init:
|
||||
$ event("introduction", "act == 'class'", event.once())
|
||||
|
||||
label introduction:
|
||||
|
||||
"I run to school, and make it to my seat just as the bell
|
||||
signalling the start of class rings."
|
||||
|
||||
t "Before we start, I have an announcement to make."
|
||||
|
||||
t "We will have two new students joining us. Girls, come on in."
|
||||
|
||||
"Two girls walk in, and stand in front of the class."
|
||||
|
||||
"They're twins."
|
||||
|
||||
"Identical twins."
|
||||
|
||||
"Identical black hair, and the same pretty face."
|
||||
|
||||
"Despite that, it's still fairly easy to tell them apart."
|
||||
|
||||
"The one on the left is wearing glasses."
|
||||
|
||||
"Not too thick, but enough to let me know she probably reads alot
|
||||
of books."
|
||||
|
||||
"If I look a little closely, I can find another difference."
|
||||
|
||||
"The one on the right probably exercises a bit more."
|
||||
|
||||
"I can tell by the muscle tone in her legs."
|
||||
|
||||
"I realize that I'm staring at her legs, and quickly look up."
|
||||
|
||||
"Suddenly, I realize that she's been talking for all this town."
|
||||
|
||||
sg "... to this town. And we hope to be friends with all of you."
|
||||
|
||||
sg "Well, that's about it. Sis, do you have anything to say?"
|
||||
|
||||
"The girl with glasses pauses for a second, and then quickly says:"
|
||||
|
||||
gg "{size=-4}It's good to meet you all.{/size}"
|
||||
|
||||
"She stops, and goes back to not saying anything."
|
||||
|
||||
t "Well, if that's all, you can take your seats and we can start
|
||||
the class."
|
||||
|
||||
"They do, and our teacher begins his lecture."
|
||||
|
||||
"I don't think anyone pays much attention to it, however."
|
||||
|
||||
return
|
||||
|
||||
|
||||
# These are the events with glasses girl.
|
||||
|
||||
init:
|
||||
# The glasses girl is studying in the library, but we do not
|
||||
# talk to her.
|
||||
#
|
||||
#
|
||||
$ event("gg_studying",
|
||||
|
||||
# This takes place when the action is 'study'.
|
||||
"act == 'study'",
|
||||
|
||||
# This will only take place if no higher-priority
|
||||
# event will occur.
|
||||
event.solo(),
|
||||
|
||||
# This takes place at least one day after seeing the
|
||||
# introduction event.
|
||||
event.depends("introduction"),
|
||||
|
||||
# This takes priority over the study event.
|
||||
priority=190)
|
||||
|
||||
# She asks to borrow our pen.
|
||||
$ event("borrow_pen",
|
||||
|
||||
# This takes place when we go to study, and we have an int
|
||||
# >= 50.
|
||||
"act == 'study' and intelligence >= 50",
|
||||
|
||||
# It runs only once.
|
||||
event.once(),
|
||||
|
||||
# It requires the introduction event to have run at least
|
||||
# one day before.
|
||||
event.depends("introduction"))
|
||||
|
||||
# After the pen, she smiles when she sees us.
|
||||
$ event("gg_smiling", "act == 'study'",
|
||||
event.solo(), event.depends("borrow_pen"),
|
||||
priority = 180)
|
||||
|
||||
# The bookslide.
|
||||
$ event("bookslide", "act == 'study' and intelligence == 100",
|
||||
event.once(), event.depends("borrow_pen"))
|
||||
|
||||
# She makes us cookies.
|
||||
$ event("cookies", "act == 'study'",
|
||||
event.once(), event.depends("bookslide"))
|
||||
|
||||
# Her solo ending.
|
||||
$ event("gg_confess", "act == 'class'",
|
||||
event.once(), event.depends("cookies"))
|
||||
|
||||
|
||||
label gg_studying:
|
||||
|
||||
"I head to the library, to get some studying done."
|
||||
|
||||
"The glasses girl is there, but she's busy reading a book, taking
|
||||
notes as she does so."
|
||||
|
||||
"I decide not to disturb her, and instead start reading my own
|
||||
book."
|
||||
|
||||
$ intelligence += 10
|
||||
|
||||
return
|
||||
|
||||
label borrow_pen:
|
||||
|
||||
"I head to the library, to get some studying done."
|
||||
|
||||
"The glasses girl is there, but she's busy reading a book."
|
||||
|
||||
"I decide not to disturb her, and instead start reading my own
|
||||
book."
|
||||
|
||||
"Suddenly, I feel a tap on my shoulder."
|
||||
|
||||
"I look up, and see the glasses girl standing right next to me."
|
||||
|
||||
gg "Excuse me, but can I borrow your pen?"
|
||||
|
||||
gg "Mine ran out of ink."
|
||||
|
||||
"I dig through my bag, to find the pen I had stashed there."
|
||||
|
||||
"While I'm looking, I point out that she seems to come to the
|
||||
library alot."
|
||||
|
||||
gg "Hm... I guess you're right."
|
||||
|
||||
gg "There's so much stuff here, and I want to know about it all."
|
||||
|
||||
gg "Surely, you must feel the same way, as you're here almost as
|
||||
much as I am."
|
||||
|
||||
"I don't have the heart to tell her that I'm only here to study so
|
||||
that I don't fail out."
|
||||
|
||||
"My hand brushes the pen, and I quickly pull it out and give it to
|
||||
her."
|
||||
|
||||
gg "Thank you."
|
||||
|
||||
"She says, and she goes back to studying."
|
||||
|
||||
return
|
||||
|
||||
label gg_smiling:
|
||||
|
||||
"I head to the library, to get some studying done."
|
||||
|
||||
"The glasses girl is there, and smiles at me for a second before
|
||||
turning back to her book."
|
||||
|
||||
"I decide not to disturb her, and instead start reading my own
|
||||
book."
|
||||
|
||||
$ intelligence += 10
|
||||
|
||||
return
|
||||
|
||||
label bookslide:
|
||||
|
||||
"I head to the library, to get some studying done."
|
||||
|
||||
"The glasses girl is standing right by the entrance, putting a
|
||||
book back into a bookcase containing science books."
|
||||
|
||||
"It looks quite old, and quite weak, as if it could break at any
|
||||
time."
|
||||
|
||||
"Suddenly, I hear a loud crack come from the bookcase."
|
||||
|
||||
"Without thinking, I throw myself between the girl and the
|
||||
bookcase, pushing her out of the way in the process."
|
||||
|
||||
"As the shelves fail one by one, I'm hit with large textbooks on
|
||||
topics ranging from Astronomy to Zoology."
|
||||
|
||||
"She's safe, but I'm knocked off my feet by the falling books."
|
||||
|
||||
"Before the dust even settled, the girl with glasses realized what
|
||||
happened and asked:"
|
||||
|
||||
gg "Are you alright?"
|
||||
|
||||
"I tell her that I am, all the while rubbing a bruise left by a
|
||||
particularly large Physics book."
|
||||
|
||||
gg "You saved me."
|
||||
|
||||
"She points out. I shrug... I guess I did, but it's not like I'm a
|
||||
hero or anything."
|
||||
|
||||
"She extends out her hand, I take it, and she helps me to get up."
|
||||
|
||||
gg "Wow... Um..."
|
||||
|
||||
"She doesn't know what to say."
|
||||
|
||||
"I suggest that we help clean up the mess, mostly to take her off
|
||||
the spot."
|
||||
|
||||
"She agrees, and together we begin piling the books up into neat
|
||||
piles."
|
||||
|
||||
return
|
||||
|
||||
|
||||
label cookies:
|
||||
|
||||
"I head to the library, to get some studying done."
|
||||
|
||||
"The glasses girl is there, apparently waiting for me."
|
||||
|
||||
"She's holding a package in her hands."
|
||||
|
||||
gg "Here."
|
||||
|
||||
"She says, and she hands me the package."
|
||||
|
||||
"I take it from her, and open it."
|
||||
|
||||
"It contains fresh homemade cookies. Ginger snaps, I think."
|
||||
|
||||
gg "It's to thank you."
|
||||
|
||||
"She points out... She's probably not used to this. Especially
|
||||
with guys."
|
||||
|
||||
"I take one of them, and stick it in my mouth."
|
||||
|
||||
"The taste is exquisite."
|
||||
|
||||
"It's perhaps one of the best cookies I've ever tasted."
|
||||
|
||||
"Of course it is. It's the only cookie I'd ever tasted that was
|
||||
made for me by a beutiful girl."
|
||||
|
||||
"I tell her this... that it's delicious, not the girl part."
|
||||
|
||||
"But I do let slip that if I could eat these every day, I'd be the
|
||||
happiest guy in the world."
|
||||
|
||||
"At this, she can only blush."
|
||||
|
||||
return
|
||||
|
||||
label gg_confess:
|
||||
|
||||
"I once again barely make it to class on time."
|
||||
|
||||
"I sit down, at my desk, and put some of my books into it."
|
||||
|
||||
"My hand brushes a folded sheet of paper, one I didn't remember
|
||||
putting in there."
|
||||
|
||||
"It's a girl's handwriting... \"Meet me on the roof at lunch.\""
|
||||
|
||||
"That's all it says... no signature or anything."
|
||||
|
||||
"Lunch is a few hours away, but the time passes like a blur."
|
||||
|
||||
"It's all I can do to avoid racing up to the roof... but I give it
|
||||
some time anyway."
|
||||
|
||||
"It wouldn't make sense for me to get there first."
|
||||
|
||||
"I let two minutes elapse before leaving the classroom, and then
|
||||
slowly walk the flights of stairs up to the roof."
|
||||
|
||||
"Standing there, I find the glasses girl."
|
||||
|
||||
"She's holding what looks like a homemade lunch... big enough for
|
||||
two."
|
||||
|
||||
"I look at it, then her, then remember what I had said after she
|
||||
made me the cookies."
|
||||
|
||||
"Finally, I ask her... \"Does this mean?\""
|
||||
|
||||
"She nods. It's all the confirmation I need."
|
||||
|
||||
"I sit down next to my new girlfriend... and together we start
|
||||
eating her lunch."
|
||||
|
||||
"I'm probably the happiest guy in the world."
|
||||
|
||||
"But I still have to find out her name."
|
||||
|
||||
".:. Ending 1."
|
||||
|
||||
$ renpy.full_restart()
|
||||
|
||||
|
||||
init:
|
||||
|
||||
$ event("catchme", "act == 'exercise'",
|
||||
event.depends('introduction'), event.once())
|
||||
|
||||
$ event("cantcatchme", "act == 'exercise'",
|
||||
event.depends('catchme'), event.solo(), priority=190)
|
||||
|
||||
$ event("caughtme", "act == 'exercise' and strength >= 50",
|
||||
event.depends('catchme'), event.once())
|
||||
|
||||
$ event("together", "act == 'exercise' and strength >= 50",
|
||||
event.depends('caughtme'), event.solo(), priority=180)
|
||||
|
||||
$ event("apart", "act == 'exercise' and strength < 50",
|
||||
event.depends('caughtme'), event.solo(), priority=180)
|
||||
|
||||
$ event("pothole", "act == 'exercise' and strength >= 100",
|
||||
event.depends('caughtme'), event.once())
|
||||
|
||||
$ event("dontsee", "act == 'exercise'",
|
||||
event.depends('pothole'), event.solo(), priority=170)
|
||||
|
||||
$ event("sg_confess", "act == 'class'",
|
||||
event.depends('dontsee'), event.once())
|
||||
|
||||
|
||||
label catchme:
|
||||
|
||||
"I decide to go out for a run, to keep myself in shape."
|
||||
|
||||
"As I'm running through the town, I see a girl."
|
||||
|
||||
"She's one of the twins who transferred into my class."
|
||||
|
||||
"She waves, and comes over to me."
|
||||
|
||||
sg "I didn't know you were a runner."
|
||||
|
||||
"I point out that I'm not really a runner... I just run a little
|
||||
bit at a time."
|
||||
|
||||
"I ask her if she wants to run with me for a while."
|
||||
|
||||
sg "As if! You couldn't keep up with me."
|
||||
|
||||
"I point out that I probably can."
|
||||
|
||||
sg "Well, let's see."
|
||||
|
||||
"We set off running, but she quickly pulls past me."
|
||||
|
||||
sg "See? Well, maybe we can try it again when you're a bit
|
||||
faster."
|
||||
|
||||
sg "Until then, later."
|
||||
|
||||
"Even though I'm jogging, she pulls away as if it is nothing."
|
||||
|
||||
return
|
||||
|
||||
label cantcatchme:
|
||||
|
||||
"I'm out running again, when the sporty girl catches up to me."
|
||||
|
||||
sg "Still at it?"
|
||||
|
||||
sg "Well, keep up the good work. One day you'll be as fast as me!"
|
||||
|
||||
sg "Well, maybe."
|
||||
|
||||
"She pulls out past me, and disappears into the distance. One day
|
||||
I'll catch up to her."
|
||||
|
||||
$ strength += 10
|
||||
|
||||
return
|
||||
|
||||
label caughtme:
|
||||
|
||||
"I'm out running again, when the sporty girl catches up to me."
|
||||
|
||||
sg "Still at it?"
|
||||
|
||||
sg "Well, keep up the good work. One day you'll be as fast as me!"
|
||||
|
||||
sg "Well, maybe."
|
||||
|
||||
|
||||
"Today, however, I'm not about to let this stand unchallenged."
|
||||
|
||||
"I break out into a run, and for the first time ever, I keep up
|
||||
with her."
|
||||
|
||||
"We both run, neck and neck, me keeping up with her."
|
||||
|
||||
"Finally, she starts slowing down, and we come to a stop
|
||||
together."
|
||||
|
||||
sg "Not bad."
|
||||
|
||||
"She pauses to catch her breath."
|
||||
|
||||
sg "You've been practicing, and it really shows."
|
||||
|
||||
sg "You've finally become fast enough to run with me."
|
||||
|
||||
"I nod, accepting her praise."
|
||||
|
||||
sg "We should do this more often... it's better to run with
|
||||
someone else, to keep the challenge up."
|
||||
|
||||
"I nod again."
|
||||
|
||||
sg "Well, shall we go?"
|
||||
|
||||
"I nod a third time, and we take off, running side by side."
|
||||
|
||||
$ strength += 10
|
||||
|
||||
return
|
||||
|
||||
label together:
|
||||
|
||||
"I start running, and meet up with the sporty girl as she passes
|
||||
the street in front of my house."
|
||||
|
||||
"She's still better than me... she's been running for over a mile
|
||||
before reaching this point."
|
||||
|
||||
"Still, I can keep up with her for the rest of the run. And that's
|
||||
not bad."
|
||||
|
||||
$ strength += 10
|
||||
|
||||
return
|
||||
|
||||
label apart:
|
||||
|
||||
"I start running, and meet up with the sporty girl as she passes
|
||||
the street in front of my house."
|
||||
|
||||
"I try to keep up with her, but she pulls away from me."
|
||||
|
||||
"When she's a block away, she slows down and lets me catch up."
|
||||
|
||||
sg "It's your own fault... this is what you get for not
|
||||
practicing."
|
||||
|
||||
"She's right, of course, and I redouble my efforts to try to keep
|
||||
up with her."
|
||||
|
||||
$ strength += 10
|
||||
return
|
||||
|
||||
label pothole:
|
||||
|
||||
"I start running, and meet up with the sporty girl as she passes
|
||||
the street in front of my house."
|
||||
|
||||
"We run together for several miles."
|
||||
|
||||
"I think about how much I've improved in our time together."
|
||||
|
||||
"And, although she probably won't admit it, I think she's improved
|
||||
as well."
|
||||
|
||||
"I guess a little friendly competition is usually for the best."
|
||||
|
||||
"A small yelp pulls me out of my thought."
|
||||
|
||||
sg "Ow!"
|
||||
|
||||
"The sporty girl sits down, grabbing her ankle."
|
||||
|
||||
"I ask her what happened."
|
||||
|
||||
sg "I... hit a... pothole. Twisted my... ankle."
|
||||
|
||||
"I wince in sympathy."
|
||||
|
||||
"We wait a bit. I'm not sure what to do."
|
||||
|
||||
"Finally, I ask her if she can walk on it."
|
||||
|
||||
"She tries for a bit, but then winces in pain."
|
||||
|
||||
sg "No, I don't think so."
|
||||
|
||||
"I realize that we can't stay here."
|
||||
|
||||
"And so, I crouch down and motion for her to climb up onto my
|
||||
back."
|
||||
|
||||
sg "What are you doing?"
|
||||
|
||||
"I explain that she can't stay out in the middle of the street
|
||||
forever, and she won't get any help until I can get her home."
|
||||
|
||||
"And the only way to do that is for me to carry her."
|
||||
|
||||
"She accepts this, and climbs up onto my back."
|
||||
|
||||
"She wraps her arms around my neck, and I place my hands
|
||||
underneath her to make a seat."
|
||||
|
||||
"I stand up, and start carrying her home."
|
||||
|
||||
return
|
||||
|
||||
label dontsee:
|
||||
|
||||
"I go running again."
|
||||
|
||||
"But this time, I don't see the sporty girl."
|
||||
|
||||
"I finish the course that we usually take, but it's not the same
|
||||
without her."
|
||||
|
||||
return
|
||||
|
||||
label sg_confess:
|
||||
|
||||
"I once again barely make it to class on time."
|
||||
|
||||
"I sit down, at my desk, and put some of my books into it."
|
||||
|
||||
"My hand brushes a folded sheet of paper, one I didn't remember
|
||||
putting in there."
|
||||
|
||||
"It's a girl's handwriting... \"Meet me on the roof at lunch.\""
|
||||
|
||||
"That's all it says... no signature or anything."
|
||||
|
||||
"Lunch is a few hours away, but the time passes like a blur."
|
||||
|
||||
"It's all I can do to avoid racing up to the roof... but I give it
|
||||
some time anyway."
|
||||
|
||||
"It wouldn't make sense for me to get there first."
|
||||
|
||||
"I let two minutes elapse before leaving the classroom, and then
|
||||
slowly walk the flights of stairs up to the roof."
|
||||
|
||||
"Standing there, I find the sporty girl."
|
||||
|
||||
"She's leaning on a crutch."
|
||||
|
||||
"I look at it for a second, and she notices that."
|
||||
|
||||
sg "I went to the doctor, and he gave me this."
|
||||
|
||||
sg "Looks like we won't be running together for a while."
|
||||
|
||||
"I nod."
|
||||
|
||||
sg "And that's why I asked you here."
|
||||
|
||||
sg "I couldn't stand the though of not seeing you for a few
|
||||
weeks."
|
||||
|
||||
"I search my feelings, and realize I feel the same way."
|
||||
|
||||
sg "So I thought..."
|
||||
|
||||
"She doesn't say it... she doesn't need to."
|
||||
|
||||
"We both know how we feel about each other."
|
||||
|
||||
"And with that, we went from being running partners to partners in
|
||||
a deeper sense."
|
||||
|
||||
"Now if I only knew her name..."
|
||||
|
||||
".:. Ending 2."
|
||||
|
||||
$ renpy.full_restart()
|
||||
|
||||
|
||||
init:
|
||||
|
||||
# This needs to be higher-priority than either girl's ending.
|
||||
$ event('both_confess', 'act == "class"',
|
||||
event.depends("dontsee"), event.depends("cookies"),
|
||||
event.once(), priority = 50)
|
||||
|
||||
label both_confess:
|
||||
|
||||
"I once again barely make it to class on time."
|
||||
|
||||
"I sit down, at my desk, and put some of my books into it."
|
||||
|
||||
"My hand brushes a folded sheet of paper, then another."
|
||||
|
||||
"I take the first one out, and read it."
|
||||
|
||||
"It's a girl's handwriting... \"Meet me on the roof at lunch.\""
|
||||
|
||||
"That's all it says... no signature or anything."
|
||||
|
||||
"I take a look at the second one, and it says the same thing."
|
||||
|
||||
"Sure, the handwriting is a little different, but..."
|
||||
|
||||
"Lunch is a few hours away, but the time passes like a blur."
|
||||
|
||||
"It's all I can do to avoid racing up to the roof... but I give it
|
||||
some time anyway."
|
||||
|
||||
"I let two minutes elapse before leaving the classroom, and then
|
||||
slowly walk the flights of stairs up to the roof."
|
||||
|
||||
"Standing there are the twins."
|
||||
|
||||
"Both of them."
|
||||
|
||||
"As in, the two notes came from the two twins."
|
||||
|
||||
"I ask them what they are doing there, feigning ignorance."
|
||||
|
||||
sg "Well, I invited you up here to confess to you..."
|
||||
|
||||
sg "... and then I found out that my sister here was about to do
|
||||
the same thing."
|
||||
|
||||
gg "{size=-4}...I was...{/size}"
|
||||
|
||||
sg "When we found out, we were quite shocked, but after comparing
|
||||
notes, we decide what we're going to do..."
|
||||
|
||||
"I quickly run through the possibilities in my head."
|
||||
|
||||
"The best cases involve them never talking to me again."
|
||||
|
||||
"The worst cases involve me being thrown off the roof."
|
||||
|
||||
bg "We're going to share you!"
|
||||
|
||||
"Eh?"
|
||||
|
||||
"That wasn't something I considered."
|
||||
|
||||
"Each of the girls grabs onto one of my arms."
|
||||
|
||||
"I don't know what the future holds for us..."
|
||||
|
||||
"... and I don't know if this will work out."
|
||||
|
||||
"But I do know that one day I will work up the courage to find out
|
||||
their names."
|
||||
|
||||
".:. Ending 3."
|
||||
|
||||
$ renpy.full_restart()
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
# This is the main program. This can be changed quite a bit to
|
||||
# customize it for your program... But remember what you do, so you
|
||||
# can integrate with a new version of DSE when it comes out.
|
||||
|
||||
# Declare black, so we can use it in the game,
|
||||
init:
|
||||
image black = Solid((0, 0, 0, 255))
|
||||
|
||||
# This is the entry point into the game.
|
||||
label start:
|
||||
|
||||
# Required to initialize the event engine.
|
||||
call events_init from _call_events_init_1
|
||||
|
||||
# Initialize the default values of some of the variables used in
|
||||
# the game.
|
||||
$ day = 0
|
||||
$ strength = 10
|
||||
$ intelligence = 10
|
||||
|
||||
scene black
|
||||
|
||||
|
||||
# The script here is run before any event.
|
||||
|
||||
"In case you're just tuning in, here's the story of my life to
|
||||
date."
|
||||
|
||||
"I'm a guy in the second year of high school."
|
||||
|
||||
"I'm not good at sports or school or even something as simple as
|
||||
remembering peoples names."
|
||||
|
||||
"In short, I am your usual random loser guy."
|
||||
|
||||
"And this is my story..."
|
||||
|
||||
|
||||
# We jump to day to start the first day.
|
||||
jump day
|
||||
|
||||
|
||||
# This is the label that is jumped to at the start of a day.
|
||||
label day:
|
||||
|
||||
# Increment the day it is.
|
||||
$ day += 1
|
||||
|
||||
# We may also want to compute the name for the day here, but
|
||||
# right now we don't bother.
|
||||
|
||||
"It's day %(day)d."
|
||||
|
||||
# Here, we want to set up some of the default values for the
|
||||
# day planner. In a more complicated game, we would probably
|
||||
# want to add and remove choices from the dp_ variables
|
||||
# (especially dp_period_acts) to reflect the choices the
|
||||
# user has available to him.
|
||||
|
||||
$ morning_act = None
|
||||
$ afternoon_act = None
|
||||
$ evening_act = None
|
||||
|
||||
# Now, we call the day planner, which may set the act variables
|
||||
# to new values.
|
||||
call day_planner from _call_day_planner_1
|
||||
|
||||
|
||||
# We process each of the three periods of the day, in turn.
|
||||
label morning:
|
||||
|
||||
# Tell the user what period it is.
|
||||
centered "Morning"
|
||||
|
||||
# Set these variables to appropriate values, so they can be
|
||||
# picked up by the expression in the various events defined below.
|
||||
$ period = "morning"
|
||||
$ act = morning_act
|
||||
|
||||
# Execute the events for the morning.
|
||||
call events_run_period from _call_events_run_period_1
|
||||
|
||||
# That's it for the morning, so we fall through to the
|
||||
# afternoon.
|
||||
|
||||
label afternoon:
|
||||
|
||||
# It's possible that we will be skipping the afternoon, if one
|
||||
# of the events in the morning jumped to skip_next_period. If
|
||||
# so, we should skip the afternoon.
|
||||
if check_skip_period():
|
||||
jump evening
|
||||
|
||||
# The rest of this is the same as for the morning.
|
||||
|
||||
centered "Afternoon"
|
||||
|
||||
$ period = "afternoon"
|
||||
$ act = afternoon_act
|
||||
|
||||
call events_run_period from _call_events_run_period_2
|
||||
|
||||
|
||||
label evening:
|
||||
|
||||
# The evening is the same as the afternoon.
|
||||
if check_skip_period():
|
||||
jump night
|
||||
|
||||
centered "Evening"
|
||||
|
||||
$ period = "evening"
|
||||
$ act = evening_act
|
||||
|
||||
call events_run_period from _call_events_run_period_3
|
||||
|
||||
|
||||
label night:
|
||||
|
||||
# This is now the end of the day, and not a period in which
|
||||
# events can be run. We put some boilerplate end-of-day text
|
||||
# in here.
|
||||
|
||||
centered "Night"
|
||||
|
||||
"It's getting late, so I decide to go to sleep."
|
||||
|
||||
# We call events_end_day to let it know that the day is done.
|
||||
call events_end_day from _call_events_end_day_1
|
||||
|
||||
# We force the statistics into the range 0-100.
|
||||
$ intelligence = max(intelligence, 0)
|
||||
$ intelligence = min(intelligence, 100)
|
||||
|
||||
$ strength = max(strength, 0)
|
||||
$ strength = min(strength, 100)
|
||||
|
||||
# And we jump back to day to start the next day. This goes
|
||||
# on forever, until an event ends the game.
|
||||
jump day
|
||||
|
||||
|
||||
# This is the code, that is called from the day planner, to show
|
||||
# the statistics to the user. It can actually show just about
|
||||
# anything, so the statistics are really just a suggestion.
|
||||
|
||||
label show_stats:
|
||||
|
||||
python hide:
|
||||
|
||||
# Add in a line of dialogue asking the question that's on
|
||||
# everybody's mind.
|
||||
narrator("What should I do today?", interact=False)
|
||||
|
||||
# This is a list of the statistics that we are showing to the
|
||||
# user.
|
||||
stats = [
|
||||
('Strength', 100, strength ),
|
||||
('Intelligence', 100, intelligence ),
|
||||
]
|
||||
|
||||
# This is the window that the stats are kept in, if any.
|
||||
ui.window(xpos=0,
|
||||
ypos=0,
|
||||
xanchor='left',
|
||||
yanchor='top',
|
||||
xfill=True,
|
||||
yminimum=0)
|
||||
|
||||
ui.vbox()
|
||||
|
||||
ui.text('Day %d' % day)
|
||||
ui.null(height=20)
|
||||
|
||||
for name, range, value in stats:
|
||||
|
||||
ui.hbox()
|
||||
ui.text(name, minwidth=150)
|
||||
ui.bar(600, 20, range, value, ypos=0.5, yanchor=center)
|
||||
ui.close()
|
||||
|
||||
ui.close()
|
||||
|
||||
return
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
# This file contains the old day planner, the one used in the
|
||||
# Ren'Py demo.
|
||||
|
||||
# Used in the old day planner to show the date.
|
||||
init:
|
||||
|
||||
$ dp_date = "No Date Set"
|
||||
|
||||
|
||||
# This is the old day planner, as found in the demo code,
|
||||
# reimplemented to be compatible with the new day planner.
|
||||
#
|
||||
# This isn't as well tested as the real one, given above.
|
||||
label old_day_planner:
|
||||
|
||||
# This is None to indicate that nothing is being edited, or
|
||||
# a name, var tuple to give the period that is being edited.
|
||||
$ dp_editing = None
|
||||
|
||||
label old_day_planner_cycle:
|
||||
|
||||
call show_stats
|
||||
|
||||
python hide:
|
||||
|
||||
renpy.choice_for_skipping()
|
||||
|
||||
# Period Selection Window.
|
||||
ui.window(xpos=0,
|
||||
ypos=200,
|
||||
xanchor='left',
|
||||
yanchor='top',
|
||||
xfill=False,
|
||||
xminimum=300
|
||||
)
|
||||
|
||||
ui.vbox(xpos=0.5, xanchor='center')
|
||||
ui.text(dp_date, xpos=0.5, xanchor='center', textalign=0.5)
|
||||
ui.null(height=20)
|
||||
|
||||
cannot_continue = False
|
||||
|
||||
for name, var in zip(dp_period_names, dp_period_vars):
|
||||
|
||||
def period_clicked(name=name, var=var):
|
||||
store.dp_editing = (name, var)
|
||||
return True
|
||||
|
||||
value = getattr(store, var)
|
||||
valname = ""
|
||||
|
||||
for n, v in dp_period_acts[name]:
|
||||
if v == value:
|
||||
valname = n
|
||||
|
||||
|
||||
if not valname:
|
||||
cannot_continue = True
|
||||
|
||||
face = name + ": " + valname
|
||||
|
||||
|
||||
|
||||
|
||||
_button_factory(face, "dp",
|
||||
selected = dp_editing == (name, var),
|
||||
clicked = period_clicked)
|
||||
|
||||
|
||||
ui.null(height=20)
|
||||
|
||||
if cannot_continue:
|
||||
_button_factory("Continue", "dp")
|
||||
else:
|
||||
_button_factory("Continue", "dp", clicked=ui.returns(False))
|
||||
|
||||
ui.null(height=20)
|
||||
ui.close()
|
||||
|
||||
|
||||
# Choice window.
|
||||
if dp_editing:
|
||||
|
||||
name, var = dp_editing
|
||||
|
||||
ui.window(xpos=300,
|
||||
ypos=200,
|
||||
xanchor='left',
|
||||
yanchor='top',
|
||||
xfill=False,
|
||||
xminimum=500
|
||||
)
|
||||
|
||||
ui.vbox()
|
||||
|
||||
_label_factory("What will you do in the %s?" %
|
||||
name.lower(), "dp")
|
||||
ui.null(height=20)
|
||||
|
||||
for label, value in dp_period_acts[name]:
|
||||
|
||||
def clicked(var=var, value=value):
|
||||
setattr(store, var, value)
|
||||
store.dp_editing = None
|
||||
return True
|
||||
|
||||
|
||||
|
||||
_button_factory(label, "dp",
|
||||
selected = getattr(store, var) == value,
|
||||
clicked = clicked)
|
||||
|
||||
ui.close()
|
||||
|
||||
|
||||
if ui.interact():
|
||||
jump old_day_planner_cycle
|
||||
|
||||
return
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,8 @@
|
||||
init:
|
||||
image black = Solid((0, 0, 0, 255))
|
||||
|
||||
label main_menu:
|
||||
|
||||
$ renpy.renpy.style.write_docs("doc/styles.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,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,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")))
|
||||
|
||||
|
||||
label gallery:
|
||||
|
||||
$ gallery()
|
||||
|
||||
jump _main_menu
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
# This is an implementation of Kana mode, a mode in which multiple
|
||||
# lines of dialogue are shown at once, in a fullscreen window. A
|
||||
# single line of dialogue is placed onto the screen for each mouse
|
||||
# click. Calling the clear function clears the screen, ensuring that
|
||||
# the next line will appear at the top of the sceen.
|
||||
#
|
||||
# An example of using Kana mode follows the code that implements it.
|
||||
|
||||
|
||||
# This init block contains all of the code needed to implement
|
||||
# Kana mode. You probably don't want to change this... unless you
|
||||
# do. But all of the customization is found below.
|
||||
init -100:
|
||||
python:
|
||||
|
||||
# This is a list of KanaCharacter, line of dialogue tuples.
|
||||
kana_display_list = [ ]
|
||||
|
||||
# Spacings used.
|
||||
kana_vspacing = 0
|
||||
kana_hspacing = 0
|
||||
|
||||
def clear(arg=None):
|
||||
global kana_display_list
|
||||
kana_display_list = [ ]
|
||||
|
||||
def kana_show():
|
||||
|
||||
ui.window(style='say_window')
|
||||
ui.vbox(kana_vspacing)
|
||||
|
||||
for char, what in kana_display_list:
|
||||
char.show(what)
|
||||
|
||||
ui.close()
|
||||
|
||||
ui.saybehavior()
|
||||
ui.interact()
|
||||
renpy.checkpoint()
|
||||
|
||||
# Characters and the narrator should be instances of this
|
||||
# object.
|
||||
class KanaCharacter(object):
|
||||
|
||||
def __init__(self, who,
|
||||
what_prefix='"',
|
||||
what_suffix='"',
|
||||
who_style='say_label',
|
||||
what_style='say_dialogue',
|
||||
**properties):
|
||||
|
||||
self.who = who
|
||||
self.what_prefix = what_prefix
|
||||
self.what_suffix = what_suffix
|
||||
self.who_style = who_style
|
||||
self.what_style = what_style
|
||||
self.properties = properties
|
||||
|
||||
def __call__(self, what):
|
||||
kana_display_list.append((self, what))
|
||||
kana_show()
|
||||
|
||||
def show(self, what):
|
||||
ui.hbox(kana_hspacing)
|
||||
ui.text(self.who, style=self.who_style,
|
||||
**self.properties)
|
||||
ui.text(self.what_prefix + what + self.what_suffix,
|
||||
style=self.what_style)
|
||||
ui.close()
|
||||
|
||||
|
||||
# This section updates the styles that are used by the Kana mode stuff
|
||||
# so that they work well with Kana mode. The user may want to change
|
||||
# these so that they look more appropriate.
|
||||
init:
|
||||
|
||||
# The space between lines of dialogue.
|
||||
$ kana_vspacing = 10
|
||||
$ kana_hspacing = 10
|
||||
|
||||
$ style.say_window.background = Solid((0, 0, 0, 96))
|
||||
$ style.say_window.xfill = True
|
||||
$ style.say_window.yfill = True
|
||||
$ style.say_window.xmargin = 0
|
||||
$ style.say_window.ymargin = 0
|
||||
$ style.say_window.xpadding = 20
|
||||
$ style.say_window.ypadding = 20
|
||||
|
||||
$ style.say_label.minwidth = 100
|
||||
$ style.say_label.textalign = 1.0
|
||||
|
||||
# This just makes it look nicer.
|
||||
$ style.say_dialogue.rest_indent = 9
|
||||
|
||||
# Adjust the menu to match.
|
||||
$ style.menu_window.background = Solid((0, 0, 0, 96))
|
||||
$ style.menu_window.xfill = True
|
||||
$ style.menu_window.yfill = True
|
||||
$ style.menu_window.xmargin = 0
|
||||
$ style.menu_window.ymargin = 0
|
||||
$ style.menu_window.xpadding = 20
|
||||
$ style.menu_window.ypadding = 20
|
||||
|
||||
$ style.menu.xpos = 110
|
||||
|
||||
# And now, the example. This example can't be simply run, but instead
|
||||
# should serve as a guide to how to get Kana mode working in your own
|
||||
# game.
|
||||
|
||||
# In the init block, declare the characters as KanaCharacters. We also
|
||||
# want to declare the narrator as a special KanaCharacter.
|
||||
init:
|
||||
$ p = KanaCharacter("")
|
||||
$ g = KanaCharacter("Girl:", color=(255, 128, 128, 255))
|
||||
$ narrator = KanaCharacter("",
|
||||
what_prefix='',
|
||||
what_suffix='',
|
||||
what_style='say_thought')
|
||||
|
||||
|
||||
# Now, the actual script of the example. (An excerpt from Moonlight
|
||||
# Walks.) Notice how we place calls to clear in places where the
|
||||
# we want the screen to be cleared.
|
||||
|
||||
# This text is here to serve as an example, and shouldn't be used
|
||||
# in your game.
|
||||
label example:
|
||||
|
||||
show beach2
|
||||
show mary dark wistful
|
||||
|
||||
$ clear()
|
||||
|
||||
p "What can you tell me about your parents?"
|
||||
|
||||
g "Papa and Mama both came across the ocean as settlers when they
|
||||
were just children."
|
||||
|
||||
g "Papa fought in the war. When it was over, he married Mama, and
|
||||
they used his pension to move here from the mainland, and to
|
||||
build us a house."
|
||||
|
||||
g "Together, they farmed the land, and eventually they had
|
||||
children."
|
||||
|
||||
g "I had an older sister and a younger sister. I was the middle
|
||||
child."
|
||||
|
||||
$ clear()
|
||||
|
||||
show mary dark sad
|
||||
|
||||
"She paused for a second to collect her thoughts before
|
||||
continuing. This part was taking a strain on her."
|
||||
|
||||
g "When I was ten, an epidemic hit the island."
|
||||
|
||||
g "We came down with it, and so did all of the other families."
|
||||
|
||||
g "My family was too sick to move, but our neighbors, the Millers,
|
||||
sent some of their boys to get help from the mainland."
|
||||
|
||||
"I remember thinking that Miller was the last name of my aunt and
|
||||
uncle, and wondering if they could be related to me."
|
||||
|
||||
g "I don't know what happened to them, but I never heard from them
|
||||
again."
|
||||
|
||||
show mary dark crying
|
||||
|
||||
g "It lasted a week, and then it was over. My sisters... my
|
||||
parents... they all..."
|
||||
|
||||
g "Now I'm the only one left."
|
||||
|
||||
$ clear()
|
||||
|
||||
p "I'm sorry."
|
||||
|
||||
"I didn't know what else to say to a girl that had lost her
|
||||
family, and was obviously broken up about it."
|
||||
|
||||
show mary dark sad
|
||||
|
||||
"She nodded in response, wiped her tears, and we once again
|
||||
started walking in silence."
|
||||
|
||||
menu menu_1:
|
||||
"Show me the example again.":
|
||||
jump example
|
||||
|
||||
"I'm done. Let me go.":
|
||||
return
|
||||
@@ -0,0 +1,87 @@
|
||||
# This file replaces most of the game menu navigation with overlay
|
||||
# buttons that jump directly to various parts of the game menu.
|
||||
# Right clicking hides and shows the buttons, rather than calling
|
||||
# up the game menu directly.
|
||||
|
||||
init:
|
||||
python hide:
|
||||
|
||||
# overlay_menu is an object storing information about the
|
||||
# overlay menu state.
|
||||
store.overlay_menu = object()
|
||||
overlay_menu.shown = False
|
||||
|
||||
# overlay_menu is also a new layer, containing the overlay
|
||||
# menu.
|
||||
config.layers.append("overlay_menu")
|
||||
config.overlay_layers.append("overlay_menu")
|
||||
|
||||
|
||||
# This function actually draws the overlay menu.
|
||||
def overlay_menu_func():
|
||||
if overlay_menu.shown:
|
||||
|
||||
ui.layer("overlay_menu")
|
||||
|
||||
ui.vbox(xpos=1.0, xanchor="right", ypos=0.75, yanchor="bottom")
|
||||
|
||||
def button(label, target):
|
||||
ui.textbutton(label, clicked=renpy.curried_call_in_new_context(target))
|
||||
|
||||
button("Load Game", "_game_menu_load")
|
||||
button("Save Game", "_game_menu_save")
|
||||
button("Preferences", "_game_menu_preferences")
|
||||
|
||||
ui.close()
|
||||
ui.close()
|
||||
|
||||
|
||||
config.overlay_functions.append(overlay_menu_func)
|
||||
|
||||
|
||||
# This function toggles the visibility of the overlay menu.
|
||||
def overlay_menu_toggle():
|
||||
shown = not overlay_menu.shown
|
||||
overlay_menu.shown = shown
|
||||
|
||||
# How long should the transitions take?
|
||||
trans_delay = 0.5
|
||||
|
||||
# These transitions assume that the menu is placed on the
|
||||
# right side of the screen. This indicator gives the
|
||||
# fraction of the screen that participates in transitions.
|
||||
|
||||
trans_frac = 0.75
|
||||
|
||||
if shown:
|
||||
|
||||
trans = CropMove(trans_delay,
|
||||
"custom",
|
||||
startcrop=(trans_frac, 0.0, 0.0, 1.0),
|
||||
startpos=(1.0, 0.0),
|
||||
endcrop=(trans_frac, 0.0, 1.0-trans_frac, 1.0),
|
||||
endpos=(trans_frac, 0.0),
|
||||
topnew=True)
|
||||
|
||||
renpy.transition(trans, 'overlay_menu')
|
||||
|
||||
else:
|
||||
|
||||
trans = CropMove(trans_delay,
|
||||
"custom",
|
||||
endcrop=(trans_frac, 0.0, 0.0, 1.0),
|
||||
endpos=(1.0, 0.0),
|
||||
startcrop=(trans_frac, 0.0, 1.0-trans_frac, 1.0),
|
||||
startpos=(trans_frac, 0.0),
|
||||
topnew=False)
|
||||
|
||||
renpy.transition(trans, 'overlay_menu')
|
||||
|
||||
|
||||
renpy.restart_interaction()
|
||||
|
||||
|
||||
# Add a new underlay that handles the overlay menu toggle.
|
||||
config.underlay.append(renpy.Keymap(game_menu = overlay_menu_toggle))
|
||||
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
# 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:
|
||||
# $ 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,
|
||||
**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',
|
||||
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,71 @@
|
||||
# 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 -10:
|
||||
|
||||
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:
|
||||
|
||||
vp = _Preference('Voice', 'voice', [
|
||||
('Enabled', True, 'True'),
|
||||
('Disabled', False, 'True')
|
||||
], base=persistent)
|
||||
|
||||
library.preferences['prefs_left'].append(vp)
|
||||
|
||||
# This is called on each interaction, to ensure that the
|
||||
# appropriate voice file is played for the user.
|
||||
def voice_interact():
|
||||
|
||||
if not audio.sound_enabled():
|
||||
return
|
||||
|
||||
if not persistent.voice:
|
||||
return
|
||||
|
||||
if _voice.play and not config.skipping:
|
||||
audio.sound_play(_voice.play, channel=1)
|
||||
elif not _voice.sustain:
|
||||
audio.sound_stop(channel=1)
|
||||
|
||||
_voice.play = None
|
||||
_voice.sustain = False
|
||||
|
||||
config.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()
|
||||
@@ -1,4 +0,0 @@
|
||||
import renpy
|
||||
|
||||
# Do nothing when the editor is invoked.
|
||||
Editor = renpy.editor.Editor
|
||||
@@ -1,4 +0,0 @@
|
||||
import renpy
|
||||
|
||||
# Pass the file off to the system editor (as determined by file associations).
|
||||
Editor = renpy.editor.SystemEditor
|
||||
@@ -1,89 +0,0 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import renpy
|
||||
|
||||
class Editor(renpy.editor.Editor):
|
||||
|
||||
has_projects = True
|
||||
|
||||
def get_code(self):
|
||||
"""
|
||||
Returns the path to the code executable.
|
||||
"""
|
||||
|
||||
system = __file__.endswith(" (System).edit.py")
|
||||
|
||||
if system:
|
||||
|
||||
if "RENPY_VSCODE" in os.environ:
|
||||
return os.environ["RENPY_VSCODE"]
|
||||
|
||||
if renpy.windows:
|
||||
return "code.cmd"
|
||||
|
||||
if renpy.macintosh and os.path.exists("/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code"):
|
||||
return "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code"
|
||||
|
||||
return "code"
|
||||
|
||||
else:
|
||||
|
||||
RENPY_VSCODE = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "vscode"))
|
||||
|
||||
if renpy.windows:
|
||||
code = os.path.join(RENPY_VSCODE, "VSCode-win32-x64", "bin", "code.cmd")
|
||||
elif renpy.macintosh:
|
||||
code = os.path.join(RENPY_VSCODE, "Visual Studio Code.app", "Contents", "Resources", "app", "bin", "code")
|
||||
elif renpy.linux:
|
||||
if renpy.arch == "aarch64":
|
||||
arch = "arm64"
|
||||
elif renpy.arch == "armv7l":
|
||||
arch = "arm"
|
||||
else:
|
||||
arch = "x86_64"
|
||||
|
||||
code = os.path.join(RENPY_VSCODE, "VSCode-linux-" + arch, "bin", "code")
|
||||
else:
|
||||
code = "code"
|
||||
|
||||
return code
|
||||
|
||||
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 begin(self, new_window=False, **kwargs):
|
||||
self.args = [ ]
|
||||
|
||||
def end(self, **kwargs):
|
||||
self.args.reverse()
|
||||
|
||||
code = self.get_code()
|
||||
args = [ code, "-g" ] + self.args
|
||||
args = [ renpy.exports.fsencode(i) for i in args ]
|
||||
|
||||
if renpy.windows:
|
||||
CREATE_NO_WINDOW = 0x08000000
|
||||
subprocess.Popen(args, creationflags=CREATE_NO_WINDOW)
|
||||
else:
|
||||
subprocess.Popen(args)
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
e = Editor()
|
||||
e.begin()
|
||||
|
||||
for i in sys.argv[1:]:
|
||||
e.open(i)
|
||||
|
||||
e.end()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,89 +0,0 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import renpy
|
||||
|
||||
class Editor(renpy.editor.Editor):
|
||||
|
||||
has_projects = True
|
||||
|
||||
def get_code(self):
|
||||
"""
|
||||
Returns the path to the code executable.
|
||||
"""
|
||||
|
||||
system = __file__.endswith(" (System).edit.py")
|
||||
|
||||
if system:
|
||||
|
||||
if "RENPY_VSCODE" in os.environ:
|
||||
return os.environ["RENPY_VSCODE"]
|
||||
|
||||
if renpy.windows:
|
||||
return "code.cmd"
|
||||
|
||||
if renpy.macintosh and os.path.exists("/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code"):
|
||||
return "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code"
|
||||
|
||||
return "code"
|
||||
|
||||
else:
|
||||
|
||||
RENPY_VSCODE = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "vscode"))
|
||||
|
||||
if renpy.windows:
|
||||
code = os.path.join(RENPY_VSCODE, "VSCode-win32-x64", "bin", "code.cmd")
|
||||
elif renpy.macintosh:
|
||||
code = os.path.join(RENPY_VSCODE, "Visual Studio Code.app", "Contents", "Resources", "app", "bin", "code")
|
||||
elif renpy.linux:
|
||||
if renpy.arch == "aarch64":
|
||||
arch = "arm64"
|
||||
elif renpy.arch == "armv7l":
|
||||
arch = "arm"
|
||||
else:
|
||||
arch = "x86_64"
|
||||
|
||||
code = os.path.join(RENPY_VSCODE, "VSCode-linux-" + arch, "bin", "code")
|
||||
else:
|
||||
code = "code"
|
||||
|
||||
return code
|
||||
|
||||
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 begin(self, new_window=False, **kwargs):
|
||||
self.args = [ ]
|
||||
|
||||
def end(self, **kwargs):
|
||||
self.args.reverse()
|
||||
|
||||
code = self.get_code()
|
||||
args = [ code, "-g" ] + self.args
|
||||
args = [ renpy.exports.fsencode(i) for i in args ]
|
||||
|
||||
if renpy.windows:
|
||||
CREATE_NO_WINDOW = 0x08000000
|
||||
subprocess.Popen(args, creationflags=CREATE_NO_WINDOW)
|
||||
else:
|
||||
subprocess.Popen(args)
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
e = Editor()
|
||||
e.begin()
|
||||
|
||||
for i in sys.argv[1:]:
|
||||
e.open(i)
|
||||
|
||||
e.end()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,45 +0,0 @@
|
||||
# Copyright 2004-2023 Tom Rothamel <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.
|
||||
|
||||
# Checks for various abilities that might be taken away from us by
|
||||
# redistributors.
|
||||
|
||||
init 1 python in ability:
|
||||
|
||||
from store import config
|
||||
import store
|
||||
import store.updater as updater
|
||||
|
||||
import os
|
||||
|
||||
EXECUTABLES = [ "renpy.exe", "renpy.app", "renpy.sh" ]
|
||||
|
||||
# can_distribute - True if we can distribute
|
||||
for i in EXECUTABLES:
|
||||
if not os.path.exists(os.path.join(config.renpy_base, i)):
|
||||
can_distribute = False
|
||||
else:
|
||||
can_distribute = True
|
||||
|
||||
|
||||
# can_update - True if we can update.
|
||||
can_update = updater.can_update() or (store.UPDATE_SIMULATE is not None)
|
||||
|
||||