Compare commits

..

4 Commits

Author SHA1 Message Date
Tom Rothamel cac50d3099 Create music contexts out of the periodic function.
This prevents a problem where creating them in the context can
interfere with rollback.

Fixes #1278.
2017-10-21 21:01:06 -04:00
Tom Rothamel 7c4b859fa2 More locking. 2017-10-21 21:01:06 -04:00
Tom Rothamel c7aef439b5 Make the audio periodic thread optional. 2017-10-21 21:01:06 -04:00
Tom Rothamel a24b1eb49c Move renpy.audio.periodic into its own thread.
Per #1174.

This is one of the few places where Ren'Py reads from a file,
completely unpredictably. Hence, it's one of the few places
where disk access outside Ren'Py can block the main thread.

Now, audio files are opened in a special thread, leaving the
GUI thread to do its thing.
2017-10-21 21:01:06 -04:00
1548 changed files with 134384 additions and 335474 deletions
+13
View File
@@ -0,0 +1,13 @@
[run]
plugins = renpycoverage
[report]
omit =
keywords.py
style.pxd
*pygame_sdl2*
*.rpyc
*/game/*
*style_*_functions.pyx
renpy/gl/gl.pyx
renpy/gl/gl1.pyx
+63 -161
View File
@@ -1,176 +1,78 @@
# 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
*.egg-info
# Editor/tool files.
*~
*.bak
.gdb_history
# Compiled Ren'Py files.
*.rpyc
*.rpyb
*.rpymc
*.pyc
*.pyo
*~
*.bak
saves
tmp
cache
env
# Ren'Py log files.
log.txt
errors.txt
traceback.txt
lint.txt
# Created directories.
saves/
tmp/
cache/
styles.txt
# 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.
/android
/build
/dist
/dists
/renpy.app
/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
# Updater.
update.pem
# Steam.
steam_appid.txt
/steamapi.py
# Android.
*.keystore
# Type analysis.
/typings
# Live2D
cubism
CubismSdkForNative-4-*.zip
# Created files.
/lint.txt
/renpy.code
/*testing*
/screenshot*
.android.json
/renpy.exe
/lib
/lib.old
/doc
/.pydevproject
/.pydevproject.bak
/.project
/.cproject
/.settings
/LICENSE.txt
/templates/english/README.html
/the_question/README.html
/tutorial/README.html
/renpy/angle/*.pyx
/renpy/angle/*.pxd
/renpy-ppc.zip
/module/build
/module/gen*
/editra
/launcher/game/theme
/launcher/game/script_version.rpy
# Old files.
/0old
/dl
renpy/vc_version.py
.externalToolBuilders
/rapt
/evil
/iaptest
/pygame_sdl2
/renios
/steam_appid.txt
/.coverage
/id
/ai
/interface_7
/htmlcov
/the_question*/.android.json
/WINDOWS.rst
/old
# Works in progress, throwaway scripts, etc.
/scratch
/*-dists
/c
/cg
/.vscode/launch.json
/.vscode/tasks.json
# Pyi Files (generated by scripts/generate_pyi.py)
renpy/audio/filter.pyi
renpy/audio/renpysound.pyi
renpy/display/accelerator.pyi
renpy/display/matrix.pyi
renpy/display/quaternion.pyi
renpy/display/render.pyi
renpy/encryption.pyi
renpy/gl2/gl2draw.pyi
renpy/gl2/gl2mesh.pyi
renpy/gl2/gl2mesh2.pyi
renpy/gl2/gl2mesh3.pyi
renpy/gl2/gl2model.pyi
renpy/gl2/gl2polygon.pyi
renpy/gl2/gl2shader.pyi
renpy/gl2/gl2texture.pyi
renpy/gl2/live2dmodel.pyi
renpy/lexersupport.pyi
renpy/pydict.pyi
renpy/style.pyi
renpy/style.pyi
renpy/styledata/style_activate_functions.pyi
renpy/styledata/style_functions.pyi
renpy/styledata/style_hover_functions.pyi
renpy/styledata/style_idle_functions.pyi
renpy/styledata/style_insensitive_functions.pyi
renpy/styledata/style_selected_activate_functions.pyi
renpy/styledata/style_selected_functions.pyi
renpy/styledata/style_selected_hover_functions.pyi
renpy/styledata/style_selected_idle_functions.pyi
renpy/styledata/style_selected_insensitive_functions.pyi
renpy/styledata/styleclass.pyi
renpy/styledata/stylesets.pyi
renpy/text/ftfont.pyi
renpy/text/hbfont.pyi
renpy/text/textsupport.pyi
renpy/text/texwrap.pyi
renpy/uguu/gl.pyi
renpy/uguu/uguu.pyi
/*-all/
/*-win/
/*-mac/
/*-linux*/
/*-dists/
doc-web
+2
View File
@@ -0,0 +1,2 @@
[pep8]
ignore=E123,E20,E22,E501,E702,E266,E265,E262
-284
View File
@@ -1,284 +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,
"**/*.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,
"vscode": true,
"web": true,
"web2": true,
"web3": true,
"**/*.rpa": true,
"**/*.rpymc": true,
"**/cache/": true,
"renpy/audio/filter.pyi": true,
"renpy/audio/renpysound.pyi": true,
"renpy/display/accelerator.pyi": true,
"renpy/display/matrix.pyi": true,
"renpy/display/quaternion.pyi": true,
"renpy/display/render.pyi": true,
"renpy/encryption.pyi": true,
"renpy/gl2/gl2draw.pyi": true,
"renpy/gl2/gl2mesh.pyi": true,
"renpy/gl2/gl2mesh2.pyi": true,
"renpy/gl2/gl2mesh3.pyi": true,
"renpy/gl2/gl2model.pyi": true,
"renpy/gl2/gl2polygon.pyi": true,
"renpy/gl2/gl2shader.pyi": true,
"renpy/gl2/gl2texture.pyi": true,
"renpy/gl2/live2dmodel.pyi": true,
"renpy/lexersupport.pyi": true,
"renpy/pydict.pyi": true,
"renpy/style.pyi": true,
"renpy/styledata/style_activate_functions.pyi": true,
"renpy/styledata/style_functions.pyi": true,
"renpy/styledata/style_hover_functions.pyi": true,
"renpy/styledata/style_idle_functions.pyi": true,
"renpy/styledata/style_insensitive_functions.pyi": true,
"renpy/styledata/style_selected_activate_functions.pyi": true,
"renpy/styledata/style_selected_functions.pyi": true,
"renpy/styledata/style_selected_hover_functions.pyi": true,
"renpy/styledata/style_selected_idle_functions.pyi": true,
"renpy/styledata/style_selected_insensitive_functions.pyi": true,
"renpy/styledata/styleclass.pyi": true,
"renpy/styledata/stylesets.pyi": true,
"renpy/text/ftfont.pyi": true,
"renpy/text/hbfont.pyi": true,
"renpy/text/textsupport.pyi": true,
"renpy/text/texwrap.pyi": true,
"renpy/uguu/gl.pyi": true,
"renpy/uguu/uguu.pyi": 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",
"editor.tokenColorCustomizations": {
"[*Light*]": {
"textMateRules": [
{
"scope": "ref.matchtext",
"settings": {
"foreground": "#000"
}
}
]
},
"[*Dark*]": {
"textMateRules": [
{
"scope": "ref.matchtext",
"settings": {
"foreground": "#fff"
}
}
]
},
"textMateRules": [
{
"scope": "renpy.meta.plain",
"settings": {
"fontStyle": ""
}
},
{
"scope": "renpy.meta.i",
"settings": {
"fontStyle": "italic"
}
},
{
"scope": "renpy.meta.b",
"settings": {
"fontStyle": "bold"
}
},
{
"scope": [
"renpy.meta.u",
"renpy.meta.a"
],
"settings": {
"fontStyle": "underline"
}
},
{
"scope": "renpy.meta.s",
"settings": {
"fontStyle": "strikethrough"
}
},
{
"scope": "renpy.meta.i renpy.meta.b",
"settings": {
"fontStyle": "italic bold"
}
},
{
"scope": "renpy.meta.i renpy.meta.u",
"settings": {
"fontStyle": "italic underline"
}
},
{
"scope": "renpy.meta.i renpy.meta.s",
"settings": {
"fontStyle": "italic strikethrough"
}
},
{
"scope": "renpy.meta.b renpy.meta.u",
"settings": {
"fontStyle": "bold underline"
}
},
{
"scope": "renpy.meta.b renpy.meta.s",
"settings": {
"fontStyle": "bold strikethrough"
}
},
{
"scope": "renpy.meta.u renpy.meta.s",
"settings": {
"fontStyle": "underline strikethrough"
}
},
{
"scope": "renpy.meta.i renpy.meta.b renpy.meta.u",
"settings": {
"fontStyle": "italic bold underline"
}
},
{
"scope": "renpy.meta.i renpy.meta.b renpy.meta.s",
"settings": {
"fontStyle": "italic bold strikethrough"
}
},
{
"scope": "renpy.meta.i renpy.meta.u renpy.meta.s",
"settings": {
"fontStyle": "italic underline strikethrough"
}
},
{
"scope": "renpy.meta.b renpy.meta.u renpy.meta.s",
"settings": {
"fontStyle": "bold underline strikethrough"
}
},
{
"scope": "renpy.meta.i renpy.meta.b renpy.meta.u renpy.meta.s",
"settings": {
"fontStyle": "italic bold underline strikethrough"
}
},
{
"scope": "renpy.meta.color.text",
"settings": {
"foreground": "#ffffff"
}
},
{
"scope": "renpy.meta.color.#f00",
"settings": {
"foreground": "#f00"
}
},
{
"scope": "text.notes.info",
"settings": {
"foreground": "#17a2b8",
"fontStyle": "bold"
}
},
{
"scope": "text.notes.success",
"settings": {
"foreground": "#28a745",
"fontStyle": "bold"
}
},
{
"scope": "text.notes.warning",
"settings": {
"foreground": "#ffc107",
"fontStyle": "bold"
}
},
{
"scope": "text.notes.danger",
"settings": {
"foreground": "#dc3545",
"fontStyle": "bold"
}
},
{
"scope": "renpy.meta.color.#fff",
"settings": {
"foreground": "#fff"
}
},
{
"scope": "renpy.meta.color.#fcc",
"settings": {
"foreground": "#fcc"
}
},
{
"scope": "renpy.meta.color.#cfc",
"settings": {
"foreground": "#cfc"
}
},
{
"scope": "renpy.meta.color.#c00",
"settings": {
"foreground": "#c00"
}
}
]
}
}
-12
View File
@@ -1,12 +0,0 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "run.sh --build",
"type": "shell",
"command": "/home/tom/ab/renpy/run.sh --build",
}
]
}
BIN
View File
Binary file not shown.
+32 -70
View File
@@ -2,51 +2,30 @@
The Ren'Py Visual Novel Engine
==============================
https://www.renpy.org
http://www.renpy.org
Branches
========
The following branches are the most interesting.
``fix``
The fix branch is used for fixes to the current version of Ren'Py that do
not require dangerous changes. The fix branch is also the source of the
documentation on https://www.renpy.org/. This branch is automatically
merged into master on a regular basis.
Pull requests that contain fixes or documentation improvements should be
made to the fix branch. When a release is made, the master branch is
copied to the fix branch.
``master``
The master branch is where the main focus of development is. This branch
will eventually become the next release of Ren'Py.
Pull requests that contain new features, that require incompatible changes,
or major changes to Ren'Py's internals should be targeted at the master
branch.
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
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.
on Linux or Mac OS X, 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
http://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::
@@ -58,7 +37,7 @@ 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/X) to find out what's wrong.
or @renpytom on twitter) to find out what's wrong.
The ``doc`` symlink will dangle until documentation is built, as described
below.
@@ -70,60 +49,50 @@ 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 libassimp-dev libavcodec-dev libavformat-dev \
libswresample-dev libswscale-dev libharfbuzz-dev libfreetype6-dev libfribidi-dev libsdl2-dev \
libsdl2-image-dev libsdl2-gfx-dev libsdl2-mixer-dev libsdl2-ttf-dev libjpeg-dev pkg-config
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
apt-get install virtualenvwrapper python-dev libavcodec-dev libavformat-dev \
libavresample-dev libswresample-dev libswscale-dev libfreetype6-dev libglew1.6-dev \
libfribidi-dev libsdl2-dev libsdl2-image-dev libsdl2-gfx-dev \
libsdl2-mixer-dev libsdl2-ttf-dev libjpeg-turbo8-dev
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::
After activating the virtualenv, install cython::
pip install -U setuptools cython future six typing pefile requests ecdsa
pip install -U cython
Then, install pygame_sdl2 by running the following commands::
git clone https://www.github.com/renpy/pygame_sdl2
pushd pygame_sdl2
python fix_virtualenv.py $VIRTUAL_ENV
python setup.py install
python setup.py install_headers
popd
Finally, use setup.py to compile extension modules that support Ren'Py::
Next, set RENPY_DEPS_INSTALL To a \::-separated list of paths containing the
dependencies, and RENPY_CYTHON to the name of the cython command::
export RENPY_DEPS_INSTALL="/usr::/usr/lib/x86_64-linux-gnu/"
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
Other Platforms
---------------
Where supported, Ren'Py will attempt to find include directories and library paths
using pkg-config. If pkg-config is not present, include and library paths can be
specified using CFLAGS and LDFLAGS.
If RENPY_CFLAGS is present in the environment and CFLAGS is not, setup.py
will set CFLAGS to RENPY_CFLAGS. The same is true for RENPY_LDFLAGS,
RENPY_CC, RENPY_CXX, and RENPY_LD.
Setup.py does not support cross-compiling. See https://github.com/renpy/renpy-build
for software that cross-compiles Ren'Py for many platforms.
python -O renpy.py
Documentation
@@ -134,10 +103,10 @@ 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.
also need the `Sphinx <http://sphinx-doc.org/>`_ documentation generator.
If you have pip working, install Sphinx using::
pip install -U sphinx sphinx_rtd_theme sphinx_rtd_dark_mode
pip install -U sphinx
Once Sphinx is installed, change into the ``sphinx`` directory inside the
Ren'Py checkout and run::
@@ -154,7 +123,7 @@ 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
Indicates that this functions 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.
@@ -185,7 +154,7 @@ 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
http://lemmasoft.renai.us/forums/viewtopic.php?p=321603#p321603
Contributing
@@ -194,10 +163,3 @@ 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
-43
View File
@@ -19,46 +19,3 @@ 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>
Ukrainian
---------
Translation to Ukrainian language. Most of translations provided by Veydzher.
Feel free to report me any mistakes, fixes or improvements.
* Veydzher <veydzherdgswift008@gmail.com>
+9 -61
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python2
from __future__ import print_function
import argparse
@@ -6,27 +6,21 @@ import os
import subprocess
import sys
from renpy import version_tuple # @UnresolvedImport
branch = os.popen("git branch --show-current").read().strip()
from renpy import version_tuple # @UnresolvedImport
SOURCE = [
"/home/tom/ab/renpy",
"/home/tom/ab/renpy-build-" + branch,
"/home/tom/ab/android/",
"/home/tom/ab/android/python-for-android",
"/home/tom/ab/ripe/renios",
"/home/tom/ab/renpy-deps",
"/home/tom/ab/pygame_sdl2",
]
from renpy.versions import generate_vc_version
version = generate_vc_version()["version"]
short_version = version.rpartition(".")[0]
major = version.partition(".")[0]
version = ".".join(str(i) for i in version_tuple)
short_version = ".".join(str(i) for i in version_tuple[:-1])
print("Version", version)
if major == '7':
SOURCE.append("/home/tom/ab/renpy-build-fix/renpyweb")
ap = argparse.ArgumentParser()
ap.add_argument("--release", action="store_true")
@@ -35,48 +29,9 @@ 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 ])
@@ -121,8 +76,6 @@ else:
if args.no_tag:
tag = False
links = [ i + "-" + major for i in links ]
if tag:
for i in SOURCE:
os.chdir(i)
@@ -142,6 +95,7 @@ if tag:
subprocess.check_call([ "git", "tag", "-a", tag, "-m", "Tagging Ren'Py + " + version + " release." ])
os.chdir("/home/tom/ab/renpy/dl")
for i in links:
@@ -152,10 +106,4 @@ for i in links:
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)
-1
View File
@@ -12,5 +12,4 @@ 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
+10
View File
@@ -0,0 +1,10 @@
# This file sets up the normal python modules. It's used to help eclipse
# do type detection, by providing an importable version of the cython code.
from distutils.core import setup
setup(
packages=['renpy', 'renpy.gl', 'renpy.angle', 'renpy.display', 'renpy.audio', 'renpy.text', 'pysdlsound'],
package_dir={ 'pysdlsound' : 'module' },
)
+2
View File
@@ -0,0 +1,2 @@
lib\windows-i686\python.exe -OO renpy.py tutorial
pause
Executable
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
if [ -z "$RENPY_COVERAGE" ]; then
echo "$0 needs to be run with RENPY_COVERAGE set, probably in a virtualenv."
exit 1
fi
./run.sh --build || exit 1
coverage run renpy.py "$@"
coverage html
xdg-open htmlcov/index.html
Executable
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
# This builds out of date modules using the default C compiler, and then
# runs them.
ROOT="$(dirname $(realpath $0))"
export RENPY_GDB="gdb --args"
. "$ROOT/run.sh" "$@"
+63 -132
View File
@@ -1,11 +1,7 @@
#!/home/tom/ab/renpy/lib/py3-linux-x86_64/python
#!/home/tom/ab/renpy/lib/linux-x86_64/python -O
# 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
from __future__ import print_function
import sys
import os
@@ -13,143 +9,88 @@ import compileall
import shutil
import subprocess
import argparse
import time
import collections
try:
from importlib import reload
except ImportError:
pass
if not sys.flags.optimize:
raise Exception("Optimization disabled.")
ROOT = os.path.dirname(os.path.abspath(__file__))
def zip_rapt_symbols(destination):
"""
Zips up the rapt symbols.
"""
import zipfile
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)
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)
zf.close()
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.
"""
sf = open(src, "rb")
df = open(dest, "wb")
# True if we want to copy the line.
copy = True
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 l in sf:
if "# tutorial-only" in l:
copy = False
elif "# end-tutorial-only" in l:
copy = True
else:
if copy:
df.write(l)
def link_directory(dirname):
dn = os.path.join(ROOT, dirname)
sf.close()
df.close()
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()
if not sys.flags.optimize:
raise Exception("Not running with python optimization.")
ap = argparse.ArgumentParser()
ap.add_argument("version", nargs="?")
ap.add_argument("version")
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")
ap.add_argument("--nightly", action="store_true")
ap.add_argument("--print-version", action="store_true")
ap.add_argument("--sign", action="store_true")
args = ap.parse_args()
link_directory("rapt")
link_directory("renios")
link_directory("web")
import renpy.versions
renpy.versions.generate_vc_version(nightly=args.nightly)
if args.link_directories or args.vc_version_only:
return
if not os.path.abspath(sys.executable).startswith(ROOT + "/lib"):
raise Exception("Distribute must be run with the python in lib/.")
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.")
# 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
match_version = ".".join(str(i) for i in renpy.version_tuple[:2]) # @UndefinedVariable
s = subprocess.check_output([ "git", "describe", "--tags", "--dirty", "--match", "start-" + match_version ])
parts = s.strip().split("-")
if len(parts) <= 3:
vc_version = 0
else:
vc_version = int(parts[2])
if parts[-1] == "dirty":
vc_version += 1
with open("renpy/vc_version.py", "w") as f:
f.write("vc_version = {}".format(vc_version))
try:
vc_version_base = os.path.splitext(renpy.vc_version.__file__)[0]
reload(sys.modules['renpy.vc_version']) # @UndefinedVariable
except:
import renpy.vc_version # @UnusedImport
# Delete the .pyc and .pyo files, as reload can choose one of them instead
# of the new .py file.
for fn in [ vc_version_base + ".pyc", vc_version_base + ".pyo" ]:
if os.path.exists(fn):
os.unlink(fn)
reload(renpy.vc_version)
except Exception:
import renpy.vc_version
# A normal reload is fine, as renpy/__init__.py won't change.
reload(renpy)
if args.print_version:
print(renpy.version_only)
return
if args.version is None:
if args.nightly:
args.version = renpy.version_only
else:
args.version = ".".join(str(i) for i in renpy.version_tuple[:-1])
if args.append_version:
args.version += "-" + renpy.version_only
reload(sys.modules['renpy'])
# Check that the versions match.
full_version = renpy.version_only # @UndefinedVariable
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.")
@@ -162,24 +103,21 @@ def main():
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"
# Perhaps autobuild.
if "RENPY_BUILD_ALL" in os.environ:
print("Autobuild...")
subprocess.check_call(["scripts/autobuild.sh"])
# Compile all the python files.
compileall.compile_dir("renpy/", ddir="renpy/", force=True, quiet=1)
compileall.compile_dir("renpy/", ddir="renpy/", force=1, 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" ])
subprocess.check_call(["./renpy.sh", i, "quit" ])
# Kick off the rapt build.
if not args.fast:
@@ -188,8 +126,8 @@ def main():
sys.path.insert(0, os.path.join(ROOT, "rapt", "buildlib"))
import rapt.interface # type: ignore
import rapt.build # type: ignore
import rapt.interface # @UnresolvedImport
import rapt.build # @UnresolvedImport
interface = rapt.interface.Interface()
rapt.build.distclean(interface)
@@ -202,12 +140,10 @@ def main():
if not os.path.exists(destination):
os.makedirs(destination)
zip_rapt_symbols(destination)
if args.fast:
cmd = [
renpy_sh,
"./renpy.sh",
"launcher",
"distribute",
"launcher",
@@ -220,7 +156,7 @@ def main():
else:
cmd = [
renpy_sh,
"./renpy.sh",
"launcher",
"distribute",
"launcher",
@@ -228,12 +164,6 @@ def main():
destination,
]
if args.notarized:
cmd.extend([
"--macapp",
"notarized/out",
])
print()
subprocess.check_call(cmd)
@@ -252,7 +182,7 @@ def main():
"-q",
"egg_info",
"--tag-build",
"+renpy" + args.version.replace("+", "-"),
"-for-renpy-" + args.version,
"sdist",
"-d",
os.path.abspath(destination)
@@ -280,8 +210,8 @@ def main():
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
p = subprocess.Popen([ "7z", "a", sdk +".7z", sdk], stdout=subprocess.PIPE)
for i, _l in enumerate(p.stdout):
if i % 10 != 0:
continue
@@ -309,7 +239,8 @@ def main():
print()
print("Distribute took {:.0f} seconds.".format(time.time() - start))
if not (args.fast or args.sign):
print("For a final-ish release, remember to use --sign so we're signed on the mac.")
if __name__ == "__main__":
-5
View File
@@ -1,5 +0,0 @@
*.c
*.so
*.egg-info
*.html
build
-1
View File
@@ -1 +0,0 @@
../../renpy/cslots.pyx
-8
View File
@@ -1,8 +0,0 @@
from setuptools import setup, Extension
setup(
name="cslots",
version="1.0.0",
ext_modules=[
Extension("cslots", [ "cslots.pyx" ]),
])
-7
View File
@@ -1,7 +0,0 @@
#!/bin/bash
set -e
cython -a cslots.pyx
pip install -e .
python3 -m pytest
-262
View File
@@ -1,262 +0,0 @@
import sys
import pickle
from cslots import Object, Slot, IntegerSlot
class C1(Object):
str1 : str|None = None
int1 : int = 0
def __init__(self, str1=None, int1=1):
self.str1 = str1
self.int1 = int1
class C2(C1):
str2 : str|None = None
int2 : int = 0
def __init__(self, str1=None, int1=0, str2=None, int2=0):
self.str1 = str1
self.int1 = int1
self.str2 = str2
self.int2 = int2
def test_slot_count():
assert C1._cslot_count == 2
assert C2._cslot_count == 4
def test_slots():
o = C2()
assert o.str1 is None
assert o.int1 == 0
assert o.str2 is None
assert o.int2 == 0
o.str1 = "hello"
o.int1 = 42
o.str2 = "world"
o.int2 = 43
assert o.str1 == "hello"
assert o.int1 == 42
assert o.str2 == "world"
assert o.int2 == 43
o.str1 = None
o.int1 = 0
o.str2 = None
o.int2 = 0
assert o.str1 is None
assert o.int1 == 0
assert o.str2 is None
assert o.int2 == 0
def test_compress():
def expect_size(o, values, indexes):
assert sys.getsizeof(o) == 32 + 8 * values + indexes
o = C2()
o.str1 = "hello"
o.int1 = 42
o.str2 = "world"
o.int2 = 43
expect_size(o, 4, 4)
o._compress()
assert o.str1 == "hello"
assert o.int1 == 42
assert o.str2 == "world"
assert o.int2 == 43
expect_size(o, 4, 4)
o = C2()
o.str2 = "world"
o.int2 = 43
o._compress()
assert o.str1 is None
assert o.int1 == 0
assert o.str2 == "world"
assert o.int2 == 43
expect_size(o, 2, 4)
o = C2()
o.str1 = "hello"
o._compress()
assert o.str1 == "hello"
assert o.int1 == 0
assert o.str2 is None
assert o.int2 == 0
expect_size(o, 1, 1)
def test_decompress():
def expect_size(o, values, indexes):
assert sys.getsizeof(o) == 32 + 8 * values + indexes
o = C2()
o.str1 = "hello"
o.int1 = 42
o.str2 = "world"
o.int2 = 43
expect_size(o, 4, 4)
o._compress()
o._decompress()
assert o.str1 == "hello"
assert o.int1 == 42
assert o.str2 == "world"
assert o.int2 == 43
expect_size(o, 4, 4)
o = C2()
o.str2 = "world"
o.int2 = 43
o._compress()
o._decompress()
assert o.str1 is None
assert o.int1 == 0
assert o.str2 == "world"
assert o.int2 == 43
expect_size(o, 4, 4)
o = C2()
o.str1 = "hello"
o._compress()
o._decompress()
assert o.str1 == "hello"
assert o.int1 == 0
assert o.str2 is None
assert o.int2 == 0
expect_size(o, 4, 4)
def test_kill():
o = C2()
o.str1 = "hello"
o.int1 = 42
o.str2 = "world"
o.int2 = 43
o._kill()
assert o.str1 is None
assert o.int1 == 0
assert o.str2 is None
assert o.int2 == 0
o = C2()
o.str2 = "world"
o.int2 = 43
o._compress()
o._kill()
assert o.str1 is None
assert o.int1 == 0
assert o.str2 is None
assert o.int2 == 0
def test_pickle():
o = C2()
o.str1 = "hello"
o.int1 = 42
o.str2 = "world"
o.int2 = 43
o = pickle.loads(pickle.dumps(o))
assert o.str1 == "hello"
assert o.int1 == 42
assert o.str2 == "world"
assert o.int2 == 43
o = C2()
o.str2 = "world"
o.int2 = 43
o = pickle.loads(pickle.dumps(o))
assert o.str1 is None
assert o.int1 == 0
assert o.str2 == "world"
assert o.int2 == 43
o = C2()
o.str1 = "hello"
o = pickle.loads(pickle.dumps(o))
assert o.str1 == "hello"
assert o.int1 == 0
assert o.str2 is None
assert o.int2 == 0
def test_pickle_compressed():
o = C2()
o.str1 = "hello"
o.int1 = 42
o.str2 = "world"
o.int2 = 43
o._compress()
o = pickle.loads(pickle.dumps(o))
assert o.str1 == "hello"
assert o.int1 == 42
assert o.str2 == "world"
assert o.int2 == 43
o = C2()
o.str2 = "world"
o.int2 = 43
o._compress()
o = pickle.loads(pickle.dumps(o))
assert o.str1 is None
assert o.int1 == 0
assert o.str2 == "world"
assert o.int2 == 43
o = C2()
o.str1 = "hello"
o._compress()
o = pickle.loads(pickle.dumps(o))
assert o.str1 == "hello"
assert o.int1 == 0
assert o.str2 is None
assert o.int2 == 0
+9 -22
View File
@@ -11,8 +11,6 @@ init offset = -2
init python:
gui.init(1280, 720)
## Enable checks for invalid or unstable properties in screens or transforms
define config.check_conflicting_properties = True
################################################################################
@@ -124,7 +122,7 @@ define gui.namebox_height = None
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.
## if the namebox will be scaled.
define gui.namebox_tile = False
@@ -214,7 +212,6 @@ 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 ###########################################################
@@ -230,8 +227,6 @@ define gui.slot_button_borders = Borders(gui.scale(10), gui.scale(10), gui.scale
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)
@@ -328,7 +323,7 @@ define gui.vbar_borders = Borders(gui.scale(4), gui.scale(4), gui.scale(4), gui.
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 game menu. "hide" hides them, while
## What to do with unscrollable scrollbars in the gui. "hide" hides them, while
## None shows them.
define gui.unscrollable = "hide"
@@ -344,9 +339,6 @@ define config.history_length = 250
## at the cost of performance.
define gui.history_height = gui.scale(140)
## Additional space to add between history screen entries.
define gui.history_spacing = 0
## The position, width, and alignment of the label giving the name of the
## speaking character.
define gui.history_name_xpos = gui.scale(155)
@@ -368,10 +360,6 @@ define gui.history_text_xalign = 0.0
## 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)
@@ -404,7 +392,6 @@ define gui.nvl_thought_xalign = 0.0
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
@@ -422,15 +409,13 @@ init python:
## This increases the size of the quick buttons to make them easier to
## touch on tablets and phones.
@gui.variant
def touch():
if renpy.variant("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():
if renpy.variant("small"):
## Font sizes.
gui.text_size = gui.scale(30)
@@ -443,14 +428,13 @@ init python:
## 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)
gui.text_xpos = gui.scale(90)
gui.text_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)
@@ -479,3 +463,6 @@ init python:
gui.nvl_button_width = gui.scale(1240)
gui.nvl_button_xpos = gui.scale(20)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

-1
View File
@@ -1 +0,0 @@
## When this file exists, Ren'Py will load files in game/libs before other files in game.
+15 -10
View File
@@ -28,11 +28,10 @@ define gui.show_name = True
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.
## Text that is placed on the game's about screen. To insert a blank line
## between paragraphs, write \n\n.
define gui.about = _p("""
""")
define gui.about = _("")
## A short name for the game used for executables and directories in the
@@ -44,9 +43,9 @@ 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.
## These three variables control 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
@@ -199,10 +198,16 @@ init python:
build.documentation('*.html')
build.documentation('*.txt')
## Set this to a string containing your Apple Developer ID Application
## to enable codesigning on the Mac. Be sure to change it to your own
## Apple-issued ID.
## 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.mac_identity = "Developer ID Application: Guy Shy (XHTE5H7Z42)"
## A Google Play license key is required to download expansion files and
## perform in-app purchases. It can be found on the "Services & APIs" page
## of the Google Play developer console.
# define build.google_play_key = "..."
+87 -185
View File
@@ -96,6 +96,7 @@ style frame:
## https://www.renpy.org/doc/html/screen_special.html#say
screen say(who, what):
style_prefix "say"
window:
id "window"
@@ -159,7 +160,6 @@ style say_dialogue:
xsize gui.dialogue_width
ypos gui.dialogue_ypos
adjust_spacing False
## Input screen ################################################################
##
@@ -177,7 +177,7 @@ screen input(prompt):
window:
vbox:
xanchor gui.dialogue_text_xalign
xalign gui.dialogue_text_xalign
xpos gui.dialogue_xpos
xsize gui.dialogue_width
ypos gui.dialogue_ypos
@@ -212,6 +212,11 @@ screen choice(items):
textbutton i.caption action i.action
## When this is true, menu captions will be spoken by the narrator. When
## false, menu captions will be displayed as empty buttons.
define config.narrator_menu = True
style choice_vbox is vbox
style choice_button is button
style choice_button_text is button_text
@@ -227,7 +232,7 @@ style choice_button is default:
properties gui.button_properties("choice_button")
style choice_button_text is default:
properties gui.text_properties("choice_button")
properties gui.button_text_properties("choice_button")
## Quick Menu screen ###########################################################
@@ -244,7 +249,9 @@ screen quick_menu():
hbox:
style_prefix "quick"
style "quick_menu"
xalign 0.5
yalign 1.0
textbutton _("Back") action Rollback()
textbutton _("History") action ShowMenu('history')
@@ -263,19 +270,14 @@ init python:
default quick_menu = True
style quick_menu is hbox
style quick_button is default
style quick_button_text is button_text
style quick_menu:
xalign 0.5
yalign 1.0
style quick_button:
properties gui.button_properties("quick_button")
style quick_button_text:
properties gui.text_properties("quick_button")
properties gui.button_text_properties("quick_button")
################################################################################
@@ -321,14 +323,12 @@ screen navigation():
textbutton _("About") action ShowMenu("about")
if renpy.variant("pc") or (renpy.variant("web") and not renpy.variant("mobile")):
if renpy.variant("pc"):
## Help isn't necessary or relevant to mobile devices.
textbutton _("Help") action ShowMenu("help")
if renpy.variant("pc"):
## The quit button is banned on iOS and unnecessary on Android and Web.
## The quit button is banned on iOS and unnecessary on Android.
textbutton _("Quit") action Quit(confirm=not main_menu)
@@ -340,7 +340,7 @@ style navigation_button:
properties gui.button_properties("navigation_button")
style navigation_button_text:
properties gui.text_properties("navigation_button")
properties gui.button_text_properties("navigation_button")
## Main Menu screen ############################################################
@@ -354,11 +354,13 @@ screen main_menu():
## This ensures that any other menu screen is replaced.
tag menu
style_prefix "main_menu"
add gui.main_menu_background
## This empty frame darkens the main menu.
frame:
style "main_menu_frame"
pass
## The use statement includes another screen inside this one. The actual
## contents of the main menu are in the navigation screen.
@@ -367,8 +369,6 @@ screen main_menu():
if gui.show_name:
vbox:
style "main_menu_vbox"
text "[config.name!t]":
style "main_menu_title"
@@ -410,11 +410,11 @@ style main_menu_version:
## This lays out the basic common structure of a game menu screen. It's called
## with the screen title, and displays the background, title, and navigation.
##
## The scroll parameter can be None, or one of "viewport" or "vpgrid".
## This screen is intended to be used with one or more children, which are
## The scroll parameter can be None, or one of "viewport" or "vpgrid". When
## this screen is intended to be used with one or more children, which are
## transcluded (placed) inside it.
screen game_menu(title, scroll=None, yinitial=0.0, spacing=0):
screen game_menu(title, scroll=None, yinitial=0.0):
style_prefix "game_menu"
@@ -442,13 +442,10 @@ screen game_menu(title, scroll=None, yinitial=0.0, spacing=0):
scrollbars "vertical"
mousewheel True
draggable True
pagekeys True
side_yfill True
vbox:
spacing spacing
transclude
elif scroll == "vpgrid":
@@ -460,12 +457,9 @@ screen game_menu(title, scroll=None, yinitial=0.0, spacing=0):
scrollbars "vertical"
mousewheel True
draggable True
pagekeys True
side_yfill True
spacing spacing
transclude
else:
@@ -568,6 +562,10 @@ screen about():
text _("Made with {a=https://www.renpy.org/}Ren'Py{/a} [renpy.version_only].\n\n[renpy.license!t]")
## This is redefined in options.rpy to add text to the about screen.
define gui.about = ""
style about_label is gui_label
style about_label_text is gui_label_text
style about_text is gui_text
@@ -652,42 +650,27 @@ screen file_slots(title):
key "save_delete" action FileDelete(slot)
## Buttons to access other pages.
vbox:
hbox:
style_prefix "page"
xalign 0.5
yalign 1.0
hbox:
xalign 0.5
spacing gui.page_spacing
spacing gui.page_spacing
textbutton _("<") action FilePagePrevious()
textbutton _("<") action FilePagePrevious()
key "save_page_prev" action FilePagePrevious()
if config.has_autosave:
textbutton _("{#auto_page}A") action FilePage("auto")
if config.has_autosave:
textbutton _("{#auto_page}A") action FilePage("auto")
if config.has_quicksave:
textbutton _("{#quick_page}Q") action FilePage("quick")
if config.has_quicksave:
textbutton _("{#quick_page}Q") action FilePage("quick")
## range(1, 10) gives the numbers from 1 to 9.
for page in range(1, 10):
textbutton "[page]" action FilePage(page)
## range(1, 10) gives the numbers from 1 to 9.
for page in range(1, 10):
textbutton "[page]" action FilePage(page)
textbutton _(">") action FilePageNext()
key "save_page_next" action FilePageNext()
if config.has_sync:
if CurrentScreenName() == "save":
textbutton _("Upload Sync"):
action UploadSync()
xalign 0.5
else:
textbutton _("Download Sync"):
action DownloadSync()
xalign 0.5
textbutton _(">") action FilePageNext()
style page_label is gui_label
@@ -703,10 +686,9 @@ style slot_name_text is slot_button_text
style page_label:
xpadding gui.scale(50)
ypadding gui.scale(3)
xalign 0.5
style page_label_text:
textalign 0.5
text_align 0.5
layout "subtitle"
hover_color gui.hover_color
@@ -714,13 +696,13 @@ style page_button:
properties gui.button_properties("page_button")
style page_button_text:
properties gui.text_properties("page_button")
properties gui.button_text_properties("page_button")
style slot_button:
properties gui.button_properties("slot_button")
style slot_button_text:
properties gui.text_properties("slot_button")
properties gui.button_text_properties("slot_button")
## Preferences screen ##########################################################
@@ -741,7 +723,7 @@ screen preferences():
hbox:
box_wrap True
if renpy.variant("pc") or renpy.variant("web"):
if renpy.variant("pc"):
vbox:
style_prefix "radio"
@@ -749,6 +731,13 @@ screen preferences():
textbutton _("Window") action Preference("display", "window")
textbutton _("Fullscreen") action Preference("display", "fullscreen")
vbox:
style_prefix "radio"
label _("Rollback Side")
textbutton _("Disable") action Preference("rollback side", "disable")
textbutton _("Left") action Preference("rollback side", "left")
textbutton _("Right") action Preference("rollback side", "right")
vbox:
style_prefix "check"
label _("Skip")
@@ -852,10 +841,10 @@ style radio_vbox:
style radio_button:
properties gui.button_properties("radio_button")
foreground "gui/button/radio_[prefix_]foreground.png"
foreground "gui/button/check_[prefix_]foreground.png"
style radio_button_text:
properties gui.text_properties("radio_button")
properties gui.button_text_properties("radio_button")
style check_vbox:
spacing gui.pref_button_spacing
@@ -865,7 +854,7 @@ style check_button:
foreground "gui/button/check_[prefix_]foreground.png"
style check_button_text:
properties gui.text_properties("check_button")
properties gui.button_text_properties("check_button")
style slider_slider:
xsize gui.scale(350)
@@ -876,7 +865,7 @@ style slider_button:
left_margin gui.scale(10)
style slider_button_text:
properties gui.text_properties("slider_button")
properties gui.button_text_properties("slider_button")
style slider_vbox:
xsize gui.scale(450)
@@ -897,7 +886,7 @@ screen history():
## Avoid predicting this screen, as it can be very large.
predict False
use game_menu(_("History"), scroll=("vpgrid" if gui.history_height else "viewport"), yinitial=1.0, spacing=gui.history_spacing):
use game_menu(_("History"), scroll=("vpgrid" if gui.history_height else "viewport"), yinitial=1.0):
style_prefix "history"
@@ -913,15 +902,13 @@ screen history():
label h.who:
style "history_name"
substitute False
## Take the color of the who text from the Character, if set.
if "color" in h.who_args:
text_color h.who_args["color"]
$ what = renpy.filter_text_tags(h.what, allow=gui.history_allow_tags)
text what:
substitute False
text what
if not _history_list:
label _("The dialogue history is empty.")
@@ -930,7 +917,7 @@ screen history():
## This determines what tags are allowed to be displayed on the history
## screen.
define gui.history_allow_tags = { "alt", "noalt", "rt", "rb", "art" }
define gui.history_allow_tags = set()
style history_window is empty
@@ -939,6 +926,8 @@ style history_name is gui_label
style history_name_text is gui_label_text
style history_text is gui_text
style history_text is gui_text
style history_label is gui_label
style history_label_text is gui_label_text
@@ -954,7 +943,7 @@ style history_name:
style history_name_text:
min_width gui.history_name_width
textalign gui.history_name_xalign
text_align gui.history_name_xalign
style history_text:
xpos gui.history_text_xpos
@@ -962,7 +951,7 @@ style history_text:
xanchor gui.history_text_xalign
xsize gui.history_text_width
min_width gui.history_text_width
textalign gui.history_text_xalign
text_align gui.history_text_xalign
layout ("subtitle" if gui.history_text_xalign else "tex")
style history_label:
@@ -1053,10 +1042,6 @@ screen keyboard_help():
label "V"
text _("Toggles assistive {a=https://www.renpy.org/l/voicing}self-voicing{/a}.")
hbox:
label "Shift+A"
text _("Opens the accessibility menu.")
screen mouse_help():
@@ -1073,7 +1058,7 @@ screen mouse_help():
text _("Accesses the game menu.")
hbox:
label _("Mouse Wheel Up")
label _("Mouse Wheel Up\nClick Rollback Side")
text _("Rolls back to earlier dialogue.")
hbox:
@@ -1095,12 +1080,13 @@ screen gamepad_help():
label _("Right Shoulder")
text _("Rolls forward to later dialogue.")
hbox:
label _("D-Pad, Sticks")
text _("Navigate the interface.")
hbox:
label _("Start, Guide, B/Right Button")
label _("Start, Guide")
text _("Accesses the game menu.")
hbox:
@@ -1121,7 +1107,7 @@ style help_button:
xmargin gui.scale(8)
style help_button_text:
properties gui.text_properties("help_button")
properties gui.button_text_properties("help_button")
style help_label:
xsize gui.scale(250)
@@ -1130,7 +1116,7 @@ style help_label:
style help_label_text:
size gui.text_size
xalign 1.0
textalign 1.0
text_align 1.0
@@ -1192,14 +1178,14 @@ style confirm_frame:
yalign .5
style confirm_prompt_text:
textalign 0.5
text_align 0.5
layout "subtitle"
style confirm_button:
properties gui.button_properties("confirm_button")
style confirm_button_text:
properties gui.text_properties("confirm_button")
properties gui.button_text_properties("confirm_button")
## Skip indicator screen #######################################################
@@ -1326,7 +1312,7 @@ screen nvl(dialogue, items=None):
use nvl_dialogue(dialogue)
## Displays the menu, if given. The menu may be displayed incorrectly
## if config.narrator_menu is set to True.
## if config.narrator_menu is set to True, as it is above.
for i in items:
textbutton i.caption:
@@ -1357,7 +1343,7 @@ screen nvl_dialogue(dialogue):
## This controls the maximum number of NVL-mode entries that can be displayed
## at once.
define config.nvl_list_length = gui.nvl_list_length
define config.nvl_list_length = 6
style nvl_window is default
style nvl_entry is default
@@ -1386,7 +1372,7 @@ style nvl_label:
yanchor 0.0
xsize gui.nvl_name_width
min_width gui.nvl_name_width
textalign gui.nvl_name_xalign
text_align gui.nvl_name_xalign
style nvl_dialogue:
xpos gui.nvl_text_xpos
@@ -1394,7 +1380,7 @@ style nvl_dialogue:
ypos gui.nvl_text_ypos
xsize gui.nvl_text_width
min_width gui.nvl_text_width
textalign gui.nvl_text_xalign
text_align gui.nvl_text_xalign
layout ("subtitle" if gui.nvl_text_xalign else "tex")
style nvl_thought:
@@ -1403,7 +1389,7 @@ style nvl_thought:
ypos gui.nvl_thought_ypos
xsize gui.nvl_thought_width
min_width gui.nvl_thought_width
textalign gui.nvl_thought_xalign
text_align gui.nvl_thought_xalign
layout ("subtitle" if gui.nvl_text_xalign else "tex")
style nvl_button:
@@ -1412,96 +1398,7 @@ style nvl_button:
xanchor gui.nvl_button_xalign
style nvl_button_text:
properties gui.text_properties("nvl_button")
## Bubble screen ###############################################################
##
## The bubble screen is used to display dialogue to the player when using
## speech bubbles. The bubble screen takes the same parameters as the say
## screen, must create a displayable with the id of "what", and can create
## displayables with the "namebox", "who", and "window" ids.
##
## https://www.renpy.org/doc/html/bubble.html#bubble-screen
screen bubble(who, what):
style_prefix "bubble"
window:
id "window"
if who is not None:
window:
id "namebox"
style "bubble_namebox"
text who:
id "who"
text what:
id "what"
style bubble_window is empty
style bubble_namebox is empty
style bubble_who is default
style bubble_what is default
style bubble_window:
xpadding 30
top_padding 5
bottom_padding 5
style bubble_namebox:
xalign 0.5
style bubble_who:
xalign 0.5
textalign 0.5
color "#000"
style bubble_what:
align (0.5, 0.5)
text_align 0.5
layout "subtitle"
color "#000"
define bubble.frame = Frame("gui/bubble.png", 55, 55, 55, 95)
define bubble.thoughtframe = Frame("gui/thoughtbubble.png", 55, 55, 55, 55)
define bubble.properties = {
"bottom_left" : {
"window_background" : Transform(bubble.frame, xzoom=1, yzoom=1),
"window_bottom_padding" : 27,
},
"bottom_right" : {
"window_background" : Transform(bubble.frame, xzoom=-1, yzoom=1),
"window_bottom_padding" : 27,
},
"top_left" : {
"window_background" : Transform(bubble.frame, xzoom=1, yzoom=-1),
"window_top_padding" : 27,
},
"top_right" : {
"window_background" : Transform(bubble.frame, xzoom=-1, yzoom=-1),
"window_top_padding" : 27,
},
"thought" : {
"window_background" : bubble.thoughtframe,
}
}
define bubble.expand_area = {
"bottom_left" : (0, 0, 0, 22),
"bottom_right" : (0, 0, 0, 22),
"top_left" : (0, 22, 0, 0),
"top_right" : (0, 22, 0, 0),
"thought" : (0, 0, 0, 0),
}
properties gui.button_text_properties("nvl_button")
@@ -1520,16 +1417,16 @@ screen quick_menu():
zorder 100
if quick_menu:
hbox:
style_prefix "quick"
hbox:
style "quick_menu"
style_prefix "quick"
xalign 0.5
yalign 1.0
textbutton _("Back") action Rollback()
textbutton _("Skip") action Skip() alternate Skip(fast=True, confirm=True)
textbutton _("Auto") action Preference("auto-forward", "toggle")
textbutton _("Menu") action ShowMenu()
textbutton _("Back") action Rollback()
textbutton _("Skip") action Skip() alternate Skip(fast=True, confirm=True)
textbutton _("Auto") action Preference("auto-forward", "toggle")
textbutton _("Menu") action ShowMenu()
style window:
@@ -1538,7 +1435,7 @@ style window:
style radio_button:
variant "small"
foreground "gui/phone/button/radio_[prefix_]foreground.png"
foreground "gui/phone/button/check_[prefix_]foreground.png"
style check_button:
variant "small"
@@ -1604,10 +1501,15 @@ style vslider:
base_bar Frame("gui/phone/slider/vertical_[prefix_]bar.png", gui.vslider_borders, tile=gui.slider_tile)
thumb "gui/phone/slider/vertical_[prefix_]thumb.png"
style slider_vbox:
style slider_pref_vbox:
variant "small"
xsize None
style slider_slider:
style slider_pref_slider:
variant "small"
xsize gui.scale(600)
+178
View File
@@ -0,0 +1,178 @@
<html>
<head>
<title>README</title>
<style>
BODY {
background: #fff;
color: #444;
padding-left: 20%;
padding-top: 1em;
padding-bottom: 1em;
padding-right: 20%;
font-family: sans-serif;
line-height: 1.6em;
}
DT {
font-weight: bold;
margin-top: .33em;
}
H2 {
color: #000;
margin-left: -2.5em;
}
H3 {
color: #000;
margin-left: -1.5em;
}
.editsection {
display: none;
}
</style>
</head>
<body>
<h2>Basic Help</h2>
<p>
To advance through the game, <tt>left-click</tt> or press the <tt>space</tt> or <tt>enter</tt> keys. When at a menu,
<tt>left-click</tt> to make a choice, or use the arrow keys to select a choice and <tt>enter</tt> to activate it.
</p>
<h3>Game Menu</h3>
<p>
When playing a game, <tt>right-click</tt> or press the <tt>escape</tt> key to enter the game menu. The game menu
gives the following choices:
</p>
<dl>
<dt>Return</dt>
<dd>Returns to the game.</dd>
<dt>Save Game</dt>
<dd>Allows you to save a game by clicking on a save slot.</dd>
<dt>Load Game</dt>
<dd>Allows you to load a game by clicking on a save slot. Clicking on "Auto" accesses the automatic save slots.</dd>
<dt>Preferences</dt>
<dd>
Changes the game preferences (options/configuration):
<dl>
<dt>Display</dt>
<dd>Switches between fullscreen and windowed mode.</dd>
<dt>Transitions</dt>
<dd>Controls the display of transitions between game screens.</dd>
<dt>Text Speed</dt>
<dd>Controls the rate at which text displays. The further to the right this slider is, the faster the text
will display. All the way to the right causes text to be shown instantly.</dd>
<dt>Joystick</dt>
<dd>Lets you control the game using a joystick.</dd>
<dt>Skip</dt>
<dd>Chooses between skipping messages that have been already seen (in any play through the game), and
skipping all messages.</dd>
<dt>Begin Skipping</dt>
<dd>Returns to the game, while skipping.</dd>
<dt>After Choices</dt>
<dd>Controls if skipping stops upon reaching a menu.</dd>
<dt>Auto-Forward Time</dt>
<dd>Controls automatic advance. The further to the left this slider is, the shorter the amount of time
before the game advances. All the way to the right means text will never auto-forward.</dd>
<dt>Music, Sound, and Voice Volume</dt>
<dd>Controls the volume of the Music, Sound effect, and Voice channels, respectively. The further to the
right these are, the louder the volume.</dd>
</dl>
</dd>
<dt>Main Menu</dt>
<dd>Returns to the main menu, ending the current game.</dd>
<dt>Help</dt>
<dd>Shows this help screen.</dd>
<dt>Quit</dt>
<dd>Exits the game; the game will be closed and ended.</dd>
</dl>
<h3>Key and Mouse Bindings</h3>
<dl>
<dt>Left-click, Enter</dt>
<dd>Advances through the game, activates menu choices, buttons, and sliders.</dd>
<dt>Space</dt>
<dd>Advances through the game, but does not activate choices.</dd>
<dt>Arrow Keys</dt>
<dd>Navigates between menu choices, buttons, and sliders.</dd>
<dt>Ctrl</dt>
<dd>Causes skipping to occur while the ctrl key is held down.</dd>
<dt>Tab</dt>
<dd>Toggles skipping, causing it to occur until tab is pressed again.</dd>
<dt>Mousewheel-Up, PageUp</dt>
<dd>Causes rollback to occur. Rollback reverses the game back in time, showing prior text and even allowing
menu choices to be changed.</dd>
<dt>Mousewheel-Down, PageDown</dt>
<dd>Causes rollforward to occur, canceling out a previous rollback.</dd>
<dt>Right-click, Escape</dt>
<dd>Enters the game menu. When in the game menu, returns to the game.</dd>
<dt>Middle-click, H</dt>
<dd>Hides the text window and other transient displays.</dd>
<dt>F</dt>
<dd>Toggles fullscreen mode</dd>
<dt>S</dt>
<dd>Takes a screenshot, saving it in a file named screenshotxxxx.png, where xxxx is a serial number.</dd>
<dt>Alt-M, Command-H</dt>
<dd>Hides (iconifies) the window.</dd>
<dt>Alt-F4, Command-Q</dt>
<dd>Quits the game.</dd>
<dt>Delete</dt>
<dd>When a save slot is selected, deletes that save slot.</dd>
<dt>v</dt>
<dd>
Toggles self-voicing mode, which reads text to the user using an os-supplied speech synthesizer. For more
information, please read the <a href="https://www.renpy.org/doc/html/self_voicing.html">self-voicing</a>
documentation.
</dd>
<dt>Shift+C</dt>
<dd>Toggles clipboard-voicing mode, which copies text to the clipboard so it can be read by a screen reader.</dd>
</dl>
<h3>Controller Support and Bindings</h3>
<p>
This game should automatically detect and use game controllers supported by
SDL2. Other controllers can be configured using third-party configuration tools
like the <a href="http://www.generalarcade.com/gamepadtool/">SDL2 Gampad Tool</a>
</p>
<p>
A small number of systems may have problems using detected game controllers. Should
that happen, hold down shift as the game starts, and disable the controller support.
</p>
<p>The following bindings are used:</p>
<dl>
<dt>Right Trigger, A (Bottom Button)</dt>
<dd>Advances through the game, activates menu choices, buttons, and sliders.</dd>
<dt>Guide, Start</dt>
<dd>Enters the game menu. When in the game menu, returns to the game.</dd>
<dt>Directional Pad, Analog Sticks</dt>
<dd>Navigates between menu choices, buttons, and sliders.</dd>
<dt>Left Trigger, Left Shoulder, Back</dt>
<dd>Causes rollback to occur. Rollback reverses the game back in time, showing prior text and even allowing
menu choices to be changed.</dd>
<dt>Right Shoulder</dt>
<dd>Causes rollforward to occur, canceling out a previous rollback.</dd>
<dt>Y (Top Button)</dt>
<dd>Hides the text window and other transient displays.</dd>
</dl>
<h2>Legal Notice</h2>
<p>
This program contains free software licensed under a number of licenses, including the GNU Lesser Public License. A
complete list of software is available at <a href="https://www.renpy.org/doc/html/license.html">https://www.renpy.org/doc/html/license.html</a>.
</p>
</body>
</html>
-86
View File
@@ -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,93 +0,0 @@
import os
import subprocess
import sys
import renpy
class Editor(renpy.editor.Editor):
has_projects = True
system = __file__.endswith(" (System).edit.py")
def get_code(self):
"""
Returns the path to the code executable.
"""
if self.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 = "x64"
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()
if self.system or not renpy.linux:
args = [ code, "-g" ] + self.args
else:
args = [ code, "--no-sandbox", "-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()
-93
View File
@@ -1,93 +0,0 @@
import os
import subprocess
import sys
import renpy
class Editor(renpy.editor.Editor):
has_projects = True
system = __file__.endswith(" (System).edit.py")
def get_code(self):
"""
Returns the path to the code executable.
"""
if self.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 = "x64"
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()
if self.system or not renpy.linux:
args = [ code, "-g" ] + self.args
else:
args = [ code, "--no-sandbox", "-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()
+144
View File
@@ -0,0 +1,144 @@
import binascii
def a2b(a):
return binascii.a2b_hex(''.join(a.split()))
resources = {
260 : a2b('''
01 00 ff ff 00 00 00 00 00 00 00 00 c8 00 c0 80
02 00 00 00 00 00 b8 00 29 00 00 00 00 00 00 00
08 00 90 01 00 01 4d 00 53 00 20 00 53 00 68 00
65 00 6c 00 6c 00 20 00 44 00 6c 00 67 00 00 00
00 00 00 00 00 00 00 00 01 00 01 50 7f 00 14 00
32 00 0e 00 01 00 00 00 ff ff 80 00 4f 00 4b 00
00 00 00 00 00 00 00 00 00 00 00 00 04 08 00 50
07 00 07 00 aa 00 0c 00 ea 03 00 00 ff ff 81 00
00 00 00 00
'''),
261 : a2b('''
01 00 ff ff 00 00 00 00 00 00 00 00 c8 00 c0 80
04 00 00 00 00 00 b8 00 3a 00 00 00 00 00 00 00
08 00 90 01 00 01 4d 00 53 00 20 00 53 00 68 00
65 00 6c 00 6c 00 20 00 44 00 6c 00 67 00 00 00
00 00 00 00 00 00 00 00 80 00 81 50 07 00 14 00
aa 00 0c 00 ec 03 00 00 ff ff 81 00 00 00 00 00
00 00 00 00 00 00 00 00 01 00 01 50 7f 00 25 00
32 00 0e 00 01 00 00 00 ff ff 80 00 4f 00 4b 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 50
4a 00 25 00 32 00 0e 00 02 00 00 00 ff ff 80 00
43 00 61 00 6e 00 63 00 65 00 6c 00 00 00 00 00
00 00 00 00 00 00 00 00 04 08 00 50 07 00 07 00
aa 00 0c 00 eb 03 00 00 ff ff 81 00 00 00 00 00
'''),
262 : a2b('''
01 00 ff ff 00 00 00 00 00 00 00 00 c8 00 c0 80
04 00 00 00 00 00 b8 00 29 00 00 00 00 00 00 00
08 00 90 01 00 01 4d 00 53 00 20 00 53 00 68 00
65 00 6c 00 6c 00 20 00 44 00 6c 00 67 00 00 00
00 00 00 00 00 00 00 00 00 00 01 50 7f 00 14 00
32 00 0e 00 06 00 00 00 ff ff 80 00 59 00 65 00
73 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
04 08 00 50 07 00 07 00 aa 00 0c 00 ed 03 00 00
ff ff 81 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 01 50 07 00 14 00 32 00 0e 00 07 00 00 00
ff ff 80 00 4e 00 6f 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 01 50 4b 00 14 00 32 00 0e 00
02 00 00 00 ff ff 80 00 43 00 61 00 6e 00 63 00
65 00 6c 00 00 00 00 00
'''),
263 : a2b('''
01 00 ff ff 00 00 00 00 00 00 00 00 80 00 ca 80
03 00 00 00 00 00 e2 00 29 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 81 50 07 00 14 00
97 00 0e 00 eb 03 00 00 6d 00 73 00 63 00 74 00
6c 00 73 00 5f 00 70 00 72 00 6f 00 67 00 72 00
65 00 73 00 73 00 33 00 32 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 04 08 00 50 07 00 07 00
d4 00 0c 00 ea 03 00 00 ff ff 81 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 50 a9 00 14 00
32 00 0e 00 02 00 00 00 ff ff 80 00 43 00 61 00
6e 00 63 00 65 00 6c 00 00 00 00 00
'''),
264 : a2b('''
01 00 ff ff 00 00 00 00 00 00 00 00 c8 00 c0 80
04 00 00 00 00 00 b8 00 3a 00 00 00 00 00 00 00
08 00 90 01 00 01 4d 00 53 00 20 00 53 00 68 00
65 00 6c 00 6c 00 20 00 44 00 6c 00 67 00 00 00
00 00 00 00 00 00 00 00 a0 00 81 50 07 00 14 00
aa 00 0c 00 ec 03 00 00 ff ff 81 00 00 00 00 00
00 00 00 00 00 00 00 00 01 00 01 50 7f 00 25 00
32 00 0e 00 01 00 00 00 ff ff 80 00 4f 00 4b 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 50
4a 00 25 00 32 00 0e 00 02 00 00 00 ff ff 80 00
43 00 61 00 6e 00 63 00 65 00 6c 00 00 00 00 00
00 00 00 00 00 00 00 00 04 08 00 50 07 00 07 00
aa 00 0c 00 eb 03 00 00 ff ff 81 00 00 00 00 00
'''),
265 : a2b('''
01 00 ff ff 00 00 00 00 00 00 00 00 c8 00 c8 80
12 00 00 00 00 00 d9 00 fc 00 00 00 00 00 00 00
08 00 90 01 00 01 4d 00 53 00 20 00 53 00 68 00
65 00 6c 00 6c 00 20 00 44 00 6c 00 67 00 00 00
00 00 00 00 00 00 00 00 01 00 01 50 6c 00 ea 00
30 00 0e 00 01 00 00 00 ff ff 80 00 4f 00 4b 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 50
a2 00 ea 00 30 00 0e 00 02 00 00 00 ff ff 80 00
43 00 61 00 6e 00 63 00 65 00 6c 00 00 00 00 00
00 00 00 00 00 00 00 00 07 00 00 50 06 00 06 00
cc 00 4e 00 ff ff ff ff ff ff 80 00 00 00 00 00
00 00 00 00 00 00 00 00 03 00 21 50 48 00 12 00
84 00 64 00 03 00 00 00 ff ff 85 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 02 50 0c 00 24 00
c0 00 08 00 04 00 00 00 ff ff 82 00 00 00 00 00
00 00 00 00 00 00 00 00 80 00 81 50 0c 00 30 00
c0 00 0e 00 05 00 00 00 ff ff 81 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 01 50 9c 00 42 00
30 00 0e 00 06 00 00 00 ff ff 80 00 41 00 64 00
64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
07 00 00 50 06 00 54 00 cb 00 3c 00 ff ff ff ff
ff ff 80 00 00 00 00 00 00 00 00 00 00 00 00 00
03 00 21 50 48 00 61 00 84 00 64 00 07 00 00 00
ff ff 85 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 02 50 0c 00 72 00 c0 00 08 00 08 00 00 00
ff ff 82 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 01 50 9c 00 7e 00 30 00 0e 00 09 00 00 00
ff ff 80 00 41 00 64 00 64 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 01 50 06 00 96 00
42 00 0e 00 0a 00 00 00 ff ff 80 00 41 00 64 00
64 00 20 00 66 00 69 00 6c 00 65 00 2e 00 2e 00
2e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 01 50 4e 00 96 00 42 00 0e 00 0b 00 00 00
ff ff 80 00 41 00 64 00 64 00 20 00 6e 00 65 00
77 00 20 00 66 00 69 00 6c 00 65 00 2e 00 2e 00
2e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 01 50 06 00 a8 00 42 00 0e 00 0c 00 00 00
ff ff 80 00 41 00 64 00 64 00 20 00 66 00 6f 00
6c 00 64 00 65 00 72 00 2e 00 2e 00 2e 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 07 00 00 50
06 00 c6 00 cc 00 1e 00 ff ff ff ff ff ff 80 00
43 00 6f 00 6d 00 6d 00 61 00 6e 00 64 00 20 00
6c 00 69 00 6e 00 65 00 3a 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 80 00 81 50 0c 00 d2 00
c0 00 0e 00 0e 00 00 00 ff ff 81 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 02 50 0c 00 14 00
3c 00 08 00 1e 00 00 00 ff ff 82 00 4f 00 70 00
74 00 69 00 6f 00 6e 00 3a 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 02 50 0c 00 63 00
3c 00 08 00 46 00 00 00 ff ff 82 00 43 00 6f 00
6d 00 6d 00 61 00 6e 00 64 00 3a 00 00 00 00 00
'''),
270 : a2b('''
01 00 ff ff 00 00 00 00 00 00 00 00 48 04 00 44
02 00 00 00 00 00 23 01 1a 00 00 00 00 00 00 00
08 00 00 00 00 00 4d 00 53 00 20 00 53 00 68 00
65 00 6c 00 6c 00 20 00 44 00 6c 00 67 00 00 00
00 00 00 00 00 00 00 00 01 00 02 50 00 00 07 00
23 01 08 00 ff ff ff ff ff ff 82 00 53 00 74 00
61 00 74 00 69 00 63 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 02 40 00 00 12 00 23 01 08 00
5f 04 00 00 ff ff 82 00 73 00 74 00 63 00 33 00
32 00 00 00 00 00
'''),
}
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
+1 -1
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
+17 -26
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -23,38 +23,28 @@ label add_file:
python hide:
import os
filename = ""
while True:
filename = interface.input(
_("FILENAME"),
_("Enter the name of the script file to create."),
allow=interface.FILENAME_LETTERS,
cancel=Jump("navigation"),
default=filename,
)
filename = filename.strip()
if not filename:
interface.error(_("The file name may not be empty."), label=None)
continue
import codecs
if "." in filename and not filename.endswith(".rpy"):
interface.error(_("The filename must have the .rpy extension."), label=None)
continue
elif "." not in filename:
filename += ".rpy"
filename = interface.input(_("FILENAME"), _("Enter the name of the script file to create."), filename="withslash", cancel=Jump("navigation"))
path = os.path.join(project.current.gamedir, filename)
dir = os.path.dirname(path)
if "." in filename and not filename.endswith(".rpy"):
interface.error(_("The filename must have the .rpy extension."), label="navigation")
elif "." not in filename:
filename += ".rpy"
if os.path.exists(path):
interface.error(_("The file already exists."), label=None)
continue
path = os.path.join(project.current.gamedir, filename)
dir = os.path.dirname(path)
break
if os.path.exists(path):
interface.error(_("The file already exists."), label="navigation")
contents = u"\uFEFF"
contents += _("# Ren'Py automatically loads all script files ending with .rpy. To use this\n# file, define a label and jump to it from another file.\n")
contents += "\n"
try:
os.makedirs(dir)
except Exception:
except:
pass
contents = u"\uFEFF"
@@ -64,3 +54,4 @@ label add_file:
f.write(contents.encode("utf-8"))
jump navigation_refresh
+116 -283
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -19,48 +19,36 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
default persistent.android_bundle = False
init python:
ANDROID_NO_RAPT = 0
ANDROID_NO_JDK = 1
ANDROID_NO_SDK = 2
ANDROID_NO_KEY = 3
ANDROID_NO_BUNDLE_KEY = 4
ANDROID_NO_CONFIG = 5
ANDROID_NO_BUNDLE = 6
ANDROID_OK = 7
JDK_REQUIREMENT = 42
ANDROID_NO_CONFIG = 4
ANDROID_OK = 5
NO_RAPT_TEXT = _("To build Android packages, please download RAPT, unzip it, and place it into the Ren'Py directory. Then restart the Ren'Py launcher.")
NO_JDK_TEXT = _("A 32-bit Java Development Kit is required to build Android packages on Windows. The JDK is different from the JRE, so it's possible you have Java without having the JDK.\n\nPlease {a=http://www.oracle.com/technetwork/java/javase/downloads/index.html}download and install the JDK{/a}, then restart the Ren'Py launcher.")
NO_SDK_TEXT = _("RAPT has been installed, but you'll need to install the Android SDK before you can build Android packages. Choose Install SDK to do this.")
NO_KEY_TEXT = _("RAPT has been installed, but a key hasn't been configured. Please generate new keys, or copy android.keystore and bundle.keystore to the base directory.")
NO_KEY_TEXT = _("RAPT has been installed, but a key hasn't been configured. Please create a new key, or restore android.keystore.")
NO_CONFIG_TEXT = _("The current project has not been configured. Use \"Configure\" to configure it before building.")
NO_BUNDLE_TEXT = _("Please select if you want a Play Bundle (for Google Play), or a Universal APK (for sideloading and other app stores).")
OK_TEXT = _("Choose \"Build\" to build the current project, or attach an Android device and choose \"Build & Install\" to build and install it on the device.")
PHONE_TEXT = _("Attempts to emulate an Android phone.\n\nTouch input is emulated through the mouse, but only when the button is held down. Escape is mapped to the menu button, and PageUp is mapped to the back button.")
TABLET_TEXT = _("Attempts to emulate an Android tablet.\n\nTouch input is emulated through the mouse, but only when the button is held down. Escape is mapped to the menu button, and PageUp is mapped to the back button.")
OUYA_TEXT = _("Attempts to emulate a televison-based Android console.\n\nController input is mapped to the arrow keys, Enter is mapped to the select button, Escape is mapped to the menu button, and PageUp is mapped to the back button.")
OUYA_TEXT = _("Attempts to emulate a televison-based Android console, like the OUYA or Fire TV.\n\nController input is mapped to the arrow keys, Enter is mapped to the select button, Escape is mapped to the menu button, and PageUp is mapped to the back button.")
INSTALL_SDK_TEXT = _("Downloads and installs the Android SDK and supporting packages.")
GENERATE_KEYS_TEXT = _("Generates the keys required to sign the package.")
INSTALL_SDK_TEXT = _("Downloads and installs the Android SDK and supporting packages. Optionally, generates the keys required to sign the package.")
CONFIGURE_TEXT = _("Configures the package name, version, and other information about this project.")
PLAY_KEYS_TEXT = _("Opens the file containing the Google Play keys in the editor.\n\nThis is only needed if the application is using an expansion APK. Read the documentation for more details.")
BUILD_TEXT = _("Builds the Android package.")
BUILD_AND_INSTALL_TEXT = _("Builds the Android package, and installs it on an Android device connected to your computer.")
BUILD_INSTALL_AND_LAUNCH_TEXT = _("Builds the Android package, installs it on an Android device connected to your computer, then launches the app on your device.")
CONNECT_TEXT = _("Connects to an Android device running ADB in TCP/IP mode.")
DISCONNECT_TEXT = _("Disconnects from an Android device running ADB in TCP/IP mode.")
LOGCAT_TEXT = _("Retrieves the log from the Android device and writes it to a file.")
LIST_DEVICES_TEXT = _("Lists the connected devices.")
PAIR_TEXT = _("Pairs with a device over Wi-Fi, on Android 11+.")
CONNECT_TEXT = _("Connects to a device over Wi-Fi, on Android 11+.")
DISCONNECT_TEXT = _("Disconnects a device connected over Wi-Fi.")
CLEAN_TEXT = _("Removes Android temporary files.")
PLAY_BUNDLE_TEXT = _("Builds an Android App Bundle (ABB), intended to be uploaded to Google Play. This can include up to 2GB of data.")
UNIVERSAL_APK_TEXT = _("Builds a Universal APK package, intended for sideloading and stores other than Google Play. This can include up to 2GB of data.")
import subprocess
import re
@@ -93,17 +81,11 @@ init python:
import rapt.install_sdk
import rapt.plat
import rapt.interface
import rapt.keys
rapt.plat.renpy = True
rapt.plat.translate = __
JDK_REQUIREMENT = rapt.plat.jdk_requirement
else:
rapt = None
def AndroidState():
"""
Determines the state of the android install, and returns it.
@@ -111,18 +93,16 @@ init python:
if RAPT_PATH is None:
return ANDROID_NO_RAPT
if not os.path.exists(rapt.plat.adb):
if renpy.windows and not "JAVA_HOME" in os.environ:
return ANDROID_NO_JDK
if not os.path.exists(rapt.plat.path("android-sdk/platforms/" + rapt.plat.target)):
return ANDROID_NO_SDK
if not rapt.keys.keys_exist(project.current.path):
if not os.path.exists(rapt.plat.path("android.keystore")):
return ANDROID_NO_KEY
if not any([
os.path.exists(os.path.join(project.current.path, "android.json")),
os.path.exists(os.path.join(project.current.path, ".android.json"))
]):
if not os.path.exists(rapt.plat.path("local.properties")):
return ANDROID_NO_KEY
if not os.path.exists(os.path.join(project.current.path, ".android.json")):
return ANDROID_NO_CONFIG
if persistent.android_bundle is None:
return ANDROID_NO_BUNDLE
return ANDROID_OK
@@ -133,14 +113,14 @@ init python:
if state == ANDROID_NO_RAPT:
return NO_RAPT_TEXT
if state == ANDROID_NO_JDK:
return NO_JDK_TEXT
if state == ANDROID_NO_SDK:
return NO_SDK_TEXT
if state == ANDROID_NO_KEY:
return NO_KEY_TEXT
if state == ANDROID_NO_CONFIG:
return NO_CONFIG_TEXT
if state == ANDROID_NO_BUNDLE:
return NO_BUNDLE_TEXT
if state == ANDROID_OK:
return OK_TEXT
@@ -179,26 +159,30 @@ init python:
build = p.dump["build"]
c = rapt.configure.Configuration(p.path)
filename = os.path.join(p.path, ".android.json")
with open(filename, "r") as f:
android_json = json.load(f)
if "google_play_key" in build:
c.google_play_key = build["google_play_key"]
android_json["google_play_key"] = build["google_play_key"]
else:
c.google_play_key = None
android_json.pop("google_play_key", None)
if "google_play_salt" in build:
if len(build["google_play_salt"]) != 20:
raise Exception("build.google_play_salt must be exactly 20 bytes long.")
c.google_play_salt = ", ".join(str(i) for i in build["google_play_salt"])
android_json["google_play_salt"] = ", ".join(str(i) for i in build["google_play_salt"])
else:
c.google_play_salt = None
c.save(p.path)
android_json.pop("google_play_salt", None)
def android_build(p=None, gui=True, bundle=False, install=False, launch=False, destination=None, opendir=False, packages=None):
with open(filename, "w") as f:
json.dump(android_json, f)
def android_build(command, p=None, gui=True, launch=False, destination=None, opendir=False):
"""
This actually builds the package.
"""
@@ -220,12 +204,9 @@ init python:
reporter = distribute.TextReporter()
rapt_interface = rapt.interface.Interface()
if not packages:
packages = ['android']
distribute.Distributor(p,
reporter=reporter,
packages=packages,
packages=[ 'android' ],
build_update=False,
noarchive=True,
packagedest=dist,
@@ -249,7 +230,7 @@ init python:
parent = os.path.dirname(p.path)
destination_dir = os.path.join(parent, destination)
except Exception:
except:
destination_dir = None
dir_to_open = source_dir
@@ -260,7 +241,7 @@ init python:
try:
os.makedirs(destination_dir)
except Exception:
except:
pass
try:
@@ -270,31 +251,17 @@ init python:
dir_to_open = destination_dir
except Exception:
except:
import traceback
traceback.print_exc()
pass
if opendir:
dir_to_open = os.path.join(p.path, dir_to_open)
renpy.run(store.OpenDirectory(dir_to_open, absolute=True))
store.OpenDirectory(dir_to_open)()
with interface.nolinks():
rapt.build.build(
rapt_interface,
dist,
p.path,
bundle=bundle,
install=install,
launch=launch,
finished=finished,
permissions=p.dump['build']['android_permissions'],
version=p.dump['build']['version'])
def android_build_argument(cmd):
return cmd + project.current.data["android_build"]
rapt.build.build(rapt_interface, dist, command, launch=launch, finished=finished)
@@ -390,32 +357,15 @@ screen android:
frame style "l_indent":
has vbox
textbutton _("Install SDK"):
textbutton _("Install SDK & Create Keys"):
action AndroidIfState(state, ANDROID_NO_SDK, Jump("android_installsdk"))
hovered tt.Action(INSTALL_SDK_TEXT)
textbutton _("Generate Keys"):
action AndroidIfState(state, ANDROID_NO_KEY, Jump("android_keys"))
hovered tt.Action(GENERATE_KEYS_TEXT)
textbutton _("Configure"):
action AndroidIfState(state, ANDROID_NO_KEY, Jump("android_configure"))
action AndroidIfState(state, ANDROID_NO_CONFIG, Jump("android_configure"))
hovered tt.Action(CONFIGURE_TEXT)
add SPACER
textbutton _("Play Bundle"):
action SetField(persistent, "android_bundle", True)
hovered tt.Action(PLAY_BUNDLE_TEXT)
style "l_checkbox"
textbutton _("Universal APK"):
action SetField(persistent, "android_bundle", False)
hovered tt.Action(UNIVERSAL_APK_TEXT)
style "l_checkbox"
add SPACER
textbutton _("Build Package"):
action AndroidIfState(state, ANDROID_OK, AndroidBuild("android_build"))
hovered tt.Action(BUILD_TEXT)
@@ -428,19 +378,7 @@ screen android:
action AndroidIfState(state, ANDROID_OK, AndroidBuild("android_build_install_and_launch"))
hovered tt.Action(BUILD_INSTALL_AND_LAUNCH_TEXT)
add SPACER
textbutton _("Force Recompile") action DataToggle("force_recompile") style "l_checkbox"
# Right side.
frame:
style "l_indent"
xmaximum ONEHALF
xfill True
has vbox
add SPACER
add SEPARATOR2
frame:
@@ -455,31 +393,27 @@ screen android:
has vbox
textbutton _("Remote ADB Connect"):
action AndroidIfState(state, ANDROID_OK, Jump("android_connect"))
hovered tt.Action(CONNECT_TEXT)
textbutton _("Remote ADB Disconnect"):
action AndroidIfState(state, ANDROID_OK, Jump("android_disconnect"))
hovered tt.Action(DISCONNECT_TEXT)
textbutton _("Logcat"):
action AndroidIfState(state, ANDROID_NO_KEY, Jump("logcat"))
hovered tt.Action(LOGCAT_TEXT)
textbutton _("List Devices"):
action AndroidIfState(state, ANDROID_NO_KEY, Jump("android_list_devices"))
hovered tt.Action(LIST_DEVICES_TEXT)
textbutton _("Wi-Fi Debugging Pair"):
action AndroidIfState(state, ANDROID_NO_KEY, Jump("android_pair"))
hovered tt.Action(PAIR_TEXT)
# Right side.
frame:
style "l_indent"
xmaximum ONEHALF
xfill True
textbutton _("Wi-Fi Debugging Connect"):
action AndroidIfState(state, ANDROID_NO_KEY, Jump("android_connect"))
hovered tt.Action(CONNECT_TEXT)
has vbox
textbutton _("Wi-Fi Debugging Disconnect"):
action AndroidIfState(state, ANDROID_NO_KEY, Jump("android_disconnect"))
hovered tt.Action(DISCONNECT_TEXT)
textbutton _("Clean"):
action AndroidIfState(state, ANDROID_NO_KEY, Jump("android_clean"))
hovered tt.Action(CLEAN_TEXT)
add SPACER
add SEPARATOR2
frame:
@@ -515,14 +449,6 @@ label android_installsdk:
jump android
label android_keys:
python:
rapt.keys.generate_keys(MobileInterface("android"), project.current.path)
jump android
label android_configure:
python:
@@ -539,20 +465,74 @@ label android_configure:
label android_build:
$ android_build(bundle=persistent.android_bundle, opendir=True)
$ android_build([ 'release' ], opendir=True)
jump android
label android_build_and_install:
$ android_build(bundle=persistent.android_bundle, install=True, opendir=True)
$ android_build([ 'release', 'install' ])
jump android
label android_build_install_and_launch:
$ android_build(bundle=persistent.android_bundle, install=True, launch=True, opendir=True)
$ android_build([ 'release', 'install' ], launch=True)
jump android
label android_connect:
python hide:
if persistent.connect_address is not None:
address = persistent.connect_address
else:
address = ""
while True:
address = interface.input(
_("Remote ADB Address"),
_("Please enter the IP address and port number to connect to, in the form \"192.168.1.143:5555\". Consult your device's documentation to determine if it supports remote ADB, and if so, the address and port to use."),
default=address,
cancel=Jump("android"),
)
address = address.strip()
try:
host, port = address.split(":")
except:
interface.error(_("Invalid remote ADB address"), _("The address must contain one exactly one ':'."), label=None)
continue
if " " in host:
interface.error(_("Invalid remote ADB address"), _("The host may not contain whitespace."), label=None)
continue
try:
int(port)
except:
interface.error(_("Invalid remote ADB address"), _("The port must be a number."), label=None)
continue
break
persistent.connect_address = address
rapt_interface = MobileInterface("android")
rapt.build.connect(rapt_interface, address)
jump android
label android_disconnect:
python hide:
rapt_interface = MobileInterface("android")
rapt.build.disconnect(rapt_interface)
jump android
@@ -567,169 +547,22 @@ label logcat:
jump android
label android_list_devices:
python hide:
cc = ConsoleCommand()
cc.add(rapt.plat.adb, "devices")
cc.run()
jump android
label android_pair:
python hide:
pairing_code = interface.input(
_("Wi-Fi Pairing Code"),
_("If supported, this can be found in 'Developer options', 'Wireless debugging', 'Pair device with pairing code'."),
sanitize=False,
cancel=Jump("android"),
)
host = interface.input(
_("Pairing Host & Port"),
_("If supported, this can be found in 'Developer options', 'Wireless debugging', 'Pair device with pairing code'."),
sanitize=False,
cancel=Jump("android"),
)
cc = ConsoleCommand()
cc.add(rapt.plat.adb, "pair", host, pairing_code)
cc.run()
jump android
label android_connect:
python hide:
host = interface.input(
_("IP Address & Port"),
_("If supported, this can be found in 'Developer options', 'Wireless debugging'."),
sanitize=False,
cancel=Jump("android"),
)
cc = ConsoleCommand()
cc.add(rapt.plat.adb, "connect", host)
cc.run()
jump android
label android_disconnect:
python hide:
host = interface.input(
_("IP Address & Port"),
_("This can be found in 'List Devices'."),
sanitize=False,
cancel=Jump("android"),
)
cc = ConsoleCommand()
cc.add(rapt.plat.adb, "disconnect", host)
cc.run()
jump android
label android_clean:
python hide:
import shutil
import time
interface = MobileInterface("android")
interface.info(_("Cleaning up Android project."))
# Get the android json file, for the update_always key.
try:
filename = os.path.join(project.current.path, ".android.json")
with open(filename, "rb") as f:
android_json = json.load(f)
except Exception:
android_json = {}
# Clean up the files.
def clean(path):
if os.path.exists(path):
shutil.rmtree(path)
if android_json.get("update_always", True):
try:
with open(rapt.plat.path("project/local.properties"), "r") as f:
local_properties = f.read()
except Exception:
local_properties = None
try:
with open(rapt.plat.path("project/bundle.properties"), "r") as f:
bundle_properties = f.read()
except Exception:
bundle_properties = None
try:
with open(rapt.plat.path("project/gradle.properties"), "r") as f:
gradle_properties = f.read()
except Exception:
gradle_properties = None
clean(rapt.plat.path("project"))
if local_properties or bundle_properties or gradle_properties:
os.mkdir(rapt.plat.path("project"))
if local_properties:
with open(rapt.plat.path("project/local.properties"), "w") as f:
f.write(local_properties)
if bundle_properties:
with open(rapt.plat.path("project/bundle.properties"), "w") as f:
f.write(bundle_properties)
if gradle_properties:
with open(rapt.plat.path("project/gradle.properties"), "w") as f:
f.write(gradle_properties)
clean(rapt.plat.path("bin"))
clean(project.current.temp_filename("android.dist"))
# This can go really fast, so pause so it looks like something is happening.
time.sleep(.5)
jump android
init python:
def android_build_command():
ap = renpy.arguments.ArgumentParser()
ap.add_argument("android_project", help="The path to the project directory.")
ap.add_argument("--bundle", action="store_true", help="Builds an android app bundle.")
ap.add_argument("--install", action="store_true", help="Installs the app on a device.")
ap.add_argument("--launch", action="store_true", help="Launches the app after build and install complete. Implies --install.")
ap.add_argument("ant_commands", help="Commands to pass to ant. (Try 'release' 'install'.)", nargs='+')
ap.add_argument("--launch", action="store_true", help="Launches the app after build and install compete.")
ap.add_argument("--destination", "--dest", default=None, action="store", help="The directory where the packaged files should be placed.")
ap.add_argument("--package", action="append", help="If given, a package to build. Defaults to building the 'android' package.")
args = ap.parse_args()
if args.launch:
args.install = True
p = project.Project(args.android_project)
if args.package:
packages = args.package
else:
packages = None
android_build(p=p, gui=False, bundle=args.bundle, install=args.install, launch=args.launch, destination=args.destination, packages=packages)
android_build(args.ant_commands, p=p, gui=False, launch=args.launch, destination=args.destination)
return False
renpy.arguments.register_command("android_build", android_build_command)
-67
View File
@@ -1,67 +0,0 @@
# This file contains strings used by RAPT, so the Ren'Py translation framework
# can find them. It's automatically generated by rapt/update_translations.py, and
# hence should not be changed by hand.
init python hide:
__("{} is not a directory.")
__("{} does not contain a Ren'Py game.")
__("{} does not contain a Ren'Py game.")
__("Run configure before attempting to build the app.")
__("Updating project.")
__("Creating assets directory.")
__("Packaging internal data.")
__("I'm using Gradle to build the package.")
__("The build seems to have failed.")
__("I'm installing the bundle.")
__("Installing the bundle appears to have failed.")
__("Launching app.")
__("Launching the app appears to have failed.")
__("The build seems to have succeeded.")
__("What is the full name of your application? This name will appear in the list of installed applications.")
__("What is the short name of your application? This name will be used in the launcher, and for application shortcuts.")
__("What is the name of the package?\n\nThis is usually of the form com.domain.program or com.domain.email.program. It may only contain ASCII letters and dots. It must contain at least one dot.")
__("The package name may not be empty.")
__("The package name may not contain spaces.")
__("The package name must contain at least one dot.")
__("The package name may not contain two dots in a row, or begin or end with a dot.")
__("Each part of the package name must start with a letter, and contain only letters, numbers, and underscores.")
__("{} is a Java keyword, and can't be used as part of a package name.")
__("How much RAM (in GB) do you want to allocate to Gradle?\nThis must be a positive integer number.")
__("The RAM size must contain only numbers and be positive.")
__("How would you like your application to be displayed?")
__("In landscape orientation.")
__("In portrait orientation.")
__("In the user's preferred orientation.")
__("Which app store would you like to support in-app purchasing through?")
__("Google Play.")
__("Amazon App Store.")
__("Both, in one app.")
__("Neither.")
__("Do you want to automatically update the Java source code?")
__("Yes. This is the best choice for most projects.")
__("No. This may require manual updates when Ren'Py or the project configuration changes.")
__("Unknown configuration variable: {}")
__("I'm compiling a short test program, to see if you have a working JDK on your system.")
__("I was unable to use javac to compile a test file. If you haven't installed the Java Development Kit yet, please download it from:\n\n{a=https://adoptium.net}https://adoptium.net/{/a}\n\nThe JDK is different from the JRE, so it's possible you have Java without having the JDK. Please install JDK [JDK_REQUIREMENT], and add it to your PATH.\n\nWithout a working JDK, I can't continue.")
__("The version of Java on your computer does not appear to be JDK [JDK_REQUIREMENT], which is required to build Android apps. If you need to install a newer JDK, you can download it from:\n\n{a=https://adoptium.net/}https://adoptium.net/{/a}, and add it to your PATH.\n\nYou can also set the JAVA_HOME environment variable to use a different version of Java.")
__("The JDK is present and working. Good!")
__("The Android SDK has already been unpacked.")
__("Do you accept the Android SDK Terms and Conditions?")
__("I'm downloading the Android SDK. This might take a while.")
__("I'm extracting the Android SDK.")
__("I've finished unpacking the Android SDK.")
__("I'm about to download and install the required Android packages. This might take a while.")
__("I was unable to accept the Android licenses.")
__("I was unable to accept the Android licenses.")
__("I was unable to install the required Android packages.")
__("I've finished installing the required Android packages.")
__("It looks like you're ready to start packaging games.")
__("Please enter your name or the name of your organization.")
__("I found an android.keystore file in the rapt directory. Do you want to use this file?")
__("I will create the key in the android.keystore file.\n\nYou need to back this file up. If you lose it, you will not be able to upgrade your application.\n\nYou also need to keep the key safe. If evil people get this file, they could make fake versions of your application, and potentially steal your users' data.\n\nWill you make a backup of android.keystore, and keep it in a safe place?")
__("\n\nSaying 'No' will prevent key creation.")
__("Could not create android.keystore. Is keytool in your path?")
__("I've finished creating android.keystore. Please back it up, and keep it in a safe place.")
__("I found a bundle.keystore file in the rapt directory. Do you want to use this file?")
__("I've opened the directory containing android.keystore and bundle.keystore. Please back them up, and keep them in a safe place.")
+3 -4
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -28,9 +28,8 @@ init python in archiver:
import sys
import random
import glob
import zlib
from pickle import dumps, HIGHEST_PROTOCOL
from cPickle import dumps, HIGHEST_PROTOCOL
class Archive(object):
@@ -77,7 +76,7 @@ init python in archiver:
indexoff = self.f.tell()
self.f.write(zlib.compress(dumps(self.index, HIGHEST_PROTOCOL)))
self.f.write(dumps(self.index, HIGHEST_PROTOCOL).encode("zlib"))
self.f.seek(0)
self.f.write(b"RPA-3.0 %016x %08x\n" % (indexoff, self.key))
+57 -117
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -19,42 +19,42 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# http://www.csn.ul.ie/~caolan/publink/winresdump/winresdump/doc/pefile.html
# Contains a reasonable description of the format.
from __future__ import print_function
from renpy.compat import bchr, chr, str, PY2
import struct
import sys
import array
import pefile
import pefile # @UnresolvedImport
# This class performs various operations on memory-loaded binary files,
# including modifications.
class BinFile(object):
def set_u32(self, addr, value):
self.a[addr + 0] = (value >> 0) & 0xff
self.a[addr + 1] = (value >> 8) & 0xff
self.a[addr + 2] = (value >> 16) & 0xff
self.a[addr + 3] = (value >> 24) & 0xff
self.a[addr+0] = (value >> 0) & 0xff
self.a[addr+1] = (value >> 8) & 0xff
self.a[addr+2] = (value >> 16) & 0xff
self.a[addr+3] = (value >> 24) & 0xff
def u32(self):
addr = self.addr
rv = self.a[addr]
rv |= self.a[addr + 1] << 8
rv |= self.a[addr + 2] << 16
rv |= self.a[addr + 3] << 24
rv |= self.a[addr+1] << 8
rv |= self.a[addr+2] << 16
rv |= self.a[addr+3] << 24
self.addr += 4
return rv
def u16(self):
addr = self.addr
rv = self.a[addr]
rv |= self.a[addr + 1] << 8
rv |= self.a[addr+1] << 8
self.addr += 2
return rv
@@ -68,7 +68,7 @@ class BinFile(object):
rv = u""
for _i in range(c):
rv += chr(self.u16())
rv += unichr(self.u16())
return rv
@@ -76,20 +76,19 @@ class BinFile(object):
self.addr = addr
def tostring(self):
return self.a.tobytes()
return self.a.tostring()
def substring(self, start, len): # @ReservedAssignment
return self.a[start:start + len].tobytes()
def substring(self, start, len): # @ReservedAssignment
return self.a[start:start+len].tostring()
def __init__(self, data):
self.a = array.array('B')
self.a.frombytes(data)
self.a = array.array(b'B')
self.a.fromstring(data)
##############################################################################
# These functions parse data out of the file. In these functions, offset is
# relative to the start of the file.
# The virtual address of the resource segment.
resource_virtual = 0
@@ -107,7 +106,7 @@ def parse_data(bf, offset):
bf.seek(data_offset - resource_virtual)
for _i in range(data_len):
l.append(bchr(bf.u8()))
l.append(chr(bf.u8()))
return (code_page, b"".join(l))
@@ -117,10 +116,10 @@ def parse_data(bf, offset):
def parse_directory(bf, offset):
bf.seek(offset)
char = bf.u32() # @UnusedVariable
timedate = bf.u32() # @UnusedVariable
major = bf.u16() # @UnusedVariable
minor = bf.u16() # @UnusedVariable
char = bf.u32() # @UnusedVariable
timedate = bf.u32() # @UnusedVariable
major = bf.u16() # @UnusedVariable
minor = bf.u16() # @UnusedVariable
n_named = bf.u16()
n_id = bf.u16()
@@ -211,8 +210,8 @@ class Packer(object):
return rv
def pack_dict(self, d, offset):
name_entries = sorted((a, b) for a, b in d.items() if isinstance(a, str))
id_entries = sorted((a, b) for a, b in d.items() if isinstance(a, int))
name_entries = sorted((a, b) for a, b in d.iteritems() if isinstance(a, unicode))
id_entries = sorted((a, b) for a, b in d.iteritems() if isinstance(a, int))
rv = struct.pack("<IIHHHH", 0, 0, 4, 0, len(name_entries), len(id_entries))
@@ -221,7 +220,7 @@ class Packer(object):
rest = b""
for (name, value) in name_entries + id_entries:
if isinstance(name, str):
if isinstance(name, unicode):
name = 0x80000000 | self.pack_name(name)
if isinstance(value, dict):
@@ -242,7 +241,7 @@ class Packer(object):
def load_icon(fn):
f = BinFile(open(fn, "rb").read())
f = BinFile(file(fn, "rb").read())
f.seek(0)
f.u16()
@@ -269,17 +268,14 @@ def load_icon(fn):
if not f.u32():
f.set_u32(offset + 20, 0)
#rv[3][i + 1] = { 0 : (1252, f.substring(offset, size)) }
#I do not have justification for the 0x0409 entry ID, other than copying what other files do. Same for codepage of 0
rv[3][i + 1] = { 1033 : (0, f.substring(offset, size)) }
rv[3][i + 1] = { 0 : (1252, f.substring(offset, size)) }
group += struct.pack("BBBBHHIH", width, height, colors, reserved,
planes, bpp, size, i + 1)
f.seek(addr)
#rv[14] = { 1 : { 0 : (1252, group) } }
rv[14] = { 1 : { 1033 : (0, group) } }
rv[14] = { 1 : { 0 : (1252, group) } }
return rv
@@ -305,24 +301,9 @@ def change_icons(oldexe, icofn):
physize = rsrc_section.SizeOfRawData
virsize = rsrc_section.Misc_VirtualSize
f = open(oldexe, "rb")
basedata = f.read(base)
f = file(oldexe, "rb")
f.seek(base)
data = f.read(physize)
#Symbol table, I could not understand so well
# Some analysis and the little I could find on how the symbol tables were layed out show that the PE table is in two sections. One is
# a list of NumberOfSymbols*(18 bytes) symbol entries, followed immediately by a string table who's length is specified in the 4 bytes following the symbol structure list
#Again, the information *seems* to indicate that this will always follow the entire block of all sections (so be at the end of the file)
######
# As a related note, apparently this flag has been deprecated, but appears to be set (I think ..) in the renpy.exe that is used to build from..
# no idea of a consequence, just an observation
# https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#characteristics , see "IMAGE_FILE_LINE_NUMS_STRIPPED" :
# COFF line numbers have been removed. This flag is deprecated and should be zero.
# In renpy.exe, this 2-byte flag is @ 0x096 and 0x097 in little-endian
######
f.close()
bf = BinFile(data)
@@ -334,85 +315,44 @@ def change_icons(oldexe, icofn):
rsrc = Packer().pack(resources)
newExactSize = len(rsrc)
alignment = pe.OPTIONAL_HEADER.SectionAlignment
#From note about flags above regarding image flags. These two should be 0
pe.FILE_HEADER.Characteristics = pe.FILE_HEADER.Characteristics & (~pefile.IMAGE_CHARACTERISTICS["IMAGE_FILE_LINE_NUMS_STRIPPED"])
pe.FILE_HEADER.Characteristics = pe.FILE_HEADER.Characteristics & (~pefile.IMAGE_CHARACTERISTICS["IMAGE_FILE_LOCAL_SYMS_STRIPPED"])
# print "Alignment is", alignment
#Per docs, symbol table can just be removed
#https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image see "PointerToSymbolTable" and "NumberOfSymbols "
pe.FILE_HEADER.PointerToSymbolTable = 0
pe.FILE_HEADER.NumberOfSymbols = 0
#####
# Alignment requirements as I understand them
#
# SectionAlignment (misnomer.. 'memory' alignment) = 4096
# FileAlignment = 512
#
# Section, SizeOfRawData = multiple of FileAlignment (On Disk size)
# Scetion, VirtualSize is NOT aligned to any specific number, and rwdata may be > than this due to its alignment requirement (In memory size)
#
# the resource section should be padded out to meet the alignment requirement of SizeOfRawData = multiple of FileAlignment (So, in the file, since on disk)
#
#####
# RVA table virt address and size = that in the section header
#
#####
# SizeOfImage must be aligned to SectionAlignment (memory alignment)
# There is NO padding needed, the image size number simply needs to be aligned to >= the
memoryalignment = pe.OPTIONAL_HEADER.SectionAlignment
filealignment = pe.OPTIONAL_HEADER.FileAlignment
rsrc_section.Misc_VirtualSize = newExactSize
rsrc_section.Misc_PhysicalAddress = newExactSize
rsrc_section.Misc = newExactSize
#Size of resource section (and pointer, but we're not changing anything 'above' resource) is the same as virtual address in the section table
# https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-data-directories-image-only
pe.OPTIONAL_HEADER.DATA_DIRECTORY[2].Size = rsrc_section.Misc_VirtualSize
#Alignment for raw data
if len(rsrc) % filealignment:
pad = filealignment - (len(rsrc) % filealignment)
padding = b"\0" * (pad)
if len(rsrc) % alignment:
pad = alignment - (len(rsrc) % alignment)
padding = b"RENPYVNE" * (pad / 8 + 1)
padding = padding[:pad]
rsrc += padding
rsrc_section.SizeOfRawData = len(rsrc)
newsize = len(rsrc)
#Image size is the memory size of all sections + all headers, padded to memory alignment.
#There's already a header size, and this isn't changing headers, so just add.
#imageSize = pe.OPTIONAL_HEADER.ImageBase + pe.OPTIONAL_HEADER.SizeOfHeaders
imageSize = pe.OPTIONAL_HEADER.SizeOfHeaders
for s in pe.sections:
sectionSize = s.Misc_VirtualSize
#Align every section, so add padding to size
pad = 0
if sectionSize % memoryalignment:
pad = memoryalignment - (sectionSize % memoryalignment)
imageSize += (sectionSize + pad)
pad = 0
if imageSize % memoryalignment:
pad = memoryalignment - (imageSize % memoryalignment)
pe.OPTIONAL_HEADER.SizeOfImage = imageSize + pad
rsrc_section.Misc_VirtualSize += newsize - virsize
rsrc_section.Misc_PhysicalAddress += newsize - virsize
rsrc_section.Misc += newsize - virsize
rsrc_section.SizeOfRawData += newsize - physize
pe.OPTIONAL_HEADER.SizeOfInitializedData += newsize - physize
#The symbol table is simply left off. Its size DOES factor into ImageSize, but we didn't calculate it above, so fine
#Correctly checksum the file. The entire file is involved in the calculation, so a new PE object must be generated for the calculation to work against
newFile = pe.write()[:base] + rsrc
newpe = pefile.PE(data=bytes(newFile))
newpe.OPTIONAL_HEADER.CheckSum = newpe.generate_checksum()
# Resource size.
pe.OPTIONAL_HEADER.DATA_DIRECTORY[2].Size += newsize - virsize
return bytes(newpe.write())
# Compute the total size of the image.
total_size = 0
for i in pe.sections:
sec_size = i.Misc_VirtualSize
sec_size = sec_size - (sec_size % alignment) + alignment
total_size += sec_size
pe.OPTIONAL_HEADER.SizeOfImage = total_size
return pe.write()[:base] + rsrc
if __name__ == "__main__":
f = open(sys.argv[3], "wb")
f = file(sys.argv[3], "wb")
f.write(change_icons(sys.argv[1], sys.argv[2]))
f.close()
+54 -27
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -21,11 +21,6 @@
init python:
try:
import _renpytfd
except Exception:
_renpytfd = None
def directory_is_writable(path):
test = os.path.join(path, "renpy test do not use")
@@ -38,10 +33,10 @@ init python:
return True
except Exception:
except:
return False
def choose_directory(default_path):
def choose_directory(path):
"""
Pops up a directory chooser.
@@ -54,31 +49,63 @@ init python:
rather than user choice.
"""
if _renpytfd:
path = _renpytfd.selectFolderDialog(__("Select Projects Directory"), default_path)
if path:
default_path = path
else:
path = None
try:
default_path = os.path.dirname(os.path.abspath(config.renpy_base))
except:
default_path = os.path.abspath(config.renpy_base)
if default_path is None:
try:
default_path = os.path.dirname(os.path.abspath(config.renpy_base))
except Exception:
default_path = os.path.abspath(config.renpy_base)
if EasyDialogs:
# Path being None or "" means nothing was selected.
if not path:
choice = EasyDialogs.AskFolder(defaultLocation=default_path, wanted=unicode)
if default_path is None or not os.path.isdir(default_path) or not directory_is_writable(default_path):
interface.error(_("No directory was selected, but one is required."))
if choice is not None:
path = choice
else:
path = None
return default_path, True
else:
try:
cmd = [ "/usr/bin/python", os.path.join(config.gamedir, "tkaskdir.py"), renpy.fsencode(default_path) ]
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
choice = p.stdout.read()
code = p.wait()
except:
import traceback
traceback.print_exc()
code = 0
choice = ""
path = None
interface.error(_("Ren'Py was unable to run python with tkinter to choose the directory. Please install the python-tk or tkinter package."), label=None)
if code:
interface.error(_("Ren'Py was unable to run python with tkinter to choose the directory. Please install the python-tk or tkinter package."), label=None)
elif choice:
path = choice.decode("utf-8")
is_default = False
if path is None:
path = default_path
is_default = True
# Apply more thorough checks to an explicit path.
path = renpy.fsdecode(path)
if not os.path.isdir(path):
interface.error(_("The selected directory does not exist."))
elif not directory_is_writable(path):
interface.error(_("The selected directory is not writable."))
if (not os.path.isdir(path)) or (not directory_is_writable(path)):
interface.error(_("The selected projects directory is not writable."))
path = default_path
is_default = True
return path, False
if is_default and (not directory_is_writable(path)):
path = os.path.expanduser("~")
return path, is_default
+9 -9
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -97,7 +97,7 @@ init python:
return
renpy.style.restore(style_backup)
exec(theme_data.THEME[theme][scheme], globals(), globals())
exec theme_data.THEME[theme][scheme] in globals()
# Rebuild the style cache.
renpy.style.rebuild(False)
@@ -167,8 +167,8 @@ init python:
def list_logical_lines(filename):
"""
This reads in filename, and turns it into a list of logical
lines.
This reads in filename, and turns it into a list of logical
lines.
"""
f = codecs.open(filename, "rb", "utf-8")
@@ -187,7 +187,7 @@ init python:
# The line that we're building up.
line = ""
# The number of open parenthesis there are right now.
# The number of open parenthesis there are right now.
parendepth = 0
# Looping over the characters in a single logical line.
@@ -293,7 +293,7 @@ init python:
if changed:
try:
os.unlink(filename + ".bak")
except Exception:
except:
pass
os.rename(filename, filename + ".bak")
@@ -322,7 +322,7 @@ init python:
if changed:
try:
os.unlink(filename + ".bak")
except Exception:
except:
pass
os.rename(filename, filename + ".bak")
@@ -332,7 +332,7 @@ init python:
try:
# just in case
os.unlink(filename + ".new")
except Exception:
except:
pass
pass
@@ -504,7 +504,7 @@ label choose_theme_callable:
call screen choose_theme
python hide:
with interface.error_handling(_("changing the theme")):
with interface.error_handling("changing the theme"):
switch_theme()
return
+16 -23
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -33,15 +33,15 @@ init python:
if renpy.macintosh:
fn = "console.command"
nl = b"\n"
prefix = "#!/bin/sh"
nl = "\n"
prefix = "#!/bin/bash"
elif renpy.windows:
fn = "console.bat"
nl = b"\r\n"
nl = "\r\n"
prefix = "@echo off"
else:
fn = "console.sh"
nl = b"\n"
nl = "\n"
prefix = "#!/bin/bash"
@@ -49,23 +49,16 @@ init python:
self.f = open(self.fn, "wb")
self.nl = nl
self.f.write(renpy.fsencode(prefix, force=True) + nl)
self.f.write(renpy.fsencode(prefix) + nl)
def add(self, *args):
"""
Adds a command to be run.
"""
args = [ b'"' + renpy.fsencode(i, force=True) + b'"' for i in args]
self.f.write(b" ".join(args) + self.nl)
def write(self, *args):
"""
Adds a command to be run.
"""
args = [ renpy.fsencode(i, force=True) for i in args]
self.f.write(b" ".join(args) + self.nl)
args = [ '"{}"'.format(renpy.fsencode(i)) for i in args]
self.f.write(" ".join(args) + self.nl)
def run(self):
"""
@@ -73,19 +66,19 @@ init python:
"""
if renpy.windows:
self.write("pause")
self.add("pause")
elif renpy.linux:
self.add("echo", "Press enter to close this window...")
self.write("read")
self.add("read")
self.f.close()
os.chmod(self.fn, 0o755)
if renpy.linux:
command = renpy.fsencode('"{}"'.format(self.fn.replace("\"", "\\\"")))
subprocess.Popen([ "x-terminal-emulator", "-e", command ])
if renpy.windows:
subprocess.Popen([ renpy.fsencode(self.fn) ], shell=True)
elif renpy.macintosh:
subprocess.Popen([ "open", "-a", "Terminal", renpy.fsencode(self.fn) ])
else:
command = renpy.fsencode(self.fn)
os.startfile(command)
subprocess.Popen([ "x-terminal-emulator", "-e", renpy.fsencode(self.fn) ])
interface.interaction(_("INFORMATION"), _("The command is being run in a new operating system console window."), pause=2.5)
File diff suppressed because it is too large Load Diff
+2 -30
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -192,7 +192,6 @@ screen build_distributions:
textbutton _("Edit options.rpy") action editor.Edit("game/options.rpy", check=True)
textbutton _("Add from clauses to calls, once") action Jump("add_from")
textbutton _("Update old-game") action Jump("start_update_old_game")
textbutton _("Refresh") action Jump("build_distributions")
add HALF_SPACER
@@ -222,13 +221,7 @@ screen build_distributions:
for pkg in packages:
if not pkg["hidden"]:
$ description = pkg["description"]
button:
action PackageToggle(pkg["name"]) style "l_checkbox"
hbox:
spacing 3
text "[description!q]"
if pkg["dlc"]:
text _("(DLC)")
textbutton "[description!q]" action PackageToggle(pkg["name"]) style "l_checkbox"
add SPACER
add HALF_SPACER
@@ -263,27 +256,8 @@ label add_from:
call add_from_common
jump build_distributions
label start_update_old_game:
call update_old_game
jump build_distributions
label add_update_pem:
python hide:
interface.info("You're trying to build an update, but an update.pem file doesn't exist.\n\nThis file is used to sign updates, and will be automatically created in your projects's base directory.\n\nYou'll need to back up update.pem and keep it safe.", cancel=Jump("build_distributions"))
import ecdsa
key = ecdsa.SigningKey.generate(curve=ecdsa.NIST256p).to_pem()
with open(os.path.join(project.current.path, "update.pem"), "wb") as f:
f.write(key)
return
label start_distribute:
if project.current.dump["build"]["include_update"] and not os.path.exists(os.path.join(project.current.path, "update.pem")):
call add_update_pem
if project.current.data["add_from"]:
call add_from_common
@@ -302,8 +276,6 @@ label build_distributions:
call build_update_dump
label post_build:
if not project.current.dump["build"]["directory_name"]:
jump build_missing
+3 -3
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -36,13 +36,13 @@ init python:
if os.path.exists(fn):
os.unlink(fn)
with open(fn, "w") as f:
with open(fn, "wb") as f:
f.write("Testing to see if we're in a DMG.\r\n")
os.unlink(fn)
return
except Exception:
except:
# If we're here, we didn't make it through the checks safely. So
# put up a warning message.
+12 -11
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -21,9 +21,7 @@
init python:
import requests
import urllib.request
import urllib2
import os
import threading
import time
@@ -52,14 +50,13 @@ init python:
# This is set to true by cancel() to indicate the download should be cancelled.
self.cancelled = False
# Set on success or failure.
# Set on succes or failure.
self.success = False
self.failure = None
try:
# Open the URL.
self.urlfile = requests.get(url, stream=True, proxies=renpy.proxies, timeout=15)
self.urlfile = urllib2.urlopen(url)
t = threading.Thread(target=self.thread)
t.daemon = True
@@ -78,7 +75,12 @@ init python:
else:
length = 0
for data in self.urlfile.iter_content(1024 * 1024):
while not self.cancelled:
data = self.urlfile.read(65536)
if not data:
break
count += len(data)
self.tmpfile.write(data)
@@ -86,9 +88,6 @@ init python:
if length > 0:
self.progress = 1.0 * count / length
if self.cancelled:
break
self.tmpfile.close()
if self.cancelled:
@@ -106,6 +105,7 @@ init python:
except Exception as e:
self.failure = str(e)
def safe_unlink(self, fn):
if os.path.exists(fn):
os.unlink(fn)
@@ -147,3 +147,4 @@ init python:
def periodic(self, st):
self.adjustment.change(self.downloader.progress)
return .25
+54 -237
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -67,8 +67,8 @@ init 1 python in editor:
ei = EditorInfo(filename)
if ei.name in editors:
if editors[ei.name].mtime >= ei.mtime:
return
if editors[ei.name].mtime >= ei.mtime:
return
editors[ei.name] = ei
@@ -112,7 +112,7 @@ init 1 python in editor:
editor is installed or not.
"""
def __init__(self, priority, name, description=None, dlc=None, dldescription=None, error_message=None, deprecated=False):
def __init__(self, priority, name, description=None, dlc=None, dldescription=None, error_message=None):
# The priority of the editor. Lower priorities will come later
# in the list.
self.priority = priority
@@ -135,118 +135,67 @@ init 1 python in editor:
# An error message to display if the editor failed to start.
self.error_message = error_message
# If the editor is considered deprecated for use with Ren'Py.
self.deprecated = deprecated
def vscode_path():
"""
Gets the path to VS Code for this platform.
"""
if renpy.windows:
return os.path.join(config.renpy_base, "vscode/VSCode-win32-x64")
elif renpy.macintosh:
return os.path.join(config.renpy_base, "vscode/Visual Studio Code.app")
else:
if renpy.arch == "aarch64":
arch = "arm64"
elif renpy.arch == "armv7l":
arch = "arm"
else:
arch = "x64"
return os.path.join(config.renpy_base, "vscode/VSCode-linux-" + arch)
def fancy_scan_editors():
"""
Creates the list of FancyEditorInfo objects.
"""
import platform
global fancy_editors
scan_all()
fei = fancy_editors = [ ]
# Visual Studio Code
AD1 = _("A modern editor with many extensions including advanced Ren'Py integration.")
AD2 = _("A modern editor with many extensions including advanced Ren'Py integration.\n{a=jump:reinstall_vscode}Upgrade Visual Studio Code to the latest version.{/a}")
installed = os.path.exists(vscode_path())
e = FancyEditorInfo(
0,
_("Visual Studio Code"),
AD2 if installed else AD2,
"extension:vscode",
_("Up to 110 MB download required."),
None)
e.installed = e.installed and installed
fei.append(e)
# Atom.
AD = _("Atom is deprecated and its bugs are known for corrupting games, using another editor is recommended.")
# Editra.
ED = _("{b}Recommended.{/b} A beta editor with an easy to use interface and features that aid in development, such as spell-checking. Editra currently lacks the IME support required for Chinese, Japanese, and Korean text input.")
EDL = _("{b}Recommended.{/b} A beta editor with an easy to use interface and features that aid in development, such as spell-checking. Editra currently lacks the IME support required for Chinese, Japanese, and Korean text input. On Linux, Editra requires wxPython.")
if renpy.windows:
dlc = "atom-windows"
installed = os.path.exists(os.path.join(config.renpy_base, "atom/atom-windows"))
dlc = "editra-windows"
installed = os.path.exists(os.path.join(config.basedir, "editra/Editra-win32"))
description = ED
error_message = None
elif renpy.macintosh:
dlc = "atom-mac"
installed = os.path.exists(os.path.join(config.renpy_base, "atom/Atom.app"))
dlc = "editra-mac"
installed = os.path.exists(os.path.join(config.basedir, "editra/Editra-mac.app"))
description = ED
error_message = None
else:
dlc = "atom-linux"
installed = os.path.exists(os.path.join(config.renpy_base, "atom/atom-linux-" + platform.machine()))
dlc = "editra-linux"
installed = os.path.exists(os.path.join(config.basedir, "editra/Editra"))
description = EDL
error_message = _("This may have occured because wxPython is not installed on this system.")
if not (renpy.arch in [ "aarch64", "armv7l" ]):
e = FancyEditorInfo(
1,
_("Atom"),
AD,
"extension:atom",
_("Up to 150 MB download required."),
None,
deprecated=True,
)
e.installed = e.installed and (installed or 'RENPY_ATOM' in os.environ)
fei.append(e)
# jEdit - Only present if it exists on system.
e = FancyEditorInfo(
2,
_("jEdit"),
_("JEdit is deprecated, using another editor is recommended."),
"jedit",
_("1.8 MB download required."),
_("This may have occurred because Java is not installed on this system."),
deprecated=True,
)
1,
"Editra",
description,
dlc,
_("Up to 22 MB download required."),
error_message)
e.installed = e.installed and os.path.exists(os.path.join(config.renpy_base, "jedit"))
e.installed = e.installed or installed
fei.append(e)
# jEdit
fei.append(FancyEditorInfo(
3,
_("Visual Studio Code (System)"),
_("Uses a copy of Visual Studio Code that you have installed outside of Ren'Py. It's recommended you install the language-renpy extension to add support for Ren'Py files."),
2,
"jEdit",
_("A mature editor that requires Java."),
"jedit",
_("1.8 MB download required."),
_("This may have occured because Java is not installed on this system."),
))
fei.append(FancyEditorInfo(
3,
_("System Editor"),
"System Editor",
_("Invokes the editor your operating system has associated with .rpy files."),
None))
for k in editors:
if k in [ "Visual Studio Code", "Visual Studio Code (System)", "Atom", "jEdit", "System Editor", "None" ]:
if k in [ "Editra", "jEdit", "System Editor", "None" ]:
continue
fei.append(FancyEditorInfo(
@@ -257,7 +206,7 @@ init 1 python in editor:
fei.append(FancyEditorInfo(
5,
_("None"),
"None",
_("Prevents Ren'Py from opening a text editor."),
None))
@@ -266,8 +215,7 @@ init 1 python in editor:
# If we're in a linux distro or something, assume all editors work.
if not updater.can_update():
for i in fei:
if i.dlc and not i.dlc.startswith("extension:") and not i.deprecated:
i.installed = True
i.installed = True
def fancy_activate_editor(default=False):
"""
@@ -290,13 +238,13 @@ init 1 python in editor:
if i.name == persistent.editor:
if i.installed and i.name in editors:
ei = editors[i.name]
os.environ["RENPY_EDIT_PY"] = renpy.fsencode(os.path.abspath(ei.filename))
os.environ[b"RENPY_EDIT_PY"] = renpy.fsencode(os.path.abspath(ei.filename))
error_message = i.error_message
break
else:
persistent.editor = None
os.environ.pop("RENPY_EDIT_PY", None)
os.environ.pop(b"RENPY_EDIT_PY", None)
renpy.editor.init()
@@ -314,12 +262,9 @@ init 1 python in editor:
if not fe.installed:
if fe.dlc.startswith("extension:"):
import installer
manifest = fe.dlc.partition(":")[2]
renpy.invoke_in_new_context(installer.manifest, "https://www.renpy.org/extensions/{}/{}.py".format(manifest, manifest), renpy=True)
else:
store.add_dlc(fe.dlc)
# We don't check the status of this because fancy_activate_editor
# will fail if the editor is not installed.
store.add_dlc(fe.dlc)
persistent.editor = fe.name
fancy_activate_editor()
@@ -362,7 +307,7 @@ init 1 python in editor:
class Edit(Action):
alt = _("Edit [text].")
alt = "Edit [text]."
def __init__(self, filename, line=None, check=False):
"""
@@ -389,7 +334,7 @@ init 1 python in editor:
return True
fn = project.current.unelide_filename(self.filename)
return os.path.exists(renpy.fsencode(fn))
return os.path.exists(fn)
def __call__(self):
@@ -409,9 +354,9 @@ init 1 python in editor:
e.open(fn, line=self.line)
e.end()
except Exception as e:
except Exception, e:
exception = traceback.format_exception_only(type(e), e)[-1][:-1]
renpy.invoke_in_new_context(interface.error, _("An exception occurred while launching the text editor:\n[exception!q]"), error_message, exception=exception)
renpy.invoke_in_new_context(interface.error, _("An exception occured while launching the text editor:\n[exception!q]"), error_message, exception=exception)
class EditAbsolute(Action):
def __init__(self, filename, line=None, check=False):
@@ -437,7 +382,7 @@ init 1 python in editor:
if not self.check:
return True
return os.path.exists(renpy.fsencode(self.filename))
return os.path.exists(self.filename)
def __call__(self):
@@ -455,9 +400,9 @@ init 1 python in editor:
e.open(self.filename, line=self.line)
e.end()
except Exception as e:
except Exception, e:
exception = traceback.format_exception_only(type(e), e)[-1][:-1]
renpy.invoke_in_new_context(interface.error, _("An exception occurred while launching the text editor:\n[exception!q]"), error_message, exception=exception)
renpy.invoke_in_new_context(interface.error, _("An exception occured while launching the text editor:\n[exception!q]"), error_message, exception=exception)
class EditAll(Action):
@@ -465,6 +410,8 @@ init 1 python in editor:
Opens all scripts that are part of the current project in a web browser.
"""
alt = "Edit [text]."
def __init__(self):
return
@@ -493,66 +440,9 @@ init 1 python in editor:
e.end()
except Exception as e:
except Exception, e:
exception = traceback.format_exception_only(type(e), e)[-1][:-1]
renpy.invoke_in_new_context(interface.error, _("An exception occurred while launching the text editor:\n[exception!q]"), error_message, exception=exception)
class EditProject(Action):
"""
Opens the project's base directory in an editor.
"""
def __call__(self):
if not check_editor():
return
try:
e = renpy.editor.editor
e.begin()
e.open_project(project.current.path)
e.end()
except Exception as e:
exception = traceback.format_exception_only(type(e), e)[-1][:-1]
renpy.invoke_in_new_context(interface.error, _("An exception occurred while launching the text editor:\n[exception!q]"), error_message, exception=exception)
def CanEditProject():
"""
Returns True if EditProject can be used.
"""
try:
e = renpy.editor.editor
return e.has_projects
except Exception:
return False
def check_old_vscode_extension():
"""
Check to see if the old version of the Ren'Py vscode extension is installed.
"""
extensions = os.path.join(vscode_path(), "data", "extensions")
if not os.path.isdir(extensions):
return False
for i in util.listdir(extensions):
if i.lower().startswith("renpy.language-renpy-"):
return False
for i in util.listdir(extensions):
if i.lower().startswith("luquedaniel.languague-renpy-"):
return True
return False
renpy.invoke_in_new_context(interface.error, _("An exception occured while launching the text editor:\n[exception!q]"), error_message, exception=exception)
screen editor:
@@ -583,9 +473,6 @@ screen editor:
for fe in editor.fancy_editors:
if fe.deprecated and not fe.installed:
continue
add SPACER
textbutton fe.name action editor.SelectEditor(fe.name)
@@ -597,10 +484,7 @@ screen editor:
has vbox
if fe.description:
text fe.description:
style "l_small_text"
if fe.deprecated:
color ERROR_COLOR
text fe.description style "l_small_text"
if not fe.installed:
add HALF_SPACER
@@ -609,73 +493,6 @@ screen editor:
textbutton _("Cancel") action Return(False) style "l_left_button"
label reinstall_vscode:
python hide:
manifest = "vscode"
renpy.invoke_in_new_context(installer.manifest, "https://www.renpy.org/extensions/{}/{}.py".format(manifest, manifest), renpy=True)
jump editor_preference
label upgrade_vscode_extension:
python hide:
manifest = "vscode"
renpy.invoke_in_new_context(installer.manifest, "https://www.renpy.org/extensions/vscode/upgrade_extension.py", renpy=True)
jump post_extension_check
label editor_preference:
call screen editor
jump preferences
default persistent.ignore_obsolete_editor = set()
# This label is called when the launcher starts, to check if the editor
# is obsolete, and let them change it.
label editor_check:
if persistent.editor in persistent.ignore_obsolete_editor:
jump post_editor_check
if persistent.editor == "Atom":
$ result = interface.choice(
_("The Atom text editor is no longer supported by its developers. We suggest switching to Visual Studio Code or another editor."), [
( "select", _("Select editor now.")),
( "ignore", _("Ignore until next launch.")),
( "block", _("Do not ask again.")),
], "select")
else:
jump post_atom_check
if result == "select":
$ renpy.pop_call()
jump editor_preference
elif result == "block":
$ persistent.ignore_obsolete_editor.add(persistent.editor)
label post_atom_check:
if "luquedaniel.languague-renpy" in persistent.ignore_obsolete_editor:
jump post_extension_check
if persistent.editor != "Visual Studio Code":
jump post_extension_check
if editor.check_old_vscode_extension():
$ result = interface.choice(
_("You are using an old version of the Ren'Py Language support for Visual Studio Code. Would you like to upgrade?"), [
( "upgrade", _("Upgrade.")),
( "ignore", _("Ignore until next launch.")),
( "block", _("Do not ask again.")),
], "select")
if result == "upgrade":
jump upgrade_vscode_extension
elif result == "block":
$ persistent.ignore_obsolete_editor.add("luquedaniel.languague-renpy")
label post_extension_check:
jump post_editor_check
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
</dict>
</plist>
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,91 @@
네이버 나눔글꼴의 지적 재산권은 NHN에 있습니다.
네이버 나눔글꼴은 개인 및 기업 사용자를 포함한 모든 사용자에게 무료로 제공되며
자유롭게 수정하고 재배포하실 수 있습니다.
단, 글꼴 자체를 유료로 판매하는 것은 금지하며, 네이버 나눔글꼴 라이선스 전문을 포함하기만 한다면
자유롭게 다른 소프트웨어에 번들하여 유료로 판매하거나 또는 재 배포하실 수도 있습니다.
네이버 나눔글꼴 라이선스 전문을 포함하기 어려울 경우,
나눔글꼴의 출처 표기를 부탁 드립니다. 예) 이 페이지에는 네이버에서 제공한 나눔글꼴이 적용되어 있습니다
네이버 나눔글꼴을 사용한 인쇄물, 광고물(온라인 포함)의 이미지는 나눔글꼴 프로모션을 위해 활용될 수 있습니다.
이를 원치 않는 사용자는 언제든지 당사에 요청하실 수 있습니다.
정확한 사용 조건은 아래 네이버 나눔글꼴 라이선스 전문을 참고하시기 바랍니다.
Copyright (c) 2010, NHN Corporation (http://www.nhncorp.com),
with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic, NanumMyeongjo,
Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen, Naver NanumPen
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
SIL OPEN FONT LICENSE
Version 1.1 - 26 February 2007
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects,
to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework
in which fonts may be shared and improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold
by themselves.
The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided
that any reserved names are not used by derivative works.
The fonts and derivatives, however, cannot be released under any other type of license.
The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such.
This may include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting, or substituting ? in part or in whole ?
any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge,
embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,in Original or Modified Versions,
may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software,
provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files,
human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields
can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written
permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented
to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise
any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit
written permission.
5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed
under any other license. The requirement for fonts to remain under this license does not apply to any document created using
the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are not met.
DISCLAIMER
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 THE COPYRIGHT HOLDER 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.
+58 -48
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -23,7 +23,40 @@ define PROJECT_ADJUSTMENT = ui.adjustment()
init python:
import datetime
import os
import subprocess
class OpenDirectory(Action):
"""
Opens `directory` in a file browser. `directory` is relative to
the project root.
"""
alt = _("Open [text] directory.")
def __init__(self, directory, absolute=False):
if absolute:
self.directory = directory
else:
self.directory = os.path.join(project.current.path, directory)
def get_sensitive(self):
return os.path.exists(self.directory)
def __call__(self):
try:
directory = renpy.fsencode(self.directory)
if renpy.windows:
os.startfile(directory)
elif renpy.macintosh:
subprocess.Popen([ "open", directory ])
else:
subprocess.Popen([ "xdg-open", directory ])
except:
pass
# Used for testing.
def Relaunch():
@@ -46,7 +79,7 @@ screen front_page:
right_margin 2
top_padding 20
bottom_padding 13
bottom_padding 26
side "t c b":
@@ -157,11 +190,13 @@ screen front_page_project:
label _("Open Directory") style "l_label_small"
frame style "l_indent":
has grid 2 max(5, (len(p.get_renpy_launcher()["open_directory"]) + 1) // 2):
transpose True xfill True
has vbox
for button_name, path in p.get_renpy_launcher()["open_directory"].items():
textbutton button_name action OpenDirectory(os.path.join(p.path, path), absolute=True)
textbutton _("game") action OpenDirectory("game")
textbutton _("base") action OpenDirectory(".")
textbutton _("images") action OpenDirectory("game/images")
textbutton _("gui") action OpenDirectory("game/gui")
# textbutton _("save") action None style "l_list"
vbox:
if persistent.show_edit_funcs:
@@ -171,17 +206,23 @@ screen front_page_project:
frame style "l_indent":
has vbox
for button_name, path in p.get_renpy_launcher()["edit_file"].items():
textbutton button_name action editor.Edit(path, check=True)
textbutton "script.rpy" action editor.Edit("game/script.rpy", check=True)
textbutton "options.rpy" action editor.Edit("game/options.rpy", check=True)
textbutton "gui.rpy" action editor.Edit("game/gui.rpy", check=True)
textbutton "screens.rpy" action editor.Edit("game/screens.rpy", check=True)
if editor.CanEditProject():
textbutton _("Open project") action editor.EditProject()
else:
textbutton _("All script files") action editor.EditAll()
textbutton _("All script files") action editor.EditAll()
add SPACER
add SEPARATOR
add SPACER
label _("Actions") style "l_label_small"
frame style "l_indent":
has vbox
textbutton _("Navigate Script") text_size 30 action Jump("navigation")
add SPACER
grid 2 1:
xfill True
@@ -190,8 +231,7 @@ screen front_page_project:
frame style "l_indent":
has vbox
textbutton _("Navigate Script") action Jump("navigation")
textbutton _("Check Script (Lint)") action Call("lint")
textbutton _("Check Script (Lint)") action Jump("lint")
if project.current.exists("game/gui.rpy"):
textbutton _("Change/Update GUI") action Jump("change_gui")
@@ -212,7 +252,6 @@ screen front_page_project:
textbutton _("Android") action Jump("android")
textbutton _("iOS") action Jump("ios")
textbutton _("Web") action Jump("web")
textbutton _("Generate Translations") action Jump("translate")
textbutton _("Extract Dialogue") action Jump("extract_dialogue")
@@ -223,32 +262,7 @@ label start:
show screen bottom_info
$ dmgcheck()
jump expression renpy.session.pop("launcher_start_label", "before_front_page")
default persistent.has_chosen_language = False
default persistent.has_update = False
label before_front_page:
if (not persistent.has_chosen_language) or ("RENPY_CHOOSE_LANGUAGE" in os.environ):
if (_preferences.language is None) or ("RENPY_CHOOSE_LANGUAGE" in os.environ):
hide screen bottom_info
call choose_language
show screen bottom_info
$ persistent.has_chosen_language = True
call editor_check
label post_editor_check:
label front_page:
if persistent.daily_update_check and ((not persistent.last_update_check) or (datetime.date.today() > persistent.last_update_check)):
python hide:
persistent.last_update_check = datetime.date.today()
renpy.invoke_in_thread(fetch_update_channels)
call screen front_page
jump front_page
@@ -259,18 +273,14 @@ label lint:
interface.processing(_("Checking script for potential problems..."))
lint_fn = project.current.temp_filename("lint.txt")
persistent.lint_options.discard("--orphan-tl") # compat
persistent.lint_options.discard("--builtins-parameters") # compat
persistent.lint_options.discard("--words-char-count") # compat
project.current.launch([ 'lint', lint_fn, ] + list(persistent.lint_options), wait=True)
project.current.launch([ 'lint', lint_fn ], wait=True)
e = renpy.editor.editor
e.begin(True)
e.open(lint_fn)
e.end()
return
jump front_page
label rmpersistent:
+56 -112
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -22,19 +22,6 @@
define gui.project_system_font = None
define gui.asian = False
# This is set in new_project before new_template_project is called,
# and provides the full path to the template project.
default gui_template_path = None
define LIBS_TXT = _("""\
The game/libs/ directory is meant for third-party libraries that ask to be
put in game/libs.
When this file (game/libs/libs.txt) is present, Ren'Py will load all files
in libs before any other file in game/, and will not consider directory names
when loading these files.
""")
init -1 python:
import gui7
@@ -44,7 +31,7 @@ init -1 python:
from store import config
def translate_font(language, font):
def translate_font(language, font, path=None):
"""
Selects the font file that is used when translating `language`.
@@ -52,17 +39,26 @@ init -1 python:
Is the name of the font file used for both the launcher and
the new GUI template. This should be a string giving the name
of the font file.
`path`
The path to the font file, relative to the launcher's game
directory. If not given, defaults to fonts.
"""
if path is None:
path = "fonts"
fullfont = path + "/" + font
def callback():
gui.REGULAR_FONT = font
gui.LIGHT_FONT = font
gui.REGULAR_FONT = fullfont
gui.LIGHT_FONT = fullfont
gui.REGULAR_BOLD = True
style._default.font = font
style.default.font = font
style._default.font = fullfont
style.default.font = fullfont
gui.system_font = font
gui.system_font = fullfont
gui.project_system_font = font
config.language_callbacks[language].append(callback)
@@ -70,7 +66,7 @@ init -1 python:
def s(s):
return '"' + s + '"'
gui7.translate_copy(language, "sdk-fonts/" + font, font)
gui7.translate_copy(language, fullfont, font)
gui7.translate_define(language, "gui.text_font", s(font))
gui7.translate_define(language, "gui.name_text_font", s(font))
gui7.translate_define(language, "gui.interface_text_font", s(font))
@@ -294,7 +290,6 @@ screen choose_gui_color():
if gui_color:
textbutton _("Continue") action Return(True) style "l_right_button"
key "input_enter" action Return(True)
label change_gui:
@@ -313,8 +308,7 @@ label change_gui:
project_name = project.current.name
mode = interface.choice(
__("{b}Warning{/b}\nContinuing will overwrite customized bar, button, save slot, scrollbar, and slider images.\n\nWhat would you like to do?") +
__("{size=-4}\n\nThis will not overwrite gui/main_menu.png, gui/game_menu.png, and gui/window_icon.png, but will create files that do not exist.{/size}"),
_("{b}Warning{/b}\nContinuing will overwrite customized bar, button, save slot, scrollbar, and slider images.\n\nWhat would you like to do?"),
[
("change", _("Choose new colors, then regenerate image files.")),
("regenerate", _("Regenerate the image files using the colors in gui.rpy.")),
@@ -332,6 +326,25 @@ label new_gui_project:
python:
gui_new = True
project_name = interface.input(
_("PROJECT NAME"),
_("Please enter the name of your project:"),
filename=True,
cancel=Jump("front_page"))
project_name = project_name.strip()
if not project_name:
interface.error(_("The project name may not be empty."))
project_dir = os.path.join(persistent.projects_directory, project_name)
if project.manager.get(project_name) is not None:
interface.error(_("[project_name!q] already exists. Please choose a different project name."), project_name=project_name)
if os.path.exists(project_dir):
interface.error(_("[project_dir!q] already exists. Please choose a different project name."), project_dir=project_dir)
gui_replace_images = True
gui_replace_code = True
gui_update_code = True
@@ -341,54 +354,34 @@ label gui_project_size:
python:
default_size = (1920, 1080)
gui_size = interface.choice(
_("What resolution should the project use? Although Ren'Py can scale the window up and down, this is the initial size of the window, the size at which assets should be drawn, and the size at which the assets will be at their sharpest.\n\nThe default of [default_size[0]]x[default_size[1]] is a reasonable compromise."),
_("What resolution should the project use? Although Ren'Py can scale the window up and down, this is the initial size of the window, the size at which assets should be drawn, and the size at which the assets will be at their sharpest.\n\nThe default of 1280x720 is a reasonable compromise."),
[
((1066, 600), "1066x600"),
((1280, 720), "1280x720"),
((1920, 1080), "1920x1080"),
((2560, 1440), "2560x1440"),
((3840, 2160), "3840x2160"),
("custom", _("Custom. The GUI is optimized for a 16:9 aspect ratio.")),
],
default_size,
(1280, 720),
cancel=Jump("front_page"),
)
if gui_size == "custom":
gui_width = ""
while True:
gui_width = interface.input(
_("WIDTH"),
_("Please enter the width of your game, in pixels."),
cancel=Jump("front_page"),
allow=interface.DIGITS_LETTERS,
)
try:
gui_width = int(gui_width)
except Exception:
interface.error(_("The width must be a number."), label=None)
continue
break
gui_width = interface.input(_("WIDTH"), _("Please enter the width of your game, in pixels."), cancel=Jump("front_page"))
gui_height = ""
while True:
gui_height = interface.input(
_("HEIGHT"),
_("Please enter the height of your game, in pixels."),
cancel=Jump("front_page"),
allow=interface.DIGITS_LETTERS,
)
try:
gui_width = int(gui_width)
except:
interface.error(_("The width must be a number."))
try:
gui_height = int(gui_height)
except Exception:
interface.error(_("The height must be a number."), label=None)
continue
break
gui_height = interface.input(_("HEIGHT"), _("Please enter the height of your game, in pixels."), cancel=Jump("front_page"))
try:
gui_height = int(gui_height)
except:
interface.error(_("The height must be a number."))
gui_size = (gui_width, gui_height)
@@ -434,11 +427,11 @@ label gui_project_common:
else:
interface.processing(_("Updating the project..."))
with interface.error_handling(_("creating a new project")):
with interface.error_handling("creating a new project"):
gui7.generate_gui(p)
# Activate the project.
with interface.error_handling(_("activating the new project")):
with interface.error_handling("activating the new project"):
project.manager.scan()
project.Select(project.manager.get(project_name))()
@@ -449,7 +442,7 @@ label gui_project_common:
python hide:
if gui.project_system_font:
with open(os.path.join(project.current.gamedir, "tl/None/common.rpym"), "ab") as f:
f.write("define gui.system_font = {!r}\r\n".format(gui.project_system_font).encode("utf-8"))
f.write("define gui.system_font = {!r}\r\n".format(gui.project_system_font))
label gui_generate_images:
@@ -457,56 +450,7 @@ label gui_generate_images:
python:
interface.processing(_("Updating the project..."))
project.current.launch([ 'gui_images' ], env={ "RENPY_VARIANT" : "small phone" }, wait=True)
project.current.launch([ 'gui_images' ], wait=True)
jump front_page
label new_template_project:
# Unused in new_template_project.
$ gui_size = (1920, 1080)
$ gui_color = (DARK_COLORS[0], "#000000", False)
python hide:
width, height = gui_size
accent, boring, light = gui_color
prefix = os.path.join(project_dir, "game")
template = os.path.join(gui_template_path, "game")
# Most of this isn't actually used.
p = gui7.GuiParameters(
prefix,
template,
width,
height,
accent,
boring,
light,
_preferences.language,
False,
True,
True,
project_name,
)
interface.processing(_("Creating the new project..."))
with interface.error_handling(_("creating a new project")):
gui7.generate_minimal(p)
# Activate the project.
with interface.error_handling(_("activating the new project")):
project.manager.scan()
project.Select(project.manager.get(project_name))()
call update_renpy_strings_common
python hide:
if gui.project_system_font:
with open(os.path.join(project.current.gamedir, "tl/None/common.rpym"), "ab") as f:
f.write("define gui.system_font = {!r}\r\n".format(gui.project_system_font).encode("utf-8"))
project.current.launch([ 'gui_images' ], env={ "RENPY_VARIANT" : "small phone" } )
project.current.launch([ 'gui_images' ])
jump front_page
+8 -63
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -25,22 +25,8 @@ from gui7.parameters import GuiParameters
import renpy.arguments
import os
import shutil
def finish(p):
for dn in [ "images", "audio", "libs" ]:
fulldn = os.path.join(p.prefix, dn)
if not os.path.exists(fulldn):
os.mkdir(fulldn)
with open(os.path.join(p.prefix, "libs", "libs.txt"), "w") as f:
f.write(renpy.translation.translate_string(renpy.store.LIBS_TXT, language=p.language))
def generate_gui(p):
ImageGenerator(p).generate_all()
@@ -50,40 +36,10 @@ def generate_gui(p):
CodeGenerator(p).copy_script("script.rpy")
CodeGenerator(p).copy_files()
finish(p)
images = os.path.join(p.prefix, "images")
def generate_minimal(p):
# Copy the template over.
os.makedirs(os.path.dirname(p.prefix), 0o777)
shutil.copytree(p.template, p.prefix)
def delete(fn):
fn = os.path.join(p.prefix, fn)
if os.path.isdir(fn):
shutil.rmtree(fn)
elif os.path.exists(fn):
os.unlink(fn)
# Prune directories.
delete("cache")
delete("saves")
delete("tl")
# Prune files to be regenerated.
delete("gui.rpy")
delete("screens.rpy")
delete("options.rpy")
# Generate files.
CodeGenerator(p).generate_code("gui.rpy")
CodeGenerator(p).generate_code("screens.rpy")
CodeGenerator(p).generate_code("options.rpy")
CodeGenerator(p).copy_files()
finish(p)
if not os.path.exists(images):
os.mkdir(images)
def generate_gui_command():
@@ -102,9 +58,7 @@ def generate_gui_command():
ap.add_argument("--start", default=False, action="store_true", help="Starts a new project, replacing images and code.")
ap.add_argument("--replace-images", default=False, action="store_true", help="True if existing images should be overwritten.")
ap.add_argument("--replace-code", default=False, action="store_true", help="True if an existing gui.rpy file should be overwritten.")
ap.add_argument("--update-code", default=False, action="store_true", help="True if an existing gui.rpy file should be updated.")
ap.add_argument("--minimal", default=False, action="store_true", help="Only update option.rpy and translations.")
ap.add_argument("--update-code", default=False, action="store_true", help="True if an existing gui.rpy file should be update.")
args = ap.parse_args()
@@ -113,16 +67,10 @@ def generate_gui_command():
args.replace_code = True
args.update_code = True
if args.minimal:
args.replace_code = True
args.update_code = True
prefix = os.path.join(args.target, "game")
if os.path.exists(args.target):
if not os.path.isdir(prefix):
ap.error("{} does not appear to be a Ren'Py game.".format(prefix))
if not os.path.isdir(prefix):
ap.error("{} does not appear to be a Ren'Py game.".format(prefix))
template = os.path.join(args.template, "game")
@@ -144,9 +92,6 @@ def generate_gui_command():
os.path.basename(args.target),
)
if args.minimal:
generate_minimal(p)
else:
generate_gui(p)
generate_gui(p)
renpy.arguments.register_command("generate_gui", generate_gui_command)
+20 -26
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -121,13 +121,8 @@ class CodeGenerator(object):
else:
template = os.path.join(self.p.template, filename)
if not os.path.exists(template):
return False
else:
with codecs.open(template, "r", "utf-8") as f:
self.lines = [ i.rstrip().replace(u"\ufeff", "") for i in f ]
return True
with codecs.open(template, "r", "utf-8") as f:
self.lines = [ i.rstrip().replace(u"\ufeff", "") for i in f ]
def remove_scale(self):
@@ -172,7 +167,7 @@ class CodeGenerator(object):
for l in self.lines:
m = re.match(r'^(\s*)define (.*?) =', l)
m = re.match('^(\s*)define (.*?) =', l)
if m:
indent = m.group(1)
@@ -219,8 +214,7 @@ class CodeGenerator(object):
'gui.insensitive_color' : repr(self.p.insensitive_color.hexcode),
'gui.text_color' : repr(self.p.text_color.hexcode),
'gui.interface_text_color' : repr(self.p.text_color.hexcode),
'gui.choice_button_text_idle_color' : repr(self.p.idle_color.hexcode),
'gui.choice_button_text_insensitive_color' : repr(self.p.insensitive_color.hexcode),
'gui.choice_text_color' : repr(self.p.choice_color.hexcode),
}
self.update_defines(replacements, language_defines[self.p.language])
@@ -286,8 +280,8 @@ class CodeGenerator(object):
for l in self.lines:
l = re.sub(r'_\((\".*?\")\)', replace, l)
l = re.sub(r'_\((\'.*?\')\)', replace, l)
l = re.sub(ur'_\((\".*?\")\)', replace, l)
l = re.sub(ur'_\((\'.*?\')\)', replace, l)
lines.append(l)
@@ -329,9 +323,6 @@ class CodeGenerator(object):
m = re.match(r'## ?([ *]*)(.*)', s)
if m is None:
raise Exception("Comment translation doesn't start with '## ': {}".format(s))
prefix = m.group(1)
empty = ' ' * len(prefix)
rest = m.group(2)
@@ -362,7 +353,7 @@ class CodeGenerator(object):
def copy_files(self):
for src, dst in language_copies[self.p.language]:
src = os.path.join(renpy.config.renpy_base, src)
src = os.path.join(renpy.config.gamedir, src)
dst = os.path.join(self.p.prefix, dst)
if os.path.exists(dst):
@@ -381,7 +372,7 @@ class CodeGenerator(object):
if os.path.exists(dst):
return
language = renpy.store._preferences.language # @UndefinedVariable
language = renpy.store._preferences.language # @UndefinedVariable
if language is None:
language = "None"
@@ -391,11 +382,16 @@ class CodeGenerator(object):
if not os.path.exists(src):
src = os.path.join(self.p.template, name)
if not self.load_template(src):
return
with open(src, "rb") as f:
data = f.read().decode("utf-8")
self.remove_scale()
self.write_target(dst)
data = data.replace(u"\ufeff", u"")
data = u"\ufeff" + data
data = data.replace(u"\r\n", u"\n")
data = data.replace(u"\n", u"\r\n")
with open(dst, "wb") as f:
f.write(data.encode("utf-8"))
def add_code(self, fn):
@@ -408,8 +404,7 @@ class CodeGenerator(object):
if not self.p.update_code:
return
if not self.load_template(fn):
return
self.load_template(fn)
if defines:
self.update_gui_defines()
@@ -430,8 +425,7 @@ class CodeGenerator(object):
if os.path.exists(target):
return
if not self.load_template(fn):
return
self.load_template(fn)
self.translate_strings()
self.translate_comments()
+19 -23
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -25,7 +25,6 @@ import os
from renpy.store import config
import renpy.display
class ImageGenerator(object):
def __init__(self, parameters):
@@ -52,7 +51,7 @@ class ImageGenerator(object):
try:
os.mkdir(self.prefix, 0o777)
except Exception:
except:
pass
self.full_width = self.width / self.scale
@@ -121,7 +120,7 @@ class ImageGenerator(object):
try:
os.makedirs(dn, 0o777)
except Exception:
except:
pass
if os.path.exists(fn):
@@ -145,7 +144,12 @@ class ImageGenerator(object):
if not self.p.skip_backup:
os.rename(fn, bfn)
pygame_sdl2.image.save(s, fn, 3)
import cStringIO
sio = cStringIO.StringIO()
renpy.display.module.save_png(s, sio, 3)
with open(fn, "wb") as f:
f.write(sio.getvalue())
def make_surface(self, width, height):
return pygame_sdl2.Surface((width, height), pygame_sdl2.SRCALPHA)
@@ -195,6 +199,7 @@ class ImageGenerator(object):
self.generate_image("textbox", X, Y, self.boring_color.opacity(.8))
YSIZE = 240
YBORDER = 5
@@ -232,6 +237,7 @@ class ImageGenerator(object):
(YSIZE, 1.0),
]
self.generate_image("nvl", X, Y, self.boring_color.opacity(.8))
X = [
@@ -246,6 +252,8 @@ class ImageGenerator(object):
self.generate_image("phone/nvl", X, Y, self.boring_color.opacity(.8))
def generate_choice_button(self):
XSIZE = 790
XINSIDE = 100
@@ -269,8 +277,6 @@ class ImageGenerator(object):
self.generate_image("button/choice_idle_background", X, Y, self.boring_color.opacity(.8))
self.generate_image("button/choice_hover_background", X, Y, self.accent_color.opacity(.95))
self.generate_image("phone/button/choice_idle_background", X, Y, self.boring_color.opacity(.8))
self.generate_image("phone/button/choice_hover_background", X, Y, self.accent_color.opacity(.95))
def generate_overlay(self):
@@ -282,6 +288,7 @@ class ImageGenerator(object):
line_width = self.scale_int(3)
if self.p.light:
opacity = 0.9
else:
@@ -325,6 +332,7 @@ class ImageGenerator(object):
s.subsurface((border, border, width - 2 * border, height - 2 * border)).fill(self.boring_color)
self.save(s, "frame")
def generate_quick_buttons(self):
width = self.scale_int(100)
height = self.scale_int(30)
@@ -371,6 +379,7 @@ class ImageGenerator(object):
self.generate_image("notify", X, Y, self.boring_color.opacity(.8))
def generate_icon(self):
icon_fn = os.path.join(config.renpy_base, "launcher", "game", "gui7", "icon.png")
@@ -397,6 +406,7 @@ class ImageGenerator(object):
self.save(surf, "window_icon", overwrite=False)
def generate_menus(self):
s = self.make_surface(self.width, self.height)
s.fill(self.menu_color)
@@ -404,20 +414,6 @@ class ImageGenerator(object):
self.save(s, "main_menu", overwrite=False)
self.save(s, "game_menu", overwrite=False)
def generate_bubble(self):
import shutil
for fn in [ "bubble.png", "thoughtbubble.png" ]:
source = os.path.join(config.renpy_base, "gui", "game", "gui", fn)
dest = os.path.join(self.prefix, fn)
if source == dest:
return
shutil.copyfile(source, dest)
def generate_all(self):
self.generate_textbox()
@@ -430,8 +426,6 @@ class ImageGenerator(object):
self.generate_notify()
self.generate_menus()
self.generate_icon()
self.generate_bubble()
if __name__ == "__main__":
import argparse
@@ -445,3 +439,5 @@ if __name__ == "__main__":
args = ap.parse_args()
ImageGenerator(args.prefix, args.width, args.height).generate_all()
+6 -10
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -61,18 +61,14 @@ class GuiParameters(object):
self.muted_color = self.accent_color.shade(.4)
self.hover_muted_color = self.accent_color.shade(.6)
if light:
self.menu_color = self.accent_color.replace_hsv_saturation(.25).replace_value(.75)
else:
self.menu_color = self.accent_color.replace_hsv_saturation(.25).replace_value(.25)
self.menu_color = self.accent_color.replace_hsv_saturation(.25).replace_value(.5)
self.title_color = self.accent_color.replace_hsv_saturation(.5).replace_value(1.0)
if light:
self.selected_color = Color("#555555")
self.idle_color = Color("#707070")
self.idle_small_color = Color("#606060")
self.idle_color = Color("#aaaaaa")
self.idle_small_color = Color("#888888")
self.text_color = Color("#404040")
self.choice_color = Color("#cccccc")
@@ -101,9 +97,9 @@ class GuiParameters(object):
self.name = name
GOOD_CHARACTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"
GOOD_CHARACTERS = "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMMOPQRSTUVWXYZ-_"
simple_name = "".join(i for i in name if i in GOOD_CHARACTERS)
simple_name = "".join(i for i in name if i in GOOD_CHARACTERS).encode("ascii")
if not simple_name:
simple_name = "game"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 433 B

After

Width:  |  Height:  |  Size: 687 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

-197
View File
@@ -1,197 +0,0 @@
# Copyright 2004-2025 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.
init python:
import fnmatch
import re
import zipfile
def install_from_zip(name, zipglob, patterns):
# Determine the filename matching the zipglob, and put it into filename.
filenames = [ i for i in os.listdir(config.renpy_base) if fnmatch.fnmatch(i.lower(), zipglob.lower()) ]
if not filenames:
interface.error(
_("Could not install [name!t], as a file matching [zipglob] was not found in the Ren'Py SDK directory."),
label="install",
name=name,
zipglob=zipglob,
)
filenames.sort(key=lambda a : a.lower())
filename = filenames[-1]
# The zipfile.
zf = zipfile.ZipFile(os.path.join(config.renpy_base, filename))
for fn in zf.namelist():
matchfn = fn.replace("\\", "/")
dstfn = None
renpy.write_log(fn)
for src, dst in patterns:
if re.match(src, matchfn):
dstfn = re.sub(src, dst, matchfn)
break
if not dstfn:
continue
dstfn = os.path.join(config.renpy_base, dstfn)
if not os.path.exists(os.path.dirname(dstfn)):
try:
os.makedirs(os.path.dirname(dstfn))
except Exception:
pass
renpy.write_log(fn + " -> " + dstfn)
data = zf.read(fn)
with open(dstfn, "wb") as f:
f.write(data)
try:
os.chmod(dstfn, 0o755)
except Exception:
pass
interface.info(_("Successfully installed [name!t]."), name=name)
label install_live2d:
python hide:
_prefix = r"lib/py3-"
patterns = [
(r".*/Core/dll/linux/x86_64/(libLive2DCubismCore.so)", _prefix + r"linux-x86_64/\1"),
(r".*/Core/dll/windows/x86_64/(Live2DCubismCore.dll)", _prefix + r"windows-x86_64/\1"),
(r".*/Core/dll/macos/(libLive2DCubismCore.dylib)", _prefix + r"mac-universal/\1"),
(r".*/Core/dll/experimental/rpi/(libLive2DCubismCore.so)", _prefix + r"linux-armv7l/\1"),
(r".*/Core/dll/android/(armeabi-v7a/libLive2DCubismCore.so)", r"rapt/prototype/renpyandroid/src/main/jniLibs/\1"),
(r".*/Core/dll/android/(arm64-v8a/libLive2DCubismCore.so)", r"rapt/prototype/renpyandroid/src/main/jniLibs/\1"),
(r".*/Core/dll/android/(x86_64/libLive2DCubismCore.so)", r"rapt/prototype/renpyandroid/src/main/jniLibs/\1"),
]
install_from_zip("Live2D Cubism SDK for Native", "CubismSdkForNative-[45]-*.zip", patterns)
jump front_page
screen install_preferences():
frame:
style "l_indent"
has vbox
text _("This screen allows you to install libraries that can't be distributed with Ren'Py. Some of these libraries may require you to agree to a third-party license before being used or distributed.")
add SPACER
if not achievement.has_steam:
textbutton _("Install Steam Support"):
action Jump("install_steam")
add HALF_SPACER
frame:
style "l_indent"
has vbox
text _("Before installing Steam support, please make sure you are a {a=https://partner.steamgames.com/}Steam partner{/a}.")
else:
textbutton _("Install Steam Support")
add HALF_SPACER
frame:
style "l_indent"
has vbox
text _("Steam support has already been installed.")
add SPACER
textbutton _("Install Live2D Cubism SDK for Native"):
action Jump("prompt_live2d")
screen install_live2d():
frame:
style_group "l"
style "l_root"
window:
has vbox
label _("Install Libraries")
add HALF_SPACER
hbox:
frame:
style "l_indent"
xfill True
viewport:
scrollbars "vertical"
mousewheel True
has vbox
add SPACER
textbutton _("Install Live2D Cubism SDK for Native"):
action Jump("install_live2d")
add HALF_SPACER
frame:
style "l_indent"
has vbox
text _("The {a=https://www.live2d.com/en/download/cubism-sdk/download-native/}Cubism SDK for Native{/a} adds support for displaying Live2D models. Place CubismSdkForNative-{i}version{/i}.zip in the Ren'Py SDK directory, and then click Install. Distributing a game with Live2D requires you to accept a license from Live2D, Inc.")
add SPACER
text _("Live2D in Ren'Py doesn't support the Web, Android x86_64 (including emulators and Chrome OS), and must be added to iOS projects manually. Live2D must be reinstalled after upgrading Ren'Py or installing Android support.")
textbutton _("Cancel") action Return(False) style "l_left_button"
textbutton _("Open Ren'Py SDK Directory") action OpenDirectory(config.renpy_base, absolute=True) style "l_right_button"
timer 2.0 action renpy.restart_interaction repeat True
label prompt_live2d:
call screen install_live2d
jump preferences
label install_steam:
$ add_dlc("steam", restart=True)
jump install
-503
View File
@@ -1,503 +0,0 @@
# Copyright 2004-2025 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.
import hashlib
import os
import time
import requests
import zipfile
import tarfile
import shutil
import subprocess
import renpy
import stat
VERSION = 1
# True if the installer should run in quiet mode.
quiet = False
from store import _, config, interface, project, Jump # type: ignore
temp_exists = False
def _ensure_temp():
"""
Ensures that the directories needed by the extension API are present.
"""
global temp_exists
if temp_exists:
return
backups = os.path.join(config.renpy_base, "tmp", "installer", "backups")
try:
if not os.path.exists(backups):
os.makedirs(os.path.dirname(backups))
except Exception:
pass
temp_exists = True
# The target directory that the extensions API operates on.
target = None
def set_target(directory):
"""
This sets the directory that the extension API targets. This is where
packages are unpacked to and the default working directory where the
programs are run.
"""
global target
target = directory
_clean("temp:", 3)
def _path(filename):
"""
Returns the full path to `filename`. If `filename` starts with the
prefix temp:, it's placed in the temp directory. If the filename
starts with backup, a backup filename is returned. Otherwise,
the path is interpreted relative to the target directory.
"""
_ensure_temp()
tempdir = os.path.join(config.renpy_base, "tmp", "installer")
backups = os.path.join(config.renpy_base, "tmp", "installer", "backups")
prefix, _, rest = filename.partition(":")
if prefix == "temp":
return os.path.join(tempdir, rest)
if prefix == "backup":
base = os.path.basename(rest.rpartition(":")[2])
return os.path.join(backups, base + "." + str(time.time()))
if prefix == "renpy":
return os.path.join(config.renpy_base, rest)
if target is None:
raise Exception("The target directory has not been set.")
return os.path.join(target, filename)
def _clean(directory, age=3):
"""
Removes files from `directory` that are older than `age` days.
"""
directory = _path(directory)
for root, dirs, files in os.walk(directory, topdown=False):
for f in files:
filename = os.path.join(root, f)
mtime = os.stat(filename).st_mtime
if time.time() - mtime > age * 86400:
try:
os.unlink(filename)
except Exception:
pass
if root != directory:
try:
os.rmdir(root)
except Exception:
pass
def _friendly(filename):
"""
Returns a version of the filename without any leading prefix.
"""
return filename.rpartition(":")[2]
def _check_hash(filename, hashj):
"""
Returns a cryptographic hash of `filename`. `filename` should
be a full path, one returned by temp or path.
"""
try:
sha = hashlib.sha256()
with open(filename, "rb") as f:
while True:
data = f.read(1024 * 1024)
if not data:
break
sha.update(data)
return sha.hexdigest() == hash
except Exception:
return False
# The name and url of the file that is currently being downloaded. This is meant to
# to be used by the interface screens to show the user what files are being
# downloaded.
download_file = ""
download_url = ""
def download(url, filename, hash=None, headers=None, requests_kwargs={}):
"""
Downloads `url` to `filename`, a tempfile.
"""
global download_url
global download_file
download_url = url
download_file = _friendly(filename)
filename = _path(filename)
if hash is not None:
if _check_hash(filename, hash):
return
progress_time = time.time()
all_headers = {"User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36 renpy/8'}
if headers:
all_headers.update(headers)
try:
response = requests.get(url, stream=True, proxies=renpy.exports.proxies, timeout=15, headers=all_headers, **requests_kwargs)
response.raise_for_status()
total_size = int(response.headers.get('content-length', 1))
downloaded = 0
with open(filename, "wb") as f:
for i in response.iter_content(65536):
f.write(i)
downloaded += len(i)
if time.time() - progress_time > 0.1:
progress_time = time.time()
if not quiet:
interface.processing(
_("Downloading [installer.download_file]..."),
complete=downloaded, total=total_size)
except requests.HTTPError as e:
if not quiet:
raise
interface.error(_("Could not download [installer.download_file] from [installer.download_url]:\n{b}[installer.download_error]"))
if hash is not None:
if not quiet:
raise Exception("Hash check failed.")
if not _check_hash(filename, hash):
interface.error(_("The downloaded file [installer.download_file] from [installer.download_url] is not correct."))
class _FixedZipFile(zipfile.ZipFile):
"""
A patched version of zipfile.ZipFile that adds support for:
* Unix permissions bits.
* Unix symbolic links.
"""
def _extract_member(self, member, targetpath, pwd):
if not isinstance(member, zipfile.ZipInfo):
member = self.getinfo(member)
# build the destination pathname, replacing
# forward slashes to platform specific separators.
arcname = member.filename.replace('/', os.path.sep)
if os.path.altsep:
arcname = arcname.replace(os.path.altsep, os.path.sep)
# interpret absolute pathname as relative, remove drive letter or
# UNC path, redundant separators, "." and ".." components.
arcname = os.path.splitdrive(arcname)[1]
invalid_path_parts = ('', os.path.curdir, os.path.pardir)
arcname = os.path.sep.join(x for x in arcname.split(os.path.sep) if x not in invalid_path_parts)
targetpath = os.path.join(targetpath, arcname)
targetpath = os.path.normpath(targetpath)
# Create all upper directories if necessary.
upperdirs = os.path.dirname(targetpath)
if upperdirs and not os.path.exists(upperdirs):
os.makedirs(upperdirs)
if member.filename.endswith("/"):
if not os.path.isdir(targetpath):
os.mkdir(targetpath)
return targetpath
attr = member.external_attr >> 16
if stat.S_ISLNK(attr):
with self.open(member, pwd=pwd) as source:
linkto = source.read()
os.symlink(linkto, targetpath)
else:
with self.open(member, pwd=pwd) as source, open(targetpath, "wb") as target:
shutil.copyfileobj(source, target)
if attr:
os.chmod(targetpath, attr)
return targetpath
# The name of the archive being unpacked.
unpack_archive = ""
def unpack(archive, destination):
"""
Unpacks `archive` to `destination`. `archive` should be the name of
a zip or (perhaps compressed) tar file. `destination` should be a
directory that the contents are unpacked into.
"""
global unpack_archive
unpack_archive = _friendly(archive)
if not quiet:
interface.processing(_("Unpacking [installer.unpack_archive]..."))
archive = _path(archive)
destination = _path(destination)
if not os.path.exists(destination):
os.makedirs(destination)
old_cwd = os.getcwd()
try:
os.chdir(destination)
if tarfile.is_tarfile(archive):
tar = tarfile.open(archive)
tar.extractall(".")
tar.close()
elif zipfile.is_zipfile(archive):
zip = _FixedZipFile(archive)
zip.extractall(".")
zip.close()
else:
raise Exception("Unknown file type.")
finally:
os.chdir(old_cwd)
def exists(filename):
"""
Returns true if `filename` exists.
"""
return os.path.exists(_path(filename))
def remove(filename):
"""
Removes a file or directory from the target directory, backing it up
the temporary directory.
"""
if not exists(filename):
return
backup = _path("backup:" + filename)
shutil.move(_path(filename), backup)
# Now, touch everything so _cleanup doesn't get it too quickly.
if os.path.isdir(backup):
for root, dirs, files in os.walk(backup):
for f in files:
try:
os.utime(os.path.join(root, f), None)
except Exception:
pass
else:
try:
os.utime(backup, None)
except Exception:
pass
def move(old_filename, new_filename):
"""
Moves a filename from `old_filename` to `new_filename`.
"""
remove(new_filename)
shutil.move(_path(old_filename), _path(new_filename))
def mkdir(dirname):
"""
Makes the named directory.
"""
if not os.path.exists(_path(dirname)):
os.makedirs(_path(dirname))
def info(message, **kwargs):
"""
Displays `message` to the user, asking them to click through or
cancel.
"""
interface.info(message, cancel=Jump("front_page"), **kwargs)
def processing(message, **kwargs):
"""
Displays `message` to the user, without waiting.
"""
interface.processing(message, **kwargs)
def error(message, **kwargs):
"""
Displays `message` to the user, as an error.
"""
interface.error(message)
install_args = [ ]
install_error = ""
def run(*args, **kwargs):
"""
Runs a program with the given arguments, in the target directory.
"""
environ = { renpy.exports.fsencode(k) : renpy.exports.fsencode(v) for k, v in os.environ.items() }
for k, v in kwargs.pop("environ", {}).items():
environ[renpy.exports.fsencode(k)] = renpy.exports.fsencode(v)
global install_args
global install_error
args = [ renpy.exports.fsencode(i) for i in args ]
try:
subprocess.check_call(args, cwd=target, env=environ) # type: ignore
except Exception as e:
install_args = args
install_error = str(e)
interface.error(_("Could not run [installer.install_args!r]:\n[installer.install_error]"))
_renpy = renpy
def manifest(url, renpy=False, insecure=False):
"""
Executes the manifest at `url`.
`renpy`
If true, the manifest applies to Ren'Py. If False, the manifest applies
to the current project.
`insecure`
If true, verificaiton is disabled.
"""
import ecdsa
download(url, "temp:manifest.py")
with open(_path("temp:manifest.py"), "rb") as f:
manifest = f.read()
if not insecure:
download(url + ".sig", "temp:manifest.py.sig")
with open(_path("temp:manifest.py.sig"), "rb") as f:
sig = f.read()
key = ecdsa.VerifyingKey.from_pem(_renpy.exports.open_file("renpy_ecdsa_public.pem").read())
if not key.verify(sig, manifest):
error(_("The manifest signature is not valid."))
return
if renpy:
set_target(config.renpy_base)
else:
if project.current is None:
error(_("No project has been selected."))
return
set_target(project.current.path)
exec(manifest.decode("utf-8"), {}, {})
def local_manifest(filename, renpy=False):
"""
Executes the manifest in `filename`.
`renpy`
If true, the manifest applies to Ren'Py. If False, the manifest applies
to the current project.
"""
if renpy:
set_target(config.renpy_base)
else:
if project.current is None:
error(_("No project has been selected."))
return
set_target(project.current.path)
with open(filename, "r") as f:
exec(f.read(), {}, {})
-12
View File
@@ -1,12 +0,0 @@
# This file imports the extensions API into the default store, and makes it
# also contains the strings used by the extensions API, so the Ren'Py translation
# framework can find them.
init python:
import installer
init python hide:
_("Downloading [extension.download_file].")
_("Could not download [extension.download_file] from [extension.download_url]:\n{b}[extension.download_error]")
_("The downloaded file [extension.download_file] from [extension.download_url] is not correct.")
+45 -86
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -22,49 +22,49 @@
################################################################################
# Interface actions.
init python in interface:
from store import OpenURL, config, Return, _preferences, persistent
from store import OpenURL, config, Return, _preferences
import store
import os.path
import contextlib
RENPY_URL = "http://www.renpy.org"
DOC_PATH = os.path.join(config.renpy_base, "doc/")
RENPY_GAMES_URL = "http://games.renpy.org"
DOC_PATH = os.path.join(config.renpy_base, "doc/index.html")
DOC_URL = "http://www.renpy.org/doc/html/"
DOC_LOCAL_URL = "file:///" + DOC_PATH
# If true, the launcher should not link back to renpy.org.
NO_LAUNCHER_LINKS = os.path.exists(os.path.join(config.renpy_base, "no_launcher_links.txt"))
LICENSE_PATH = os.path.join(config.renpy_base, "doc/license.html")
LICENSE_URL = "http://www.renpy.org/doc/html/license.html"
if os.path.exists(DOC_PATH):
DOC_LOCAL_URL = "file:///" + DOC_PATH
else:
DOC_LOCAL_URL = None
local_doc_exists = os.path.exists(DOC_PATH)
if os.path.exists(LICENSE_PATH):
LICENSE_LOCAL_URL = "file:///" + LICENSE_PATH
else:
LICENSE_LOCAL_URL = None
def get_doc_url(page):
"""
Returns the URL to the documentation page.
"""
if local_doc_exists and (NO_LAUNCHER_LINKS or not persistent.use_web_doc):
from urllib.parse import urljoin
from urllib.request import pathname2url
return urljoin('file:', pathname2url(DOC_PATH + page))
else:
return DOC_URL + page
def OpenDocumentation(page="index.html"):
def OpenDocumentation():
"""
An action that opens the documentation.
"""
return OpenURL(get_doc_url(page))
if DOC_LOCAL_URL is not None:
return OpenURL(DOC_LOCAL_URL)
else:
return OpenURL(DOC_URL)
def OpenLicense():
"""
An action that opens the license.
"""
return OpenDocumentation("license.html")
if LICENSE_LOCAL_URL is not None:
return OpenURL(LICENSE_LOCAL_URL)
else:
return OpenURL(LICENSE_URL)
def get_sponsor_url():
"""
@@ -89,10 +89,6 @@ init python in interface:
finally:
links = True
# Version.
import re
version = re.sub(r'\.\d+(\w*)$', r'\1', renpy.version())
# This displays the bottom of the screen. If the tooltip is not None, this displays the
# tooltip. Otherwise, it displays a list of links (to various websites, and to the
# preferences and update screen), or is just blank.
@@ -121,26 +117,21 @@ screen bottom_info:
hbox:
spacing INDENT
textbutton _("Documentation") style "l_link" action interface.OpenDocumentation()
if not interface.NO_LAUNCHER_LINKS:
textbutton _("Ren'Py Website") style "l_link" action OpenURL(interface.RENPY_URL)
textbutton _("[interface.version]") style "l_link" action Jump("about")
textbutton _("Ren'Py Website") style "l_link" action OpenURL(interface.RENPY_URL)
textbutton _("Ren'Py Games List") style "l_link" action OpenURL(interface.RENPY_GAMES_URL)
textbutton _("About") style "l_link" action Jump("about")
hbox:
spacing INDENT
xalign 1.0
if ability.can_update:
textbutton _("update") action Jump("update") style "l_link":
if persistent.has_update:
text_color "#F96854"
text_hover_color Color("#F96854").tint(.8)
textbutton _("update") action Jump("update") style "l_link"
textbutton _("preferences") style "l_link" action Jump("preferences")
textbutton _("quit") style "l_link" action Quit(confirm=False)
if persistent.sponsor_message and not interface.NO_LAUNCHER_LINKS:
if persistent.sponsor_message:
textbutton _("Ren'Py Sponsor Information"):
style "l_link"
@@ -174,7 +165,7 @@ screen common:
has vbox
text message:
textalign 0.5
text_align 0.5
xalign 0.5
layout "subtitle"
@@ -204,15 +195,7 @@ screen common:
add SPACER
for v, l in choices:
textbutton l:
action SetScreenVariable("selected", v)
selected_background REVERSE_IDLE
selected_hover_background REVERSE_HOVER
xpadding 20
size_group "choice"
text_selected_idle_color REVERSE_TEXT
text_selected_hover_color REVERSE_TEXT
text_xalign 0.5
textbutton l action SetScreenVariable("selected", v)
if selected is not None:
$ continue_ = Return(selected)
@@ -223,7 +206,7 @@ screen common:
add SPACER
text submessage:
textalign 0.5
text_align 0.5
xalign 0.5
layout "subtitle"
@@ -262,19 +245,13 @@ screen launcher_input:
has vbox
text message:
textalign 0.5
text_align 0.5
xalign 0.5
layout "subtitle"
add SPACER
input style "l_default":
value ScreenVariableInputValue("value", returnable=True)
size 24
xalign 0.5
color INPUT_COLOR
allow allow
copypaste True
input style "l_default" value ScreenVariableInputValue("value", returnable=True) size 24 xalign 0.5 color INPUT_COLOR
if filename:
add SPACER
@@ -352,7 +329,7 @@ init python in interface:
def error(message, submessage=None, label="front_page", **kwargs):
"""
Indicates to the user that an error has occurred.
Indicates to the user that an error has occured.
`message`
The message to display.
@@ -399,28 +376,15 @@ init python in interface:
As an example of usage::
with interface.error_handling(_("opening the log file")):
with interface.error_handling("opening the log file"):
f = open("log.txt", "w")
"""
try:
yield
except Exception as e:
renpy.renpy.error.report_exception(e, editor=False)
store._ignore_action = Jump(label)
yield
store._ignore_action = Jump("front_page")
error(_("While [what!qt], an error occurred:"),
_("[exception!q]"),
what=what,
label=label,
exception=traceback.format_exception_only(type(e), e)[-1][:-1])
import string
DIGITS_LETTERS = string.digits
PROJECT_LETTERS = string.digits + string.ascii_letters + " _"
FILENAME_LETTERS = PROJECT_LETTERS + "\\/"
TRANSLATE_LETTERS = string.ascii_letters + string.digits + "_"
def input(title, message, filename=False, sanitize=True, cancel=None, allow=None, default=""):
def input(title, message, filename=False, sanitize=True, cancel=None, default=""):
"""
Requests typewritten input from the user.
"""
@@ -429,15 +393,7 @@ init python in interface:
while True:
rv = renpy.call_screen(
"launcher_input",
title=title,
message=message,
filename=filename or (allow in [PROJECT_LETTERS, FILENAME_LETTERS]),
allow=allow,
cancel=cancel,
default=rv
)
rv = renpy.call_screen("launcher_input", title=title, message=message, filename=filename, cancel=cancel, default=rv)
if sanitize:
if ("[" in rv) or ("{" in rv):
@@ -451,7 +407,7 @@ init python in interface:
try:
rv.encode("ascii")
except Exception:
except:
error(_("File and directory names must consist of ASCII characters."), label=None)
continue
@@ -496,7 +452,7 @@ init python in interface:
The amount of time to pause for after showing the message.
"""
common(title, store.INTERACTION_COLOR, message, submessage=submessage, pause=pause, show_screen=True, **kwargs)
common(title, store.INTERACTION_COLOR, message, submessage=None, pause=pause, show_screen=True, **kwargs)
renpy.pause(pause)
def processing(message, submessage=None, complete=None, total=None, **kwargs):
@@ -551,3 +507,6 @@ init python in interface:
"""
return common(_("CHOICE"), store.QUESTION_COLOR, message, choices=choices, selected=selected, **kwargs)
+28 -111
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -58,8 +58,6 @@ init python:
if RENIOS_PATH:
import renios.create
import renios.image
import renios.interface
def IOSState():
if not RENIOS_PATH:
@@ -106,15 +104,12 @@ init python:
xcode_name_cache[s] = s
return s
def xcode_project(p=None, target=None):
def xcode_project(p=None):
"""
Return the path to the Xcode project corresponding to `p`, or the current
project if `p` is None
"""
if target is not None:
return target
if p is None:
p = project.current
@@ -123,24 +118,12 @@ init python:
return os.path.join(persistent.xcode_projects_directory, xcode_name(p.name))
def ios_create(p=None, gui=True, target=None):
if p is None:
p = project.current
p.update_dump(force=True, gui=gui)
def ios_create(p=None, gui=True):
name = p.dump.get("name", None)
version = p.dump.get("version", None)
dest = xcode_project(p, target)
if gui:
iface = MobileInterface("ios")
else:
iface = renios.interface.Interface()
dest = xcode_project(p)
if os.path.exists(dest):
if not iface.yesno(_("The Xcode project already exists. Would you like to rename the old project, and replace it with a new one?")):
return
interface.yesno(_("The Xcode project already exists. Would you like to rename the old project, and replace it with a new one?"), no=Jump("ios"))
i = 0
while True:
@@ -151,35 +134,12 @@ init python:
os.rename(dest, backup)
renios.create.create_project(iface, dest, name, version)
iface = MobileInterface("ios")
renios.create.create_project(iface, dest)
ios_populate(p, gui=gui, target=target)
ios_populate(p, gui=gui)
def eliminate_pycache(directory):
"""
Eliminates the __pycache__ directory, and moves the files in it up a level,
renaming them to remove the cache tag.
"""
print("Eliminating __pycache__...")
import pathlib
import sys
paths = list(pathlib.Path(directory).glob("**/__pycache__/*.pyc"))
for p in paths:
name = p.stem.partition(".")[0]
p.rename(p.parent.parent / (name + ".pyc"))
paths = list(pathlib.Path(directory).glob("**/__pycache__"))
for p in paths:
p.rmdir()
def ios_populate(p=None, gui=True, target=None):
def ios_populate(p=None, gui=True):
"""
This actually builds the package.
"""
@@ -189,7 +149,7 @@ init python:
if p is None:
p = project.current
dist = os.path.join(xcode_project(p, target), "base")
dist = os.path.join(xcode_project(p), "base")
if os.path.exists(dist):
shutil.rmtree(dist)
@@ -208,8 +168,6 @@ init python:
report_success=False,
)
eliminate_pycache(dist)
main_fn = os.path.join(dist, "main.py")
for fn in os.listdir(dist):
@@ -229,29 +187,13 @@ init python:
os.unlink(py_fn)
ios_image(p, "ios-icon.png", "Media.xcassets/AppIcon.appiconset", True, target)
# ios_image(p, "ios-launchimage.png", "Media.xcassets/LaunchImage.launchimage", False, target)
def ios_image(p, source, destination, scale, target):
source = os.path.join(p.path, source)
destination = os.path.join(xcode_project(p, target), destination)
renios.image.generate(source, destination, scale)
def launch_xcode():
dist = xcode_project(None)
base = os.path.basename(dist)
xcodeproj = "{}/{}.xcodeproj".format(dist, base)
if not os.path.exists(dist):
return
for fn in os.listdir(dist):
if fn.endswith(".xcodeproj"):
xcodeproj = os.path.join(dist, fn)
subprocess.call([ 'open', renpy.fsencode(xcodeproj) ])
break
subprocess.call([ 'open', renpy.fsencode(xcodeproj) ])
screen ios:
@@ -336,9 +278,21 @@ screen ios:
action IOSIfState(state, IOS_OK, launch_xcode)
hovered tt.Action(IOS_XCODE_TEXT)
add SPACER
textbutton _("Force Recompile") action DataToggle("force_recompile") style "l_checkbox"
# textbutton _("Configure"):
# action AndroidIfState(state, ANDROID_NO_CONFIG, Jump("android_configure"))
# hovered tt.Action(CONFIGURE_TEXT)
#
# textbutton _("Build Package"):
# action AndroidIfState(state, ANDROID_OK, AndroidBuild("android_build"))
# hovered tt.Action(BUILD_TEXT)
#
# textbutton _("Build & Install"):
# action AndroidIfState(state, ANDROID_OK, AndroidBuild("android_build_and_install"))
# hovered tt.Action(BUILD_AND_INSTALL_TEXT)
#
# textbutton _("Build, Install & Launch"):
# action AndroidIfState(state, ANDROID_OK, AndroidBuild("android_build_install_and_launch"))
# hovered tt.Action(BUILD_INSTALL_AND_LAUNCH_TEXT)
add SPACER
add SEPARATOR2
@@ -376,10 +330,6 @@ screen ios:
add SPACER
text _("There are known issues with the iOS simulator on Apple Silicon. Please test on x86_64 or iOS devices.")
add SPACER
if tt.value:
text tt.value
else:
@@ -422,36 +372,3 @@ label update_xcode_project:
$ ios_populate(None, True)
jump ios
init python:
def ios_create_command():
ap = renpy.arguments.ArgumentParser()
ap.add_argument("project", help="The path to the Ren'Py project.")
ap.add_argument("destination", help="The path the iOS project that will be created.")
args = ap.parse_args()
p = project.Project(args.project)
ios_create(p, False, args.destination)
return False
renpy.arguments.register_command("ios_create", ios_create_command)
def ios_populate_command():
ap = renpy.arguments.ArgumentParser()
ap.add_argument("project", help="The path to the Ren'Py project.")
ap.add_argument("destination", help="The path the iOS project that will be created.")
args = ap.parse_args()
p = project.Project(args.project)
ios_populate(p, False, args.destination)
return False
renpy.arguments.register_command("ios_populate", ios_populate_command)
+37 -50
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -23,52 +23,23 @@ init python:
def find_itch_butler():
import requests
import os
if renpy.windows:
platform = "windows-amd64"
exe = "butler.exe"
rv = os.path.join(os.environ.get("APPDATA", ""), "Roaming", "itch", "bin", "butler.exe")
if not os.path.exists(rv):
rv = os.path.join(os.environ.get("APPDATA", ""), "itch", "bin", "butler.exe")
elif renpy.macintosh:
platform = "darwin-amd64"
exe = "butler"
rv = os.path.join(os.environ.get("HOME", ""), "Library", "Application Support", "itch", "bin", "butler")
else:
platform = "linux-amd64"
exe = "butler"
rv = os.path.join(os.environ.get("HOME", ""), ".config", "itch", "bin", "butler")
dn = os.path.join(config.renpy_base, "tmp", "butler-" + platform)
zip = os.path.join(dn, "butler.zip")
exe = os.path.join(dn, exe)
if os.path.exists(exe):
return exe
interface.processing(_("Downloading the itch.io butler."))
try:
os.makedirs(dn)
except Exception:
pass
with interface.error_handling(_("Downloading the itch.io butler.")):
url = "https://broth.itch.zone/butler/{}/LATEST/archive/default".format(platform)
response = requests.get(url, headers={'User-Agent' : "Renpy"}, proxies=renpy.proxies)
with open(zip, "wb") as f:
f.write(response.content)
import zipfile
with zipfile.ZipFile(zip) as zf:
zf.extractall(dn)
try:
os.chmod(exe, 0o755)
except Exception:
pass
return exe
if not os.path.exists(rv):
return None
return rv
label itch:
@@ -77,8 +48,6 @@ label itch:
python hide:
butler = find_itch_butler()
build = project.current.dump["build"]
destination = build["destination"]
@@ -97,14 +66,32 @@ label itch:
files = [ ]
for fn in os.listdir(destination):
fn = os.path.join(destination, fn)
if fn.endswith("-all.zip"):
channel = "win-osx-linux"
elif fn.endswith("-market.zip"):
channel = "win-osx-linux"
elif fn.endswith("-pc.zip"):
channel = "win-linux"
elif fn.endswith("-win.zip"):
channel = "win"
elif fn.endswith("-mac.zip"):
channel = "osx"
elif fn.endswith("-linux.tar.bz2"):
channel = "linux"
elif fn.endswith("-release.apk"):
channel = "android"
for pattern, channel in reversed(build['itch_channels'].items()):
if fnmatch.fnmatch(fn, pattern):
break
else:
continue
fn = os.path.join(destination, fn)
files.append((fn, channel))
if not os.path.exists(destination):
@@ -113,6 +100,8 @@ label itch:
label="build_distributions"
)
butler = find_itch_butler()
if not butler:
interface.error(
_("The butler program was not found."),
@@ -137,9 +126,7 @@ label itch:
butler,
"push",
filename,
itch_project + ":" + channel,
"--userversion",
build["version"],
itch_project + ":" + build["version"] + "-" + channel,
)
cc.run()
+12 -21
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -32,7 +32,7 @@ init -1 python:
if not renpy.loadable(fn1):
return True
hash1 = renpy.open_file(fn1, encoding="utf-8").read()
hash1 = renpy.file(fn1).read()
if not os.path.exists(fn2):
return False
@@ -84,8 +84,7 @@ init -1 python:
self.info_msg = ""
with open(self.filename, "w") as f:
f.write(renpy.version() + "\n")
with open(self.filename, "w"):
pass
def log(self, msg):
@@ -99,10 +98,6 @@ init -1 python:
interface.processing(prompt, pause=False)
self.log(prompt)
def open_directory(self, directory, prompt):
renpy.run(store.OpenDirectory(directory, absolute=True))
interface.info(prompt)
def yesno(self, prompt, submessage=None):
return interface.yesno(prompt, submessage=submessage)
@@ -159,11 +154,12 @@ init -1 python:
try:
while self.run_yes:
self.process.stdin.write(b'y\n')
self.process.stdin.write('y\n')
self.process.stdin.flush()
time.sleep(.2)
except Exception:
pass
except:
import traceback
traceback.print_exc()
def call(self, cmd, cancel=False, use_path=False, yes=False):
@@ -193,13 +189,12 @@ init -1 python:
interface.processing(self.info_msg, show_screen=True, cancel=cancel_action)
kwargs = { }
if yes:
kwargs["stdin"] = subprocess.PIPE
try:
self.process = subprocess.Popen(cmd, cwd=renpy.fsencode(RAPT_PATH), stdout=f, stderr=f, stdin=subprocess.PIPE, startupinfo=startupinfo, **kwargs)
# avoid SIGTTIN caused by e.g. gradle doing empty read on terminal stdin
if not yes:
self.process.stdin.close()
except Exception:
self.process = subprocess.Popen(cmd, cwd=renpy.fsencode(RAPT_PATH), stdout=f, stderr=f, startupinfo=startupinfo, **kwargs)
except:
import traceback
traceback.print_exc(file=f)
raise
@@ -220,11 +215,6 @@ init -1 python:
self.run_yes = False
self.yes_thread.join()
try:
self.process.stdin.close()
except Exception:
pass
self.process = None
self.yes_thread = None
@@ -269,3 +259,4 @@ init -1 python:
self.process.terminate()
renpy.jump(self.platform)
+4 -17
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -19,9 +19,6 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# Should translation files be shown in the launcher file navigation?
default persistent.show_translation_files = True
init python in navigation:
import store.interface as interface
import store.project as project
@@ -113,9 +110,6 @@ init python in navigation:
if shortfn.startswith("game/"):
shortfn = fn[5:]
if shortfn.startswith("tl/") and not persistent.show_translation_files:
continue
rv.append((shortfn, fn, None))
rv.sort()
@@ -161,8 +155,6 @@ init python in navigation:
screen navigation:
$ todo_count = len(project.current.dump.get("location", {}).get("todo", []))
frame:
style_group "l"
style "l_root"
@@ -174,7 +166,6 @@ screen navigation:
frame style "l_label":
has hbox xfill True
text _("Navigate: [project.current.display_name!q]") style "l_label_text"
alt _("Navigate Script")
frame:
style "l_alternate"
@@ -201,7 +192,6 @@ screen navigation:
hbox:
spacing HALF_INDENT
text _("Category:")
alt ""
textbutton _("files") action navigation.ChangeKind("file")
textbutton _("labels") action navigation.ChangeKind("label")
@@ -209,7 +199,7 @@ screen navigation:
textbutton _("transforms") action navigation.ChangeKind("transform")
textbutton _("screens") action navigation.ChangeKind("screen")
textbutton _("callables") action navigation.ChangeKind("callable")
textbutton (__("TODOs") + " (" + str(todo_count) + ")") action navigation.ChangeKind("todo")
textbutton _("TODOs") action navigation.ChangeKind("todo")
add SPACER
add SEPARATOR
@@ -226,10 +216,6 @@ screen navigation:
vbox:
style_group "l_navigation"
if persistent.navigation == "file":
textbutton _("Show translation files") style "l_checkbox" action [ ToggleField(persistent, "show_translation_files"), Jump("navigation_loop") ]
add SPACER
for group_name, group in groups:
if group_name is not None:
@@ -261,7 +247,7 @@ screen navigation:
if persistent.navigation == "todo":
text _("No TODO comments found.\n\nTo create one, include \"# TODO\" in your script."):
textalign 0.5
text_align 0.5
xalign 0.5
yalign 0.5
@@ -291,3 +277,4 @@ label navigation_loop:
label navigation_refresh:
$ project.current.update_dump(True)
jump navigation_loop
+142 -35
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -25,6 +25,34 @@ init python:
import time
import re
def check_language_support():
language = _preferences.language
new = False
legacy = False
# Check for a translation of the words "New GUI Interface".
if (language is None) or (__("New GUI Interface") != "New GUI Interface"):
new = True
try:
if (language is None) or os.path.exists(os.path.join(config.renpy_base, "templates", language)):
legacy = True
except:
pass
if new and legacy:
store.language_support = _("Both interfaces have been translated to your language.")
elif new:
store.language_support = _("Only the new GUI has been translated to your language.")
elif legacy:
store.language_support = _("Only the legacy theme interface has been translated to your language.")
else:
store.language_support = _("Neither interface has been translated to your language.")
label new_project:
@@ -34,48 +62,127 @@ label new_project:
if persistent.projects_directory is None:
$ interface.error(_("The projects directory could not be set. Giving up."))
python:
new_project_language = __("{#language name and font}")
gui_kind = "new_gui_project"
if not persistent.legacy:
python:
new_project_language = (_preferences.language or "english").title()
# When translating this, feel free to replace [new_project_language] with the translation of your language.
interface.info(_("You will be creating an [new_project_language]{#this substitution may be localized} language project. Change the launcher language in preferences to create a project in another language."), cancel=Jump("front_page"))
$ interface.info(_("You will be creating an [new_project_language] language project. Change the launcher language in preferences to create a project in another language."))
jump new_gui_project
python:
if project.manager.templates:
gui_kind, gui_template_path = interface.choice(
_("Please select a template project to use."),
[ ( ("new_gui_project", None), _("Do not use a template project.") ) ] +
[ ( ("new_template_project", p.path ), p.name) for p in project.manager.templates ],
("new_gui_project", None),
cancel=Jump("front_page"),
check_language_support()
gui_kind = interface.choice(
_("Which interface would you like to use? The new GUI has a modern look, supports wide screens and mobile devices, and is easier to customize. Legacy themes might be necessary to work with older example code.\n\n[language_support!t]\n\nIf in doubt, choose the new GUI, then click Continue on the bottom-right."),
[ ( 'new_gui_project', _("New GUI Interface") ), ( 'new_theme_project', _("Legacy Theme Interface")) ],
"new_gui_project",
cancel=Jump("front_page"),
)
python:
project_name = ""
while True:
project_name = interface.input(
_("PROJECT NAME"),
_("Please enter the name of your project:"),
allow=interface.PROJECT_LETTERS,
cancel=Jump("front_page"),
default=project_name,
)
renpy.jump(gui_kind)
project_name = project_name.strip()
if not project_name:
interface.error(_("The project name may not be empty."), label=None)
continue
screen select_template:
project_dir = os.path.join(persistent.projects_directory, project_name)
default result = project.manager.get("english")
if project.manager.get(project_name) is not None:
interface.error(_("[project_name!q] already exists. Please choose a different project name."), project_name=project_name, label=None)
continue
frame:
style_group "l"
style "l_root"
if os.path.exists(project_dir):
interface.error(_("[project_dir!q] already exists. Please choose a different project name."), project_dir=project_dir, label=None)
continue
break
window:
jump expression gui_kind
has vbox
label _("Choose Project Template")
hbox:
frame:
style "l_indent"
xmaximum ONETHIRD
viewport:
scrollbars "vertical"
vbox:
for p in project.manager.templates:
textbutton "[p.name!q]" action SetScreenVariable("result", p) style "l_list"
frame:
style "l_indent"
xmaximum TWOTHIRDS
text _("Please select a template to use for your new project. The template sets the default font and the user interface language. If your language is not supported, choose 'english'.")
textbutton _("Return") action Jump("front_page") style "l_left_button"
textbutton _("Continue") action Return(result) style "l_right_button"
label new_theme_project:
python hide:
project_name = interface.input(
_("PROJECT NAME"),
_("Please enter the name of your project:"),
filename=True,
cancel=Jump("front_page"))
project_name = project_name.strip()
if not project_name:
interface.error(_("The project name may not be empty."))
project_dir = os.path.join(persistent.projects_directory, project_name)
if project.manager.get(project_name) is not None:
interface.error(_("[project_name!q] already exists. Please choose a different project name."), project_name=project_name)
if os.path.exists(project_dir):
interface.error(_("[project_dir!q] already exists. Please choose a different project name."), project_dir=project_dir)
if len(project.manager.templates) == 1:
template = project.manager.templates[0]
else:
template = renpy.call_screen("select_template")
template_path = template.path
with interface.error_handling("creating a new project"):
shutil.copytree(template_path, project_dir, symlinks=False)
# Delete the tmp directory, if it exists.
if os.path.isdir(os.path.join(project_dir, "tmp")):
shutil.rmtree(os.path.join(project_dir, "tmp"))
# Delete project.json, which must exist.
os.unlink(os.path.join(project_dir, "project.json"))
# Change the save directory in options.rpy
fn = os.path.join(project_dir, "game/options.rpy")
with open(fn, "rb") as f:
options = f.read().decode("utf-8")
options = options.replace("PROJECT_NAME", project_name)
options = options.replace("UNIQUE", str(int(time.time())))
with open(fn, "wb") as f:
f.write(options.encode("utf-8"))
font = template.data.get("font", None)
if font is not None:
src = os.path.join(config.gamedir, "fonts", font)
dst = os.path.join(project_dir, "game", "tl", "None", font)
shutil.copy(src, dst)
# Activate the project.
with interface.error_handling("activating the new project"):
project.manager.scan()
project.Select(project.manager.get(project_name))()
call choose_theme_callable
jump front_page
+55 -128
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -30,9 +30,6 @@
init -1 python hide:
# Update the searchpath to find additional fonts.
config.searchpath.append(config.renpy_base + "/sdk-fonts")
# Should we enable the use of developer tools? This should be
# set to False before the game is released, so the user can't
# cheat using developer tools.
@@ -184,17 +181,11 @@ init -1 python hide:
#####################
# More customizations can go here.
config.has_sound = False
config.has_music = False
config.has_voice = False
config.force_sound = False
config.sound = False
config.quit_action = Quit(confirm=False)
if renpy.macintosh:
config.window_icon = "images/window-icon-mac.png"
else:
config.window_icon = "images/window-icon.png"
config.gl_resize = False
config.window_icon = "images/logo.png"
config.windows_icon = "images/logo32.png"
config.has_autosave = False
config.log_enable = False
config.mouse_hide_time = None
@@ -250,17 +241,15 @@ init python:
# allows the updater to run.
build.include_update = True
# Build both kinds of updates.
build.update_formats = [ "zsync", "rpu" ]
# Allow empty directories, so we can distribute the images directory.
build.exclude_empty_directories = False
# Mac signing options.
import os
build.mac_identity = os.environ.get("RENPY_MAC_IDENTITY", None)
build.mac_codesign_command = [ config.renpy_base + "/scripts/mac/mac_sign_client.sh", "{identity}", "{app}" ]
build.mac_create_dmg_command = [ config.renpy_base + "/scripts/mac/mac_dmg_client.sh", "{identity}", "{volname}", "{sourcedir}", "{dmg}" ]
build.mac_codesign_command = [ "/home/tom/ab/renpy-deps/mac/mac_sign_client.sh", "{identity}", "{app}" ]
build.mac_create_dmg_command = [ "/home/tom/ab/renpy-deps/mac/mac_dmg_client.sh", "{identity}", "{volname}", "{sourcedir}", "{dmg}" ]
build.mac_codesign_dmg_command = [ "/bin/true" ]
@@ -280,10 +269,7 @@ init python:
build.classify_renpy("**/thumbs.db", None)
build.classify_renpy("**/.*", None)
build.classify_renpy("rapt/**/libLive2DCubismCore.so", None)
build.classify_renpy("rapt/symbols/", None)
build.classify_renpy("rapt/**", "rapt")
build.executable("rapt/prototype/gradlew")
build.classify_renpy("renios/prototype/base/", None)
build.classify_renpy("renios/prototype/prototype.xcodeproj/*.xcworkspace/", None)
@@ -294,92 +280,48 @@ init python:
build.classify_renpy("renios/version.txt", "renios")
build.classify_renpy("renios/", "renios")
build.classify_renpy("web/game.zip", None)
build.classify_renpy("web/**", "web")
build.classify_renpy("**.old", None)
build.classify_renpy("**.new", None)
build.classify_renpy("**.bak", None)
build.classify_renpy("**.keystore", None)
build.classify_renpy("**.pyc", None)
build.classify_renpy("**/log.txt", None)
build.classify_renpy("**/traceback.txt", None)
build.classify_renpy("**/errors.txt", None)
build.classify_renpy("**/steam_appid.txt", None)
build.classify_renpy("**/saves/", None)
build.classify_renpy("**/tmp/", None)
build.classify_renpy("**/.Editra", None)
# main source.
def source_and_binary(pattern, source="source", binary="binary", py=True):
def source_and_binary(pattern, source="source", binary="binary"):
"""
Classifies source and binary files beginning with `pattern`.
.pyo, .rpyc, .rpycm, and .rpyb go into binary, everything
else but .pyi files go into source.
else goes into source.
"""
if py is True:
py = 'pycache'
if py == 'pycache':
build.classify_renpy(pattern + "/**__pycache__/", binary)
build.classify_renpy(pattern + "/**__pycache__/*.{}.pyc".format(sys.implementation.cache_tag), binary)
build.classify_renpy(pattern + "/**.pyc", None)
build.classify_renpy(pattern + "/**.pyo", None)
elif py == 'pyc':
build.classify_renpy(pattern + "/**__pycache__/", None)
build.classify_renpy(pattern + "/**.pyc", binary)
build.classify_renpy(pattern + "/**.pyo", None)
elif py == 'pyo':
build.classify_renpy(pattern + "/**__pycache__/", None)
build.classify_renpy(pattern + "/**.pyc", None)
build.classify_renpy(pattern + "/**.pyo", binary)
else:
build.classify_renpy(pattern + "/**__pycache__/", None)
build.classify_renpy(pattern + "/**.pyc", None)
build.classify_renpy(pattern + "/**.pyo", None)
build.classify_renpy(pattern + "/**.pyi", None)
build.classify_renpy(pattern + "/**.pyx", "source_only")
build.classify_renpy(pattern + "/**.pxd", "source_only")
build.classify_renpy(pattern + "/**.pyo", binary)
build.classify_renpy(pattern + "/**.rpyc", binary)
build.classify_renpy(pattern + "/**.rpymc", binary)
build.classify_renpy(pattern + "/**/" + renpy.script.BYTECODE_FILE, binary)
build.classify_renpy(pattern + "/**/cache/bytecode-*.rpyb", None)
build.classify_renpy(pattern + "/**/cache/*", binary)
build.classify_renpy(pattern + "/**", source)
build.classify_renpy("renpy/gl/", None)
build.classify_renpy("renpy.py", "binary")
source_and_binary("renpy")
# games.
build.classify_renpy("launcher/game/theme/", None)
build.classify_renpy("gui/game/gui/", None)
build.classify_renpy("gui/game/gui/", "source")
build.classify_renpy("gui/game/gui/bubble.png", "source")
build.classify_renpy("gui/game/gui/thoughtbubble.png", "source")
build.classify_renpy("gui/game/gui/*", None)
source_and_binary("launcher", py=False)
source_and_binary("gui", binary=None, py=False)
source_and_binary("launcher")
source_and_binary("templates", binary=None)
source_and_binary("gui", binary=None)
source_and_binary("the_question")
source_and_binary("tutorial")
# extra fonts.
build.classify_renpy("sdk-fonts/**", "source")
source_and_binary("oldtutorial")
# docs.
build.classify_renpy("doc/", "source")
@@ -388,75 +330,60 @@ init python:
build.classify_renpy("doc/**", "source")
build.classify_renpy("LICENSE.txt", "source")
build.classify_renpy("sphinx/", "source_only")
build.classify_renpy("sphinx/build.sh", "source_only")
build.classify_renpy("sphinx/checks.py", "source_only")
build.classify_renpy("sphinx/game/**", "source_only")
build.classify_renpy("sphinx/source/inc/", None)
build.classify_renpy("sphinx/source/**", "source_only")
# Build System.
build.classify_renpy("setup.py", "source_only")
build.classify_renpy("src/**", "source_only")
build.classify_renpy("scripts/", "source_only")
build.classify_renpy("scripts/setuplib.py", "source_only")
build.classify_renpy("scripts/generate_styles.py", "source_only")
# module.
build.classify_renpy("module/", "source")
build.classify_renpy("module/*.c", "source")
build.classify_renpy("module/gen/", "source")
build.classify_renpy("module/gen/*.c", "source")
build.classify_renpy("module/*.h", "source")
build.classify_renpy("module/*.py*", "source")
build.classify_renpy("module/include/", "source")
build.classify_renpy("module/include/*.pxd", "source")
build.classify_renpy("module/include/*.pxi", "source")
build.classify_renpy("module/pysdlsound/", "source")
build.classify_renpy("module/pysdlsound/*.py", "source")
build.classify_renpy("module/pysdlsound/*.pyx", "source")
# all-platforms binary.
build.classify_renpy("lib/**/libpython{}.{}.dll".format(sys.version_info.major, sys.version_info.minor), "binary")
build.classify_renpy("lib/**/libpython*.dll", None)
build.classify_renpy("lib/**/_renpysteam*", None)
build.classify_renpy("lib/**/*steam_api*", None)
build.classify_renpy("lib/*/renpy", None)
build.classify_renpy("lib/*/renpy.exe", None)
build.classify_renpy("lib/linux-armv7l/", "raspi")
build.classify_renpy("lib/linux-armv7l/**", "raspi")
build.classify_renpy("lib/**", "binary")
build.classify_renpy("renpy.sh", "binary")
# renpy.app is now built from scratch from distribute.rpy.
build.classify_renpy("lib/**/*steam_api*", "steam")
build.classify_renpy("lib/**/*Live2D*", None)
# jedit rules.
build.classify_renpy("jedit/**", "jedit")
build.classify_renpy("lib/py3-linux-armv7l", None)
build.classify_renpy("lib/py3-linux-aarch64/**", "linux_arm")
source_and_binary("lib/py3-**", "binary", "binary")
# editra rules.
build.classify_renpy("editra/", "editra-all")
build.classify_renpy("editra/Editra.edit.py", "editra-all")
build.classify_renpy("editra/Editra/**", "editra-linux editra-windows")
build.classify_renpy("editra/Editra-mac.app/**", "editra-mac")
build.classify_renpy("editra/lib/**", "editra-windows")
build.classify_renpy("editra/editra.exe", "editra-windows")
libpython = "lib/python{}.{}".format(sys.version_info.major, sys.version_info.minor)
source_and_binary(libpython, "binary", "binary", py='pyc')
build.classify_renpy("lib/python**", None)
build.classify_renpy("lib/py2-**", None)
build.classify_renpy("renpy3.sh", "binary")
build.classify_renpy("lib/", "binary")
# Executable rules.
build.executable("editra/Editra/Editra")
# Packages.
build.packages = [ ]
build.package("sdk", "zip tar.bz2 dmg", "source binary")
build.package("sdkarm", "tar.bz2", "source binary linux_arm")
build.package("source", "tar.bz2", "source source_only", update=False)
build.package("steam", "zip", "steam", dlc=True)
build.package("raspi", "tar.bz2", "raspi", dlc=True, update=False)
build.package("jedit", "zip", "jedit", dlc=True)
build.package("editra-linux", "tar.bz2", "editra-all editra-linux", dlc=True)
build.package("editra-mac", "zip", "editra-all editra-mac", dlc=True)
build.package("editra-windows", "zip", "editra-all editra-windows", dlc=True)
build.package("rapt", "zip", "rapt", dlc=True)
build.package("renios", "zip", "renios", dlc=True)
build.package("web", "zip", "web", dlc=True)
# The identifier for the SDK.
define build.mac_info_plist["CFBundleIdentifier"] = "org.renpy.sdk"
# Enable the special launcher translation mode.
define config.translate_launcher = True
# Allow clicks that focus the window to be processed.
define config.mouse_focus_clickthrough = True
# Reduce the rate of screen updates.
default preferences.gl_powersave = True
# Enable rtl.
define config.rtl = True
# Disable steam.
python early:
config.enable_steam = False
# Since the launcher can be run directly or can be run from the SDK directory,
# uneliding files needs to be handled slightly differently.
define config.alternate_unelide_path = os.path.join(config.basedir, "launcher")
# Disable skipping.
define config.allow_skipping = False
+89 -88
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -28,27 +28,78 @@ init python in distribute:
import struct
import stat
import shutil
import sys
import threading
from zipfile import crc32
# Since the long type doesn't exist on py3, define it here
long = int
zlib.Z_DEFAULT_COMPRESSION = 5
zlib.Z_DEFAULT_COMPRESSION = 9
class ZipFile(zipfile.ZipFile):
def write_with_info(self, zinfo, filename):
"""Put the bytes from filename into the archive under the name
arcname."""
if not self.fp:
raise RuntimeError(
"Attempt to write to ZIP archive that was already closed")
if zinfo.filename.endswith("/"):
data = b''
st = os.stat(filename)
isdir = stat.S_ISDIR(st.st_mode)
zinfo.file_size = st.st_size
zinfo.flag_bits = 0x00
zinfo.header_offset = self.fp.tell() # Start of header bytes
self._writecheck(zinfo)
self._didModify = True
if isdir:
zinfo.file_size = 0
zinfo.compress_size = 0
zinfo.CRC = 0
self.filelist.append(zinfo)
self.NameToInfo[zinfo.filename] = zinfo
self.fp.write(zinfo.FileHeader())
return
with open(filename, "rb") as fp:
# Must overwrite CRC and sizes with correct data later
zinfo.CRC = CRC = 0
zinfo.compress_size = compress_size = 0
zinfo.file_size = file_size = 0
self.fp.write(zinfo.FileHeader())
if zinfo.compress_type == zipfile.ZIP_DEFLATED:
cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
zlib.DEFLATED, -15)
else:
cmpr = None
while 1:
buf = fp.read(1024 * 1024)
if not buf:
break
file_size = file_size + len(buf)
CRC = crc32(buf, CRC) & 0xffffffff
if cmpr:
buf = cmpr.compress(buf)
compress_size = compress_size + len(buf)
self.fp.write(buf)
if cmpr:
buf = cmpr.flush()
compress_size = compress_size + len(buf)
self.fp.write(buf)
zinfo.compress_size = compress_size
else:
with open(filename, "rb") as f:
data = f.read()
self.writestr(zinfo, data)
zinfo.compress_size = file_size
zinfo.CRC = CRC
zinfo.file_size = file_size
# Seek backwards and write CRC and file sizes
position = self.fp.tell() # Preserve current position in file
self.fp.seek(zinfo.header_offset + 14, 0)
self.fp.write(struct.pack("<LLL", zinfo.CRC, zinfo.compress_size,
zinfo.file_size))
self.fp.seek(position, 0)
self.filelist.append(zinfo)
self.NameToInfo[zinfo.filename] = zinfo
class ZipPackage(object):
@@ -72,7 +123,7 @@ init python in distribute:
# Check that the time is sensible.
if rv[0] < 2000:
rv = None
except Exception:
except:
rv = None
if rv is None:
@@ -91,9 +142,9 @@ init python in distribute:
zi.create_system = 3
if xbit:
zi.external_attr = long(0o100755) << 16
zi.external_attr = long(0100755) << 16
else:
zi.external_attr = long(0o100644) << 16
zi.external_attr = long(0100644) << 16
self.zipfile.write_with_info(zi, path)
@@ -105,11 +156,11 @@ init python in distribute:
zi.date_time = self.get_date_time(path)
zi.compress_type = zipfile.ZIP_STORED
zi.create_system = 3
zi.external_attr = (long(0o040755) << 16) | 0x10
zi.external_attr = (long(0040755) << 16) | 0x10
self.zipfile.write_with_info(zi, path)
def close(self, progress=None):
def close(self):
self.zipfile.close()
@@ -121,7 +172,7 @@ init python in distribute:
If true, times will be forced to the epoch.
"""
self.tarfile = tarfile.open(filename, mode, format=tarfile.PAX_FORMAT)
self.tarfile = tarfile.open(filename, mode)
self.tarfile.dereference = True
self.notime = notime
@@ -136,9 +187,9 @@ init python in distribute:
info.type = tarfile.DIRTYPE
if xbit:
info.mode = 0o755
info.mode = 0755
else:
info.mode = 0o644
info.mode = 0644
info.uid = 1000
info.gid = 1000
@@ -157,10 +208,9 @@ init python in distribute:
def add_directory(self, name, path):
self.add_file(name, path, True)
def close(self, progress=None):
def close(self):
self.tarfile.close()
class UpdatePackage(TarPackage):
def __init__(self, filename, basename, destination):
@@ -170,7 +220,7 @@ init python in distribute:
TarPackage.__init__(self, filename, "w", notime=True)
def close(self, progress=None):
def close(self):
TarPackage.close(self)
cmd = [
@@ -198,18 +248,15 @@ init python in distribute:
sums.write(struct.pack("<I", zlib.adler32(data) & 0xffffffff))
class DirectoryPackage(object):
def mkdir(self, path):
if not os.path.isdir(path):
os.makedirs(path, 0o755)
os.mkdir(path, 0755)
def __init__(self, path):
self.path = path
if os.path.isdir(path):
shutil.rmtree(path)
self.mkdir(path)
def add_file(self, name, path, xbit):
@@ -217,22 +264,22 @@ init python in distribute:
# If this is not a directory, ensure all parent directories
# have been created
self.mkdir(os.path.dirname(fn))
shutil.copy(path, fn)
if not os.path.isdir(os.path.dirname(fn)):
os.makedirs(os.path.dirname(fn), 0755)
shutil.copy2(path, fn)
if xbit:
os.chmod(fn, 0o755)
os.chmod(fn, 0755)
else:
os.chmod(fn, 0o644)
os.chmod(fn, 0644)
def add_directory(self, name, path):
fn = os.path.join(self.path, name)
self.mkdir(fn)
def close(self, progress=None):
def close(self):
return
class ExternalZipPackage(object):
def __init__(self, path):
@@ -246,7 +293,7 @@ init python in distribute:
def add_directory(self, name, path):
self.dp.add_directory(name, path)
def close(self, progress=None):
def close(self):
self.dp.close()
if os.path.exists(self.path):
@@ -265,65 +312,17 @@ init python in distribute:
shutil.rmtree(self.directory)
class DMGPackage(DirectoryPackage):
def __init__(self, path, make_dmg):
self.make_dmg = make_dmg
DirectoryPackage.__init__(self, path)
def close(self, progress=None):
def close(self):
DirectoryPackage.close(self)
self.make_dmg()
class RPUPackage(object):
generator = None
def __init__(self, directory, variant):
import renpy.update.common
self.directory = directory + "/rpu"
self.variant = variant
self.file_list = renpy.update.common.FileList()
if not os.path.exists(self.directory):
os.mkdir(self.directory)
def add_file(self, name, path, xbit):
self.file_list.add_file(name, path, xbit)
def add_directory(self, name, _path):
self.file_list.add_directory(name)
def close(self, progress=None):
import renpy.update.generate
if RPUPackage.generator is None:
RPUPackage.generator = renpy.update.generate.BlockGenerator(self.directory)
RPUPackage.generator.generate(self.variant, self.file_list, progress)
@staticmethod
def reset():
RPUPackage.generator = None
class NullPackage(object):
"""
A package format that doesn't create an output file,
only updates.
"""
def add_file(self, name, path, xbit):
return
def add_directory(self, name, _path):
return
def close(self, progress=None):
return
parallel_threads = [ ]
class ParallelPackage(object):
@@ -341,7 +340,7 @@ init python in distribute:
def add_directory(self, name, path):
self.worklist.append((True, name, path, True))
def close(self, progress=None):
def close(self):
t = threading.Thread(target=self.run)
t.start()
@@ -377,7 +376,7 @@ init python in distribute:
break
t += 1
print("\r " + str(t) + " " + " ".join(sorted([ i.what for i in alive ])))
print(t, " ".join(sorted([ i.what for i in alive ])))
time.sleep(1)
@@ -385,4 +384,6 @@ init python in distribute:
if i.done:
i.done()
parallel_threads.clear()
File diff suppressed because it is too large Load Diff
+147 -367
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -19,91 +19,46 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
default persistent.show_edit_funcs = True
default persistent.windows_console = False
default persistent.lint_options = set()
default persistent.use_web_doc = False
init python:
from math import ceil
if persistent.gl_enable is None:
persistent.gl_enable = True
def scan_translations(piglatin=True):
config.gl_enable = persistent.gl_enable
if persistent.show_edit_funcs is None:
persistent.show_edit_funcs = True
if persistent.windows_console is None:
persistent.windows_console = False
def scan_translations():
languages = renpy.known_languages()
if not languages:
return None
languages.remove("piglatin")
rv = [ ( "English", None) ]
rv = [(i, renpy.translate_string("{#language name and font}", i)) for i in languages ]
rv.sort(key=lambda a : renpy.filter_text_tags(a[1], allow=[]).lower())
for i in languages:
rv.append((i.replace("_", " ").title(), i))
rv.insert(0, (None, "English"))
rv.sort()
if piglatin:
rv.append(("piglatin", "Igpay Atinlay"))
if ("Piglatin", "piglatin") in rv:
rv.remove(("Piglatin", "piglatin"))
rv.append(("Pig Latin", "piglatin"))
bound = ceil(len(rv)/3.)
return (rv[:bound], rv[bound:2*bound], rv[2*bound:])
class RestartAtPreferences(Action):
def __call__(self):
renpy.session["launcher_start_label"] = "preferences"
renpy.utter_restart()
class EnsureProjectsTxt(Action):
"""
Ensures the projects.txt file exists before it's opened.
"""
def __call__(self):
fn = os.path.join(project.manager.projects_directory, "projects.txt")
if os.path.exists(fn):
return
with open(fn, "w") as f:
f.write("""\
# This file can be used to add projects not in the projects directory
# by listing the full path to each project, one per line.
""")
return rv
default persistent.legacy = False
default persistent.force_new_tutorial = False
default persistent.sponsor_message = True
default persistent.daily_update_check = True
default persistent.daily_update_check_once = False
# Keep the default update check from triggering until tomorrow.
default persistent.last_update_check = datetime.date.today()
screen preferences:
# Should we try to skip the splashscreen?
default persistent.skip_splashscreen = False
# Should we prefer rpu updates?
default persistent.prefer_rpu = True
init python:
if not persistent.daily_update_check_once:
persistent.daily_update_check_once = True
persistent.daily_update_check = True
default preference_tab = "general"
define preference_tabs = (
("general", _("General")),
("options", _("Options")),
("theme", _("Theme")),
("install", _("Install Libraries")),
("actions", _("Actions")),
("lint", _("Lint")),
)
screen preferences():
default translations = scan_translations()
$ translations = scan_translations()
frame:
style_group "l"
@@ -127,44 +82,138 @@ screen preferences():
has vbox
# Projects directory selection.
add SEPARATOR2
add HALF_SPACER
for i, l in preference_tabs:
textbutton l action SetVariable("preference_tab", i) style "l_list"
if preference_tab == "general":
frame:
style "l_indent"
xmaximum TWOTHIRDS
xfill True
yminimum 75
has vbox
add SEPARATOR2
text _("Projects Directory:")
add HALF_SPACER
frame style "l_indent":
if persistent.projects_directory:
textbutton _("[persistent.projects_directory!q]"):
action Jump("projects_directory_preference")
alt _("Projects directory: [text]")
else:
textbutton _("Not Set"):
action Jump("projects_directory_preference")
alt _("Projects directory: [text]")
add SPACER
# Text editor selection.
add SEPARATOR2
frame:
style "l_indent"
yminimum 75
has vbox
text _("Text Editor:")
add HALF_SPACER
frame style "l_indent":
if persistent.editor:
textbutton persistent.editor action Jump("editor_preference") alt _("Text editor: [text]")
else:
textbutton _("Not Set") action Jump("editor_preference") alt _("Text editor: [text]")
add SPACER
if ability.can_update:
# Update URL selection.
add SEPARATOR2
frame:
style "l_indent"
yminimum 75
has vbox
text _("Projects Directory:")
text _("Update Channel:")
add HALF_SPACER
frame style "l_indent":
if persistent.projects_directory:
textbutton _("[persistent.projects_directory!q]"):
action Jump("projects_directory_preference")
alt _("Projects directory: [text]")
else:
textbutton _("Not Set"):
action Jump("projects_directory_preference")
alt _("Projects directory: [text]")
textbutton persistent.update_channel action Jump("update_preference")
frame:
style "l_indent"
xmaximum ONETHIRD
xfill True
has vbox
add SEPARATOR2
frame:
style "l_indent"
yminimum 75
has vbox
text _("Navigation Options:")
add HALF_SPACER
textbutton _("Include private names") style "l_checkbox" action ToggleField(persistent, "navigate_private")
textbutton _("Include library names") style "l_checkbox" action ToggleField(persistent, "navigate_library")
add SPACER
add SEPARATOR2
frame:
style "l_indent"
yminimum 75
has vbox
text _("Launcher Options:")
add HALF_SPACER
textbutton _("Hardware rendering") style "l_checkbox" action ToggleField(persistent, "gl_enable")
textbutton _("Show edit file section") style "l_checkbox" action ToggleField(persistent, "show_edit_funcs")
textbutton _("Large fonts") style "l_checkbox" action [ ToggleField(persistent, "large_print"), renpy.utter_restart ]
if renpy.windows:
textbutton _("Console output") style "l_checkbox" action ToggleField(persistent, "windows_console")
textbutton _("Force new tutorial") style "l_checkbox" action [ ToggleField(persistent, "force_new_tutorial"), project.SelectTutorial(True) ]
textbutton _("Legacy options") style "l_checkbox" action ToggleField(persistent, "legacy")
if persistent.legacy:
textbutton _("Show templates") style "l_checkbox" action ToggleField(persistent, "show_templates")
textbutton _("Sponsor message") style "l_checkbox" action ToggleField(persistent, "sponsor_message")
frame:
style "l_indent"
xmaximum ONETHIRD
xfill True
has vbox
add SEPARATOR2
frame:
style "l_indent"
yminimum 75
has vbox
text _("Actions:")
add HALF_SPACER
textbutton _("Open launcher project") style "l_nonbox" action [ project.Select("launcher"), Jump("front_page") ]
if translations:
add SPACER
@@ -173,225 +222,27 @@ screen preferences():
frame:
style "l_indent"
has vbox
text _("Text Editor:")
add HALF_SPACER
frame style "l_indent":
textbutton (persistent.editor or _("Not Set")) action Jump("editor_preference") alt _("Text editor: [text]")
add SPACER
add SEPARATOR2
frame:
style "l_indent"
yminimum 75
has vbox
text _("Language:")
add HALF_SPACER
hbox:
for tran in translations:
vbox:
for tlid, tlname in tran:
textbutton tlname:
xmaximum (TWOTHIRDS//3)
action [Language(tlid), project.SelectTutorial(True)]
style "l_list"
viewport:
scrollbars "vertical"
mousewheel True
elif preference_tab == "options":
has vbox
frame:
style "l_indent"
xmaximum TWOTHIRDS
xfill True
# frame style "l_indent":
has vbox
add SEPARATOR2
frame:
style "l_indent"
has vbox
text _("Navigation Options:")
add HALF_SPACER
textbutton _("Include private names") style "l_checkbox" action ToggleField(persistent, "navigate_private")
textbutton _("Include library names") style "l_checkbox" action ToggleField(persistent, "navigate_library")
add SPACER
add SEPARATOR2
frame:
style "l_indent"
has vbox
text _("Game Options:")
add HALF_SPACER
if renpy.windows:
textbutton _("Console output") style "l_checkbox" action ToggleField(persistent, "windows_console")
textbutton _("Skip splashscreen") style "l_checkbox" action ToggleField(persistent, "skip_splashscreen")
add SPACER
add SEPARATOR2
frame:
style "l_indent"
has vbox
text _("Launcher Options:")
add HALF_SPACER
textbutton _("Show edit file section") style "l_checkbox" action ToggleField(persistent, "show_edit_funcs")
textbutton _("Large fonts") style "l_checkbox" action [ ToggleField(persistent, "large_print"), renpy.utter_restart ]
if interface.local_doc_exists:
textbutton _("Prefer the web documentation") style "l_checkbox" action ToggleField(persistent, "use_web_doc")
textbutton _("Sponsor message") style "l_checkbox" action ToggleField(persistent, "sponsor_message")
textbutton _("Restore window position") style "l_checkbox" action Preference("restore window position", "toggle")
if ability.can_update:
textbutton _("Daily check for update") style "l_checkbox" action [ToggleField(persistent, "daily_update_check"), SetField(persistent, "last_update_check", None)] selected persistent.daily_update_check
textbutton _("Prefer RPU updates") style "l_checkbox" action ToggleField(persistent, "prefer_rpu")
elif preference_tab == "theme":
frame:
style "l_indent"
xmaximum TWOTHIRDS
xfill True
has vbox
add SEPARATOR2
frame:
style "l_indent"
has vbox
text _("Launcher Theme:")
add HALF_SPACER
textbutton _("Default theme") style "l_checkbox" action [SetField(persistent, "theme", None), RestartAtPreferences() ]
textbutton _("Dark theme") style "l_checkbox" action [SetField(persistent, "theme", "dark"), RestartAtPreferences()]
textbutton _("Custom theme") style "l_checkbox" action [SetField(persistent, "theme", "custom"), RestartAtPreferences()]
add SPACER
$ skins_url = interface.get_doc_url("skins.html")
text _("Information about creating a custom theme can be found {a=[skins_url]}in the Ren'Py Documentation{/a}.")
elif preference_tab == "install":
frame:
style "l_indent"
xmaximum TWOTHIRDS
xfill True
has vbox
add SEPARATOR2
frame:
style "l_indent"
has vbox
text _("Install Libraries:")
add HALF_SPACER
use install_preferences
elif preference_tab == "actions":
frame:
style "l_indent"
xmaximum TWOTHIRDS
xfill True
has vbox
add SEPARATOR2
frame:
style "l_indent"
has vbox
text _("Actions:")
add HALF_SPACER
textbutton _("Open launcher project") style "l_nonbox" action [ project.Select("launcher"), Jump("front_page") ]
textbutton _("Open projects.txt"):
style "l_nonbox"
if project.manager.projects_directory:
action [
EnsureProjectsTxt(),
editor.EditAbsolute(os.path.join(project.manager.projects_directory, "projects.txt"))
]
textbutton _("Reset window size") style "l_nonbox" action Preference("display", 1.0)
textbutton _("Clean temporary files") style "l_nonbox" action Jump("clean_tmp")
elif preference_tab == "lint":
frame:
style "l_indent"
xmaximum TWOTHIRDS
xfill True
has vbox
add SEPARATOR2
frame:
style "l_indent"
has vbox
text _("Lint toggles:")
add HALF_SPACER
textbutton _("Check for orphan/obsolete translations"):
style "l_checkbox"
action InvertSelected(ToggleSetMembership(persistent.lint_options, "--no-orphan-tl"))
textbutton _("Check parameters shadowing reserved names"):
style "l_checkbox"
action ToggleSetMembership(persistent.lint_options, "--reserved-parameters")
textbutton _("Print block, word, and character counts by speaking character."):
style "l_checkbox"
action ToggleSetMembership(persistent.lint_options, "--by-character")
textbutton _("Unclosed text tags"):
style "l_checkbox"
action ToggleSetMembership(persistent.lint_options, "--check-unclosed-tags")
textbutton _("Show all unreachable blocks and orphaned translations."):
style "l_checkbox"
action ToggleSetMembership(persistent.lint_options, "--all-problems")
for tlname, tlvalue in translations:
textbutton tlname action [ Language(tlvalue), project.SelectTutorial(True) ] style "l_list"
textbutton _("Return") action Jump("front_page") style "l_left_button"
label clean_tmp:
python hide:
installer.processing(_("Cleaning temporary files..."))
installer._clean("renpy:tmp", 0)
time.sleep(0.5)
jump preferences
label projects_directory_preference:
call choose_projects_directory
jump preferences
@@ -400,74 +251,3 @@ label projects_directory_preference:
label preferences:
call screen preferences
jump preferences
screen choose_language():
default local_lang = _preferences.language
default chosen_lang = _preferences.language
default translations = scan_translations(piglatin=False)
add BACKGROUND
vbox:
xalign .5
yalign .5
fixed:
ysize 0
text renpy.translate_string(_("{#in language font}Welcome! Please choose a language"), local_lang):
xalign .5
yanchor 1.0
ypos 1.0
style "l_label_text"
size 36
textalign .5
layout "subtitle"
add SPACER
add SPACER
hbox:
xalign .5
for tran in translations:
vbox:
for tlid, tlname in tran:
textbutton tlname:
xmaximum (TWOTHIRDS//3)
action [ SetScreenVariable("chosen_lang", tlid), Language(tlid), project.SelectTutorial(True), Return() ]
hovered SetScreenVariable("local_lang", tlid)
unhovered SetScreenVariable("local_lang", chosen_lang)
style "l_list"
text_xalign .5
add SPACER
add SPACER
$ lang_name = renpy.translate_string("{#language name and font}", local_lang)
fixed:
ysize 0
textbutton renpy.translate_string(_("{#in language font}Start using Ren'Py in [lang_name]"), local_lang):
xalign .5
action [Language(chosen_lang), project.SelectTutorial(True), Return()]
style "l_default"
text_style "l_default"
text_size 30
text_textalign .5
text_layout "subtitle"
label choose_language:
call screen choose_language
return
translate None strings:
# game/new_project.rpy:77
old "{#language name and font}"
new "English"
+52 -180
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -22,10 +22,15 @@
# Code that manages projects.
init python:
if renpy.windows:
import EasyDialogsWin as EasyDialogs
else:
EasyDialogs = None
import os
init python in project:
from store import persistent, config, Action, renpy, _preferences, MultiPersistent
from store import persistent, config, Action, renpy, _preferences
import store.util as util
import store.interface as interface
@@ -36,8 +41,6 @@ init python in project:
import re
import tempfile
multipersistent = MultiPersistent("launcher.renpy.org")
if persistent.blurb is None:
persistent.blurb = 0
@@ -48,19 +51,18 @@ init python in project:
_("Press shift+O (the letter) to access the console."),
_("Press shift+D to access the developer menu."),
_("Have you backed up your projects recently?"),
_("Lint checks your game for potential mistakes, and gives you statistics."),
]
class Project(object):
def __init__(self, path, name=None):
while path.endswith("/"):
path = path[:-1]
if name is None:
name = os.path.basename(path)
while path.endswith("/"):
path = path[:-1]
if not os.path.exists(path):
raise Exception("{} does not exist.".format(path))
@@ -92,36 +94,30 @@ init python in project:
# The mtime of the last dump file loaded.
self.dump_mtime = 0
# A processed version of data['renpy_launcher'] with missing files
# and directories removed.
self.renpy_launcher = None
def get_dump_filename(self):
if os.path.exists(os.path.join(self.gamedir, "saves")):
return os.path.join(self.gamedir, "saves", "navigation.json")
self.make_tmp()
return os.path.join(self.tmp, "navigation.json")
def load_data(self):
try:
with open(os.path.join(self.path, "project.json"), "r") as f:
self.data = json.load(f)
except Exception:
f = open(os.path.join(self.path, "project.json"), "rb")
self.data = json.load(f)
f.close()
except:
self.data = { }
self.update_data()
def save_data(self):
"""
Saves the project data.
"""
try:
with open(os.path.join(self.path, "project.json"), "w") as f:
json.dump(self.data, f, indent=2)
except Exception:
with open(os.path.join(self.path, "project.json"), "wb") as f:
json.dump(self.data, f)
except:
self.load_data()
def update_data(self):
@@ -131,8 +127,6 @@ init python in project:
data.setdefault("packages", [ "pc", "mac" ])
data.setdefault("add_from", True)
data.setdefault("force_recompile", True)
data.setdefault("android_build", "Release")
data.setdefault("tutorial", False)
if "renamed_all" not in data:
dp = data["packages"]
@@ -160,43 +154,6 @@ init python in project:
data["renamed_steam"] = True
def get_renpy_launcher(self):
if self.renpy_launcher is not None:
return self.renpy_launcher
rv = { }
default_values = {
"open_directory":
{
"game": "game",
"base": ".",
"images": "game/images",
"audio": "game/audio",
"gui": "game/gui",
"libs": "game/libs",
"mods": "game/mods",
},
"edit_file":
{
"script.rpy": "game/script.rpy",
"options.rpy": "game/options.rpy",
"gui.rpy": "game/gui.rpy",
"screens.rpy": "game/screens.rpy"
}
}
for k, default_d in default_values.items():
d = self.data.get("renpy_launcher", {}).get(k, default_d)
rv[k] = { name : path for name, path in d.items() if os.path.exists(os.path.join(self.path, path)) }
self.renpy_launcher = rv
return rv
def make_tmp(self):
"""
Makes the project's temporary directory, if it doesn't exist
@@ -210,7 +167,7 @@ init python in project:
try:
os.makedirs(tmp)
except Exception:
except:
pass
if os.path.isdir(tmp):
@@ -229,7 +186,7 @@ init python in project:
self.tmp = tmp
return
except Exception:
except:
pass
self.tmp = tempfile.mkdtemp()
@@ -282,7 +239,7 @@ init python in project:
raise Exception("Python interpreter not found: %r", executables)
# Put together the basic command line.
cmd = [ executable, sys.argv[0] ]
cmd = [ executable, "-EO", sys.argv[0] ]
cmd.append(self.path)
cmd.extend(args)
@@ -297,20 +254,10 @@ init python in project:
if persistent.navigate_library:
cmd.append("--json-dump-common")
cmd.append("--errors-in-editor")
environ = dict(os.environ)
environ["RENPY_LAUNCHER_LANGUAGE"] = _preferences.language or "english"
if persistent.skip_splashscreen:
environ["RENPY_SKIP_SPLASHSCREEN"] = "1"
environ.update(env)
# Filter out system PYTHON* environment variables.
if hasattr(sys, "renpy_executable"):
environ = { k : v for k, v in environ.items() if not k.startswith("PYTHON") }
encoded_environ = { }
for k, v in environ.items():
@@ -326,13 +273,7 @@ init python in project:
if wait:
if p.wait():
print("Launch failed. command={!r}, returncode={!r}".format(cmd, p.returncode))
if args and not self.is_writeable():
interface.error(_("Launching the project failed."), _("This may be because the project is not writeable."))
else:
interface.error(_("Launching the project failed."), _("Please ensure that your project launches normally before running this command."))
interface.error(_("Launching the project failed."), _("Please ensure that your project launches normally before running this command."))
renpy.not_infinite_loop(30)
@@ -371,7 +312,7 @@ init python in project:
# add todo list to dump data
self.update_todos()
except Exception:
except:
self.dump["error"] = True
def update_todos(self):
@@ -386,14 +327,17 @@ init python in project:
for f in files:
data = open(self.unelide_filename(f), encoding="utf-8")
data = file(self.unelide_filename(f))
for l, line in enumerate(data):
l += 1
line = line[:1024]
try:
line = line.decode("utf-8")
except:
continue
m = re.search(r"#\s*TODO(\s*:\s*|\s+)(.*)", line, re.I)
m = re.search(ur".*#\s*TODO(\s*:\s*|\s+)(.*)", line, re.I)
if m is None:
continue
@@ -415,8 +359,6 @@ init python in project:
Unelides the filename relative to the project base.
"""
fn = os.path.normpath(fn)
fn1 = os.path.join(self.path, fn)
if os.path.exists(fn1):
return fn1
@@ -434,18 +376,9 @@ init python in project:
can be included in the project.
"""
def is_script(fn):
fn = fn.lower()
for i in [ ".rpy", ".rpym", "_ren.py" ]:
if fn.endswith(i):
return True
return False
rv = [ ]
rv.extend(i for i, isdir in util.walk(self.path)
if (not isdir) and is_script(i) and (not i.startswith("tmp/")) )
if (not isdir) and (i.endswith(".rpy") or i.endswith(".rpym")) and (not i.startswith("tmp/")) )
return rv
@@ -456,14 +389,6 @@ init python in project:
return os.path.exists(os.path.join(self.path, fn))
def is_writeable(self):
"""
Returns true if it's possible to write a file in the projects
directory.
"""
return os.access(self.path, os.W_OK)
class ProjectManager(object):
"""
@@ -473,26 +398,26 @@ init python in project:
def __init__(self):
# The projects directory.
self.projects_directory = ""
# The projects directory.
self.projects_directory = ""
# Normal projects, in alphabetical order by lowercase name.
self.projects = [ ]
# Normal projects, in alphabetical order by lowercase name.
self.projects = [ ]
# Template projects.
self.templates = [ ]
# Template projects.
self.templates = [ ]
# All projects - normal, template, and hidden.
self.all_projects = [ ]
# All projects - normal, template, and hidden.
self.all_projects = [ ]
# Directories that have been scanned.
self.scanned = set()
# Directories that have been scanned.
self.scanned = set()
# The tutorial game, and the language it's for.
self.tutoral = None
self.tutorial_language = "the meowing of a cat"
# The tutorial game, and the language it's for.
self.tutoral = None
self.tutorial_language = "the meowing of a cat"
self.scan()
self.scan()
def scan(self):
"""
@@ -501,18 +426,9 @@ init python in project:
global current
if persistent.projects_directory is None:
if multipersistent.projects_directory is not None:
persistent.projects_directory = multipersistent.projects_directory
if (persistent.projects_directory is not None) and not os.path.isdir(persistent.projects_directory):
persistent.projects_directory = None
if persistent.projects_directory is not None:
if multipersistent.projects_directory is None:
multipersistent.projects_directory = persistent.projects_directory
multipersistent.save()
self.projects_directory = persistent.projects_directory
self.projects = [ ]
@@ -523,11 +439,14 @@ init python in project:
if self.projects_directory is not None:
self.scan_directory(self.projects_directory)
self.scan_directory(config.renpy_base)
self.scan_directory(os.path.join(config.renpy_base, "templates"))
self.projects.sort(key=lambda p : p.name.lower())
self.templates.sort(key=lambda p : p.name.lower())
# Select the default project.
if persistent.active_project is not None:
p = self.get(persistent.active_project)
@@ -591,10 +510,6 @@ init python in project:
for path in f:
path = path.strip()
if path.startswith("#"):
continue
if len(path) > 0:
self.scan_directory_direct(path)
@@ -611,7 +526,7 @@ init python in project:
try:
ppath = self.find_basedir(ppath)
except Exception:
except:
return
if ppath is None:
@@ -633,7 +548,6 @@ init python in project:
if project_type == "hidden":
pass
elif project_type == "template":
self.projects.append(p)
self.templates.append(p)
else:
self.projects.append(p)
@@ -671,9 +585,6 @@ init python in project:
if language is None:
rv = p
elif rv is None:
rv = p
elif os.path.exists(os.path.join(p.path, "game", "tl", _preferences.language)):
rv = p
@@ -707,7 +618,7 @@ init python in project:
The label to jump to when clicked.
"""
if isinstance(p, str):
if isinstance(p, basestring):
p = manager.get(p)
self.project = p
@@ -795,7 +706,7 @@ init python in project:
def __init__(self, p=None):
if p is None:
self.project = current
elif isinstance(p, str):
elif isinstance(p, basestring):
self.project = manager.get(p)
else:
self.project = p
@@ -807,12 +718,7 @@ init python in project:
blurb = LAUNCH_BLURBS[persistent.blurb % len(LAUNCH_BLURBS)]
persistent.blurb += 1
if persistent.skip_splashscreen:
submessage = _("Splashscreen skipped in launcher preferences.")
else:
submessage = None
interface.interaction(_("Launching"), blurb, submessage=submessage, pause=2.5)
interface.interaction(_("Launching"), blurb, pause=2.5)
def __call__(self):
@@ -839,14 +745,6 @@ init 10 python:
if not directory_is_writable(persistent.projects_directory):
persistent.projects_directory = None
label after_load:
python:
if project.current is not None:
project.current.update_dump()
return
###############################################################################
# Code to choose the projects directory.
@@ -862,8 +760,6 @@ label choose_projects_directory:
interface.info(_("Ren'Py has set the projects directory to:"), "[path!q]", path=path)
persistent.projects_directory = path
project.multipersistent.projects_directory = path
project.multipersistent.save()
project.manager.scan()
@@ -878,8 +774,6 @@ init python:
args = ap.parse_args()
persistent.projects_directory = renpy.fsdecode(args.projects)
project.multipersistent.projects_directory = persistent.projects_directory
project.multipersistent.save()
renpy.save_persistent()
return False
@@ -890,30 +784,8 @@ init python:
ap = renpy.arguments.ArgumentParser()
args = ap.parse_args()
if persistent.projects_directory is not None:
print(persistent.projects_directory)
print renpy.fsencode(persistent.projects_directory)
return False
renpy.arguments.register_command("get_projects_directory", get_projects_directory_command)
def set_project_command():
ap = renpy.arguments.ArgumentParser()
ap.add_argument("project", help="The full path to the project to select.")
args = ap.parse_args()
projects = os.path.dirname(os.path.abspath(args.project))
name = os.path.basename(args.project)
persistent.projects_directory = renpy.fsdecode(projects)
project.multipersistent.projects_directory = persistent.projects_directory
persistent.active_project = name
project.multipersistent.save()
renpy.save_persistent()
return False
renpy.arguments.register_command("set_project", set_project_command)
-4
View File
@@ -1,4 +0,0 @@
-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEmkuXOrZ2viRe4fRCrlqUFGdgRqy9IcWt
5lJvsUqRN619pWZIefz/bau9Sd8vcJt6VKwexOeBmMhzXwZ/1Lm3OQ==
-----END PUBLIC KEY-----
+17 -200
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -19,50 +19,6 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# Defaults for custom theme properties.
init -99 python:
# The color of non-interactive text.
custom_text = "#545454"
# Colors for buttons in various states.
custom_idle = "#42637b"
custom_hover = "#d86b45"
custom_disable = "#808080"
# Colors for reversed text buttons (selected list entries).
custom_reverse_idle = "#78a5c5"
reverse_hover = "#d86b45"
custom_reverse_text = "#ffffff"
# Colors for the scrollbar thumb.
custom_scrollbar_idle = "#dfdfdf"
custom_scrollbar_hover = "#d86b45"
# An image used as a separator pattern.
custom_pattern = "images/pattern.png"
# A displayable used for the background of everything.
custom_background = "images/background.png"
# A displayable used for the background of the projects list.
custom_projects_window = Null()
# A displayable used the background of information boxes.
custom_info_window = "#f9f9f9c0"
# Colors for the titles of information boxes.
custom_error_color = "#d15353"
custom_info_color = "#545454"
custom_interaction_color = "#d19753"
custom_question_color = "#d19753"
# The color of input text.
custom_input_color = "#d86b45"
# A displayable used for the background of windows
# containing commands, preferences, and navigation info.
custom_window = Frame(Fixed(Solid(custom_reverse_idle, xsize=4, xalign=0), Solid(custom_info_window, xsize=794, xalign=1.0), xsize=800, ysize=600), 0, 0, tile=True)
init -1:
# Fonts.
@@ -100,11 +56,11 @@ init -1 python:
PATTERN = "images/pattern.png"
# A displayable used for the background of everything.
BACKGROUND = "background"
BACKGROUND = "images/background.png"
# A displayable used for the background of windows
# containing commands, preferences, and navigation info.
WINDOW = Frame("window", 0, 0, tile=True)
WINDOW = Frame("images/window.png", 0, 0, tile=True)
# A displayable used for the background of the projects list.
PROJECTS_WINDOW = Null()
@@ -121,146 +77,9 @@ init -1 python:
# The color of input text.
INPUT_COLOR = "#d86b45"
if persistent.theme == 'dark':
# The color of non-interactive text.
TEXT = "#ababab"
# Colors for buttons in various states.
IDLE = "#709cbe"
HOVER = "#d86b45"
DISABLED = "#7f7f7f"
# Colors for reversed text buttons (selected list entries).
REVERSE_IDLE = "#78a5c5"
REVERSE_HOVER = "#d86b45"
REVERSE_TEXT = "#0a0a0a"
# Colors for the scrollbar thumb.
SCROLLBAR_IDLE = "#282828"
SCROLLBAR_HOVER = "#d86b45"
# An image used as a separator pattern.
PATTERN = "images/pattern.png"
# A displayable used for the background of everything.
BACKGROUND = Fixed(Solid(REVERSE_TEXT), xsize=800, ysize=600)
# A displayable used for the background of the projects list.
PROJECTS_WINDOW = Null()
# A displayable used the background of information boxes.
INFO_WINDOW = "#101010"
# Colors for the titles of information boxes.
ERROR_COLOR = "#d15353"
INFO_COLOR = "#ababab"
INTERACTION_COLOR = "#d19753"
QUESTION_COLOR = "#d19753"
# The color of input text.
INPUT_COLOR = "#d86b45"
# A displayable used for the background of windows
# containing commands, preferences, and navigation info.
WINDOW = Frame(Fixed(Solid(REVERSE_IDLE, xsize=4, xalign=0), Solid(INFO_WINDOW, xsize=794, xalign=1.0), xsize=800, ysize=600), 0, 0, tile=True)
elif persistent.theme == 'clear':
# The color of non-interactive text.
TEXT = "#545454"
# Colors for buttons in various states.
IDLE = "#42637b"
HOVER = "#d86b45"
DISABLED = "#808080"
# Colors for reversed text buttons (selected list entries).
REVERSE_IDLE = "#78a5c5"
REVERSE_HOVER = "#d86b45"
REVERSE_TEXT = "#ffffff"
# Colors for the scrollbar thumb.
SCROLLBAR_IDLE = "#dfdfdf"
SCROLLBAR_HOVER = "#d86b45"
# An image used as a separator pattern.
PATTERN = "images/pattern.png"
# A displayable used for the background of everything.
BACKGROUND = Fixed(Solid(REVERSE_TEXT), xsize=800, ysize=600)
# A displayable used for the background of the projects list.
PROJECTS_WINDOW = Null()
# A displayable used the background of information boxes.
INFO_WINDOW = "#f9f9f9"
# Colors for the titles of information boxes.
ERROR_COLOR = "#d15353"
INFO_COLOR = "#545454"
INTERACTION_COLOR = "#d19753"
QUESTION_COLOR = "#d19753"
# The color of input text.
INPUT_COLOR = "#d86b45"
# A displayable used for the background of windows
# containing commands, preferences, and navigation info.
WINDOW = Frame(Fixed(Solid(REVERSE_IDLE, xsize=4, xalign=0), Solid(INFO_WINDOW, xsize=794, xalign=1.0), xsize=800, ysize=600), 0, 0, tile=True)
elif persistent.theme == 'custom':
# The color of non-interactive text.
TEXT = custom_text
# Colors for buttons in various states.
IDLE = custom_idle
HOVER = custom_hover
DISABLED = custom_disable
# Colors for reversed text buttons (selected list entries).
REVERSE_IDLE = custom_reverse_idle
REVERSE_HOVER = reverse_hover
REVERSE_TEXT = custom_reverse_text
# Colors for the scrollbar thumb.
SCROLLBAR_IDLE = custom_scrollbar_idle
SCROLLBAR_HOVER = custom_scrollbar_hover
# An image used as a separator pattern.
PATTERN = custom_pattern
# A displayable used for the background of everything.
BACKGROUND = custom_background
# A displayable used for the background of the projects list.
PROJECTS_WINDOW = custom_projects_window
# A displayable used the background of information boxes.
INFO_WINDOW = custom_info_window
# Colors for the titles of information boxes.
ERROR_COLOR = custom_error_color
INFO_COLOR = custom_info_color
INTERACTION_COLOR = custom_interaction_color
QUESTION_COLOR = custom_question_color
# The color of input text.
INPUT_COLOR = custom_input_color
# A displayable used for the background of windows
# containing commands, preferences, and navigation info.
WINDOW = custom_window
init 1 python:
if not renpy.has_image('scrollbar_center'):
renpy.image('scrollbar_center', Fixed(Solid(REVERSE_TEXT, area=(2, 2, 2, 4)), Solid(REVERSE_TEXT, area=(8, 2, 2, 4)), Solid(REVERSE_TEXT, area=(14, 2, 2, 4)), xysize=(18, 8)))
if not renpy.has_image('vscrollbar_center'):
renpy.image('vscrollbar_center', Fixed(Solid(REVERSE_TEXT, area=(2, 2, 4, 2)), Solid(REVERSE_TEXT, area=(2, 8, 4, 2)), Solid(REVERSE_TEXT, area=(2, 14, 4, 2)), xysize=(8, 18)))
def size(n):
"""
Adjusts the font size if we're in large-print mode.
@@ -290,10 +109,10 @@ init 1 python:
SEPARATOR = Frame(PATTERN, 0, 0, tile=True, ymaximum=5, yalign=1.0)
SEPARATOR2 = Frame(PATTERN, 0, 0, tile=True, ymaximum=10, yalign=1.0)
SPACER_HEIGHT = 8
SPACER_HEIGHT = 12
SPACER = Null(height=SPACER_HEIGHT)
HALF_SPACER_HEIGHT = 4
HALF_SPACER_HEIGHT = 6
HALF_SPACER = Null(height=HALF_SPACER_HEIGHT)
# DIVIDING THE SCREEN
@@ -302,13 +121,10 @@ init 1 python:
ONEHALF = 377
def checkbox(full, color):
if renpy.has_image('checkbox_full'):
check = im.Twocolor(''.join(["images/checkbox_", "full" if full else "empty", ".png"]), color, color)
if full:
return im.Twocolor("images/checkbox_full.png", color, color, style="l_checkbox_box")
else:
check = Fixed(Solid(color, xsize=1, xalign=.0), Solid(color, xsize=1, xalign=1.0), Solid(color, ysize=1, yalign=.0), Solid(color, ysize=1, yalign=1.0), xsize=10, ysize=10)
if full:
check = Fixed(check, Solid(color, xsize=6, ysize=6, align=(.5, .5)), xsize=10, ysize=10)
return At(check, l_checkbox_box)
return im.Twocolor("images/checkbox_empty.png", color, color, style="l_checkbox_box")
@@ -355,8 +171,8 @@ style l_left_button_text is l_right_button_text
style l_root is l_default:
background BACKGROUND
xpadding 10
top_padding 32
bottom_padding 120
top_padding 64
bottom_padding 128
# An inner window.
style l_window is l_default:
@@ -402,7 +218,7 @@ style l_alternate is l_default:
style l_alternate_text is l_default:
size size(14)
font light_font()
textalign 1.0
text_align 1.0
style l_small_button is l_button
@@ -443,11 +259,11 @@ style l_list2_text is l_list_text
style l_vscrollbar is l_default:
thumb Fixed(
Solid(SCROLLBAR_IDLE, xmaximum=8, xalign=0.5),
Transform('vscrollbar_center', xalign=0.5, yalign=0.5),
Image("images/vscrollbar_center.png", xalign=0.5, yalign=0.5),
xmaximum = SCROLLBAR_SIZE)
hover_thumb Fixed(
Solid(SCROLLBAR_HOVER, xmaximum=8, xalign=0.5),
Transform('vscrollbar_center', xalign=0.5, yalign=0.5),
Image("images/vscrollbar_center.png", xalign=0.5, yalign=0.5),
xmaximum = SCROLLBAR_SIZE)
xmaximum SCROLLBAR_SIZE
bar_vertical True
@@ -488,7 +304,7 @@ style l_info_button is l_button:
xmargin 50
style l_info_button_text is l_button_text:
textalign 0.5
text_align 0.5
layout "subtitle"
# Progress bar.
@@ -525,8 +341,9 @@ style l_checkbox is l_button:
selected_hover_background checkbox(True, HOVER)
insensitive_background checkbox(False, DISABLED)
transform l_checkbox_box:
ycenter 11
style l_checkbox_box:
yanchor 0.5
ypos 11
style l_checkbox_text is l_button_text:
selected_font light_font()
+6 -12
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -22,7 +22,6 @@
init python:
class FileTail(object):
def __init__(self, filename, lines=8):
@@ -32,18 +31,13 @@ init python:
def update(self):
def filter_text(s):
if "Unknown chunk type '200'" in s:
return False
return True
try:
with open(self.filename) as f:
text = f.read()
try:
text = renpy.fsdecode(text)
except Exception:
except:
text = text.decode("latin-1")
text = text.strip()
@@ -55,9 +49,6 @@ init python:
if "\r" in l:
_head, _sep, l = l.rpartition("\r")
if not filter_text(l):
continue
while l:
newtext.append(l[:100])
l = l[100:]
@@ -70,5 +61,8 @@ init python:
self.text = text
renpy.restart_interaction()
except Exception:
except:
pass
+3 -3
View File
@@ -1,4 +1,4 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
# Copyright 2004-2017 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
@@ -534,7 +534,7 @@ init python in theme_data:
THEME_FUNCTIONS.add(function)
THEME_SCREEN_OPERATIONS[theme] = roundrect_screen_ops
for scheme, colors in ROUNDRECT_SCHEMES.items():
for scheme, colors in ROUNDRECT_SCHEMES.iteritems():
subs = dict(colors)
subs["function"] = function
subs["theme"] = theme
@@ -587,7 +587,7 @@ init python in theme_data:
THEME_SCREEN_OPERATIONS["A White Tulip"] = awt_screen_ops
for scheme, colors in ROUNDRECT_SCHEMES.items():
for scheme, colors in ROUNDRECT_SCHEMES.iteritems():
subs = dict(colors)
subs["scheme"] = scheme
@@ -1,4 +1,6 @@
# Copyright 2004-2025 Tom Rothamel <pytom@bishoujo.us>
#!/usr/bin/env python
# Copyright 2004-2017 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
@@ -19,30 +21,30 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from renpy.uguu.gl cimport *
# This is used on Linux and Mac to prompt the user for the projects
# directory.
cdef class Program:
import sys
# The name of this program.
cdef public object name
# Python3 and Python2-style imports.
try:
from tkinter import Tk
from tkinter.filedialog import askdirectory
except ImportError:
from Tkinter import Tk
from tkFileDialog import askdirectory
# The number of the OpenGL program created.
cdef GLuint program
# Binary mode stdout for python3.
try:
sys.stdout = sys.stdout.buffer
except:
pass
# The text of the vertex and fragment shaders.
cdef object vertex
cdef object fragment
# Create the TK canvas.
cdef public dict uniforms
if __name__ == "__main__":
root = Tk()
root.withdraw()
cdef public list attributes
cdef public int samplers
cdef public int nearest
cdef dict properties
cdef GLuint load_shader(self, GLenum shader_type, source) except 0
cdef dict uniform_values
result = askdirectory(initialdir=sys.argv[1], parent=root, title="Select Ren'Py Projects Directory")
sys.stdout.write(result.encode("utf8"))
+19 -19
View File
@@ -31,31 +31,31 @@ translate arabic strings:
# 00action_file.rpy:37
old "{#weekday_short}Mon"
new "{#weekday_short}اثن"
new "{#weekday_short}Mon"
# 00action_file.rpy:37
old "{#weekday_short}Tue"
new "{#weekday_short}ثلا"
new "{#weekday_short}Tue"
# 00action_file.rpy:37
old "{#weekday_short}Wed"
new "{#weekday_short}أرب"
new "{#weekday_short}Wed"
# 00action_file.rpy:37
old "{#weekday_short}Thu"
new "{#weekday_short}خمي"
new "{#weekday_short}Thu"
# 00action_file.rpy:37
old "{#weekday_short}Fri"
new "{#weekday_short}جمع"
new "{#weekday_short}Fri"
# 00action_file.rpy:37
old "{#weekday_short}Sat"
new "{#weekday_short}سبت"
new "{#weekday_short}Sat"
# 00action_file.rpy:37
old "{#weekday_short}Sun"
new "{#weekday_short}أحد"
new "{#weekday_short}Sun"
# 00action_file.rpy:47
old "{#month}January"
@@ -107,51 +107,51 @@ translate arabic strings:
# 00action_file.rpy:63
old "{#month_short}Jan"
new "{#month_short}ينا"
new "{#month_short}Jan"
# 00action_file.rpy:63
old "{#month_short}Feb"
new "{#month_short}فبر"
new "{#month_short}Feb"
# 00action_file.rpy:63
old "{#month_short}Mar"
new "{#month_short}مار"
new "{#month_short}Mar"
# 00action_file.rpy:63
old "{#month_short}Apr"
new "{#month_short}ابر"
new "{#month_short}Apr"
# 00action_file.rpy:63
old "{#month_short}May"
new "{#month_short}ماي"
new "{#month_short}May"
# 00action_file.rpy:63
old "{#month_short}Jun"
new "{#month_short}يون"
new "{#month_short}Jun"
# 00action_file.rpy:63
old "{#month_short}Jul"
new "{#month_short}يول"
new "{#month_short}Jul"
# 00action_file.rpy:63
old "{#month_short}Aug"
new "{#month_short}أغس"
new "{#month_short}Aug"
# 00action_file.rpy:63
old "{#month_short}Sep"
new "{#month_short}سبتم"
new "{#month_short}Sep"
# 00action_file.rpy:63
old "{#month_short}Oct"
new "{#month_short}أكت"
new "{#month_short}Oct"
# 00action_file.rpy:63
old "{#month_short}Nov"
new "{#month_short}نوف"
new "{#month_short}Nov"
# 00action_file.rpy:63
old "{#month_short}Dec"
new "{#month_short}ديس"
new "{#month_short}Dec"
# 00action_file.rpy:235
old "%b %d, %H:%M"
+2 -2
View File
@@ -102,8 +102,8 @@ translate arabic strings:
new "يتم تهيئة [name] ([i]/[total])"
# 00gamepad.rpy:58
old "Press or move the [control!s] [kind]."
new "الرجاء ضغط الزر او تحريك [control!s] [kind]."
old "Press or move the [control!r] [kind]."
new "الرجاء ضغط الزر او تحريك [control!r] [kind]."
# 00gamepad.rpy:66
old "Skip (A)"
+1 -1
View File
@@ -138,7 +138,7 @@ translate arabic strings:
new "## حجم الخط الذي يحيط بصندوق اسم الشخصية, الاحجام بالترتيب هي اليسار, الأعلى, اليمين, و الأسفل."
# gui.rpy:127
old "## If True, the background of the namebox will be tiled, if False, the background of the namebox will be scaled."
old "## If True, the background of the namebox will be tiled, if False, the background if the namebox will be scaled."
new "## عندما يتم اختيار True سيتم تكرار الخلفية حتى تغطي الصندوق, عند اختيار False سيتم تمديد الصورة لتغطي الصندوق."
# gui.rpy:132
+12 -13
View File
@@ -1,7 +1,5 @@
translate arabic strings:
# game/new_project.rpy:77
old "{#language name and font}"
new "{font=DejaVuSans.ttf}العربية{/font}"
translate arabic strings:
# about.rpy:39
old "[version!q]"
@@ -36,8 +34,8 @@
new "لبناء ملفات الأندرويد, الرجاء تحميل RAPT, ثم فك الضغط عن الملف ووضعه في مجلد رينباي. قد تحتاج لإعادة تشغيل رينباي ليعمل بشكل صحيح."
# android.rpy:31
old "An x86 Java Development Kit is required to build Android packages on Windows. The JDK is different from the JRE, so it's possible you have Java without having the JDK.\n\nPlease {a=http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html}download and install the JDK{/a}, then restart the Ren'Py launcher."
new "تحتاج لنسخة برمجية من جافا تعتمد الـ 32-بت لتستطيع إنشاء ملفات الأندرويد على نظام الوندوز. حزمة JDK تختلف عن JRE, قد تكون الجافا لديك موجوده لكنها تفتقد الـ JDK. \n\n الرجاء {a=http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html}تحميل و تنصيب JDK{/a} ثم إعادة تشغيل رينباي"
old "A 32-bit Java Development Kit is required to build Android packages on Windows. The JDK is different from the JRE, so it's possible you have Java without having the JDK.\n\nPlease {a=http://www.oracle.com/technetwork/java/javase/downloads/index.html}download and install the JDK{/a}, then restart the Ren'Py launcher."
new "تحتاج لنسخة برمجية من جافا تعتمد الـ 32-بت لتستطيع إنشاء ملفات الأندرويد على نظام الوندوز. حزمة JDK تختلف عن JRE, قد تكون الجافا لديك موجوده لكنها تفتقد الـ JDK. \n\n الرجاء {a=http://www.oracle.com/technetwork/java/javase/downloads/index.html}تحميل و تنصيب JDK{/a} ثم إعادة تشغيل رينباي"
# android.rpy:32
old "RAPT has been installed, but you'll need to install the Android SDK before you can build Android packages. Choose Install SDK to do this."
@@ -364,7 +362,7 @@
new "{b}نقترح.{/b} محرر نص له واجهة سهلة الاستعمال ويعين على كتابة النصوص البرمجية يفضل برنامج يحتوي على مدقق لغوي. Editraحالياً لا يدعم اللغات الأجنبية مثل اللغه الكورية و الصينية و اليابانية. على نظام لينوكس, Editra يحتاج wxPython."
# editor.rpy:167
old "This may have occurred because wxPython is not installed on this system."
old "This may have occured because wxPython is not installed on this system."
new "قد يكون سبب ذلك ان wxPython غير موجود في نظام التشغيل لديك"
# editor.rpy:169
@@ -380,7 +378,7 @@
new "مطلوب تحميل ملف بحجم 1.8 ميغا بايت."
# editor.rpy:182
old "This may have occurred because Java is not installed on this system."
old "This may have occured because Java is not installed on this system."
new "قد يكون السبب ان الجافا غير موجوده على هذا الجهاز."
# editor.rpy:191
@@ -392,7 +390,7 @@
new "يمنع رينباي من فتح اي محرر نصوص"
# editor.rpy:359
old "An exception occurred while launching the text editor:\n[exception!q]"
old "An exception occured while launching the text editor:\n[exception!q]"
new "حصل استثناء اثناء فتح المحرر: \n[exception!q]"
# editor.rpy:457
@@ -564,7 +562,7 @@
new "[project_dir!q] يوجد مسبقاً, الرجاء اختيار اسم مختلف."
# gui7.rpy:341
old "What resolution should the project use? Although Ren'Py can scale the window up and down, this is the initial size of the window, the size at which assets should be drawn, and the size at which the assets will be at their sharpest.\n\nThe default of [default_size[0]]x[default_size[1]] is a reasonable compromise."
old "What resolution should the project use? Although Ren'Py can scale the window up and down, this is the initial size of the window, the size at which assets should be drawn, and the size at which the assets will be at their sharpest.\n\nThe default of 1280x720 is a reasonable compromise."
new "ما هو الحجم المطلوب للمشروع؟ رينباي يستطيع تكبير و تصغير حجم النافذة لتناسب الشاشة, لكن الحجم الذي ستختاره هنا هو الحجم القياسي, و الذي ستظهر فيه الصور و الشخصيات في أوضح شكل لها. \n\n الحجم الذي يناسب اغلب الشاشات هو 1280×730 بكسل."
# gui7.rpy:389
@@ -608,8 +606,8 @@
new "خطأ"
# interface.rpy:356
old "While [what!qt], an error occurred:"
new "حصل خطأ أثناء [what!qt]"
old "While [what!q], an error occured:"
new "حصل خطأ أثناء [what!q]"
# interface.rpy:356
old "[exception!q]"
@@ -1136,7 +1134,7 @@
new "أحدث نسخة طازجة من رينباي التجريبي, قد يحتوي على آخر مستجدات رينباي و قد لا يعمل مطلقاً"
# updater.rpy:152
old "An error has occurred:"
old "An error has occured:"
new "حصل خطأ:"
# updater.rpy:154
@@ -1186,3 +1184,4 @@
# updater.rpy:195
old "Proceed"
new "استمرار"
+42 -41
View File
@@ -71,7 +71,7 @@ translate arabic strings:
# screens.rpy:262
old "History"
new "سجل الحوار"
new "History"
# screens.rpy:263
old "Skip"
@@ -111,11 +111,11 @@ translate arabic strings:
# screens.rpy:308
old "Start"
new "إبدأ"
new "Start"
# screens.rpy:316
old "Load"
new "استرجاع"
new "Load"
# screens.rpy:318
old "Preferences"
@@ -174,8 +174,8 @@ translate arabic strings:
new "## This lays out the basic common structure of a game menu screen. It's called with the screen title, and displays the background, title, and navigation."
# screens.rpy:418
old "## The scroll parameter can be None, or one of \"viewport\" or \"vpgrid\". This screen is intended to be used with one or more children, which are transcluded (placed) inside it."
new "## The scroll parameter can be None, or one of \"viewport\" or \"vpgrid\". This screen is intended to be used with one or more children, which are transcluded (placed) inside it."
old "## The scroll parameter can be None, or one of \"viewport\" or \"vpgrid\". When this screen is intended to be used with one or more children, which are transcluded (placed) inside it."
new "## The scroll parameter can be None, or one of \"viewport\" or \"vpgrid\". When this screen is intended to be used with one or more children, which are transcluded (placed) inside it."
# screens.rpy:476
old "Return"
@@ -227,15 +227,15 @@ translate arabic strings:
# screens.rpy:607
old "Page {}"
new "صفحة {}"
new "Page {}"
# screens.rpy:607
old "Automatic saves"
new "الحفظ التلقائي"
new "Automatic saves"
# screens.rpy:607
old "Quick saves"
new "الحفظ السريع"
new "Quick saves"
# screens.rpy:613
old "## This ensures the input will get the enter event before any of the buttons do."
@@ -251,7 +251,7 @@ translate arabic strings:
# screens.rpy:649
old "empty slot"
new "خانة فارغة"
new "empty slot"
# screens.rpy:657
old "## Buttons to access other pages."
@@ -263,11 +263,11 @@ translate arabic strings:
# screens.rpy:668
old "{#auto_page}A"
new "{#auto_page}ت"
new "{#auto_page}A"
# screens.rpy:670
old "{#quick_page}Q"
new "{#quick_page}س"
new "{#quick_page}Q"
# screens.rpy:676
old ">"
@@ -303,19 +303,19 @@ translate arabic strings:
# screens.rpy:745
old "Disable"
new "غير مفعّل"
new "Disable"
# screens.rpy:746
old "Left"
new "يسار"
new "Left"
# screens.rpy:747
old "Right"
new "يمين"
new "Right"
# screens.rpy:752
old "Unseen Text"
new "النصوص التي لم تُشاهَد بعد"
new "Unseen Text"
# screens.rpy:753
old "After Choices"
@@ -355,7 +355,7 @@ translate arabic strings:
# screens.rpy:806
old "Mute All"
new "كتم الكل"
new "Mute All"
# screens.rpy:882
old "## History screen"
@@ -395,15 +395,15 @@ translate arabic strings:
# screens.rpy:986
old "Keyboard"
new "لوحة المفاتيح"
new "Keyboard"
# screens.rpy:987
old "Mouse"
new "الفأرة"
new "Mouse"
# screens.rpy:990
old "Gamepad"
new "عصا اللعب"
new "Gamepad"
# screens.rpy:1003
old "Enter"
@@ -411,7 +411,7 @@ translate arabic strings:
# screens.rpy:1004
old "Advances dialogue and activates the interface."
new "التقدم في الحوار والتفاعل مع الواجهة."
new "Advances dialogue and activates the interface."
# screens.rpy:1007
old "Space"
@@ -419,15 +419,15 @@ translate arabic strings:
# screens.rpy:1008
old "Advances dialogue without selecting choices."
new "التقدم في الحوار دون تحديد الخيارات."
new "Advances dialogue without selecting choices."
# screens.rpy:1011
old "Arrow Keys"
new "مفاتيح الأسهم"
new "Arrow Keys"
# screens.rpy:1012
old "Navigate the interface."
new "لتصفح الواجهة."
new "Navigate the interface."
# screens.rpy:1015
old "Escape"
@@ -435,7 +435,7 @@ translate arabic strings:
# screens.rpy:1016
old "Accesses the game menu."
new "للوصول لقائمة اللعبة."
new "Accesses the game menu."
# screens.rpy:1019
old "Ctrl"
@@ -443,7 +443,7 @@ translate arabic strings:
# screens.rpy:1020
old "Skips dialogue while held down."
new "لتخطي الحوار عند الإستمرار بالضغط."
new "Skips dialogue while held down."
# screens.rpy:1023
old "Tab"
@@ -451,7 +451,7 @@ translate arabic strings:
# screens.rpy:1024
old "Toggles dialogue skipping."
new "للتبديل بين أنظمة تخطي الحوار."
new "Toggles dialogue skipping."
# screens.rpy:1027
old "Page Up"
@@ -459,7 +459,7 @@ translate arabic strings:
# screens.rpy:1028
old "Rolls back to earlier dialogue."
new "للعودة للنص السابق."
new "Rolls back to earlier dialogue."
# screens.rpy:1031
old "Page Down"
@@ -467,39 +467,39 @@ translate arabic strings:
# screens.rpy:1032
old "Rolls forward to later dialogue."
new "للإستمرار للنص اللاحق."
new "Rolls forward to later dialogue."
# screens.rpy:1036
old "Hides the user interface."
new "لإخفاء الواجهة."
new "Hides the user interface."
# screens.rpy:1040
old "Takes a screenshot."
new "إلتقاط صورة من الشاشة."
new "Takes a screenshot."
# screens.rpy:1044
old "Toggles assistive {a=https://www.renpy.org/l/voicing}self-voicing{/a}."
new "تفعيل مساعدة {a=https://www.renpy.org/l/voicing}الصوت التلقائي{/a}."
new "Toggles assistive {a=https://www.renpy.org/l/voicing}self-voicing{/a}."
# screens.rpy:1050
old "Left Click"
new "النقر بالزر الأيسر"
new "Left Click"
# screens.rpy:1054
old "Middle Click"
new "النقر بالزر الأوسط"
new "Middle Click"
# screens.rpy:1058
old "Right Click"
new "النقر بالزر الأيمن"
new "Right Click"
# screens.rpy:1062
old "Mouse Wheel Up"
new "تحريك عجلة الفأرة لأعلى"
old "Mouse Wheel Up\nClick Rollback Side"
new "Mouse Wheel Up\nClick Rollback Side"
# screens.rpy:1066
old "Mouse Wheel Down"
new "تحريك عجلة الفأرة لأسفل"
new "Mouse Wheel Down"
# screens.rpy:1073
old "Right Trigger\nA/Bottom Button"
@@ -507,11 +507,11 @@ translate arabic strings:
# screens.rpy:1074
old "Advance dialogue and activates the interface."
new "التقدم في الحوار والتفاعل مع الواجهة."
new "Advance dialogue and activates the interface."
# screens.rpy:1078
old "Roll back to earlier dialogue."
new "للعودة للنص السابق."
new "Roll back to earlier dialogue."
# screens.rpy:1081
old "Right Shoulder"
@@ -519,7 +519,7 @@ translate arabic strings:
# screens.rpy:1082
old "Roll forward to later dialogue."
new "للإستمرار للنص اللاحق."
new "Roll forward to later dialogue."
# screens.rpy:1085
old "D-Pad, Sticks"
@@ -587,7 +587,7 @@ translate arabic strings:
# screens.rpy:1208
old "Skipping"
new "جاري التخطي"
new "Skipping"
# screens.rpy:1215
old "## This transform is used to blink the arrows one after another."
@@ -640,3 +640,4 @@ translate arabic strings:
# screens.rpy:1429
old "Menu"
new "Menu"
+5 -69
View File
@@ -6,6 +6,11 @@
# من هي شخصيات هذه اللعبه؟
define e = Character('إيلين', color="#c8ffc8")
init python:
if not persistent.default_language_set:
persistent.default_language_set = True
_preferences.language = "arabic"
# اللعبه تبدأ هنا
label start:
@@ -14,72 +19,3 @@ label start:
e "كل ما عليك فعله هو إضافة نصوص و صور و إرسالها للعالم!"
return
########################################################### من اليمين الى اليسار
define config.rtl = True
define gui.textalign = 1.0
define gui.button_text_xalign = 1.0
define gui.prompt_text_xalign = 1.0
define gui.navigation_xpos = 240
define gui.name_xalign = 1.0
define gui.dialogue_text_xalign = 1.0
define gui.button_text_xalign = 1.0
define gui.main_menu_text_xalign = 1.0
define gui.history_name_xalign = 0.0
define gui.history_text_xalign = 1.0
define gui.nvl_name_xalign = 0.0
define gui.nvl_text_xalign = 1.0
define gui.nvl_thought_xalign = 1.0
define gui.nvl_button_xalign = 1.0
define gui.help_label_text_xalign = 0.0
define gui.name_xpos = gui.scale(1040)
define gui.dialogue_xpos = gui.scale(1012)
define gui.history_name_xpos = gui.scale(763)
define gui.history_text_xpos = gui.scale(748)
define gui.nvl_name_xpos = gui.scale(850)
define gui.nvl_text_xpos = gui.scale(830)
define gui.nvl_thought_xpos = gui.scale(1040)
define gui.nvl_button_xpos = gui.scale(830)
style game_menu_label:
xalign 1.0
xoffset gui.scale(-50)
style navigation_vbox:
xanchor 1.0
style return_button:
xanchor 1.0
style radio_button:
xalign 1.0
style radio_label:
xalign 1.0
style check_button:
xalign 1.0
style check_label:
xalign 1.0
style slider_label:
xpos gui.scale(350)
xalign 1.0
style help_hbox:
box_reverse True
xalign 1.0
spacing gui.scale(20)
style help_label_text:
xalign 0.0
style mute_all_button:
xalign 0.0
+3
View File
@@ -5,3 +5,6 @@
gui.REGULAR_BOLD = True
config.rtl = True
init python:
translate_define("arabic", "config.rtl", "True")
-978
View File
@@ -1,978 +0,0 @@
translate danish strings:
# renpy/common/00accessibility.rpy:28
old "Self-voicing disabled."
new "Automatisk oplæsning deaktiveret."
# renpy/common/00accessibility.rpy:29
old "Clipboard voicing enabled. "
new "Oplæsning af udklipsholder aktiveret."
# renpy/common/00accessibility.rpy:30
old "Self-voicing enabled. "
new "Automatisk oplæsning aktiveret."
# renpy/common/00accessibility.rpy:32
old "bar"
new "bjælke"
# renpy/common/00accessibility.rpy:33
old "selected"
new "valgt"
# renpy/common/00accessibility.rpy:34
old "viewport"
new "visningsport"
# renpy/common/00accessibility.rpy:35
old "horizontal scroll"
new "vandret rulning"
# renpy/common/00accessibility.rpy:36
old "vertical scroll"
new "lodret rulning"
# renpy/common/00accessibility.rpy:37
old "activate"
new "aktivér"
# renpy/common/00accessibility.rpy:38
old "deactivate"
new "deaktiver"
# renpy/common/00accessibility.rpy:39
old "increase"
new "øg"
# renpy/common/00accessibility.rpy:40
old "decrease"
new "sænk"
# renpy/common/00accessibility.rpy:138
old "Font Override"
new "Tilsidesættelse af skrifttype"
# renpy/common/00accessibility.rpy:142
old "Default"
new "Standard"
# renpy/common/00accessibility.rpy:146
old "DejaVu Sans"
new "DejaVu Sans"
# renpy/common/00accessibility.rpy:150
old "Opendyslexic"
new "Opendyslexic"
# renpy/common/00accessibility.rpy:156
old "Text Size Scaling"
new "Skalering af skriftstørrelse"
# renpy/common/00accessibility.rpy:162
old "Reset"
new "Nulstil"
# renpy/common/00accessibility.rpy:168
old "Line Spacing Scaling"
new "Skalering af linjeafstand"
# renpy/common/00accessibility.rpy:180
old "High Contrast Text"
new "Tekst med høj kontrast"
# renpy/common/00accessibility.rpy:182
old "Enable"
new "Slå til"
# renpy/common/00accessibility.rpy:186
old "Disable"
new "Slå fra"
# renpy/common/00accessibility.rpy:193
old "Self-Voicing"
new "Automatisk oplæsning"
# renpy/common/00accessibility.rpy:197
old "Off"
new "Fra"
# renpy/common/00accessibility.rpy:201
old "Text-to-speech"
new "Tekst til tale"
# renpy/common/00accessibility.rpy:205
old "Clipboard"
new "Udklipsholder"
# renpy/common/00accessibility.rpy:209
old "Debug"
new "Fejlsøgning"
# renpy/common/00accessibility.rpy:223
old "Self-Voicing Volume Drop"
new "Lydstyrkesænkning ved automatisk oplæsning"
# renpy/common/00accessibility.rpy:234
old "The options on this menu are intended to improve accessibility. They may not work with all games, and some combinations of options may render the game unplayable. This is not an issue with the game or engine. For the best results when changing fonts, try to keep the text size the same as it originally was."
new "Indstillingerne i denne menu er tiltænkt at forbedre tilgængelighed. De virker muligvis ikke med alle spil, og visse kombinationer af indstillinger gør muligvis spillet uspilleligt. Dette er ikke et problem med spillet eller spilmotoren. Prøv at bevare den originale skriftstørrelse for at opnå det bedste resultat væd ændring af skrifttype."
# renpy/common/00action_file.rpy:26
old "{#weekday}Monday"
new "{#weekday}mandag"
# renpy/common/00action_file.rpy:26
old "{#weekday}Tuesday"
new "{#weekday}tirsdag"
# renpy/common/00action_file.rpy:26
old "{#weekday}Wednesday"
new "{#weekday}onsdag"
# renpy/common/00action_file.rpy:26
old "{#weekday}Thursday"
new "{#weekday}torsdag"
# renpy/common/00action_file.rpy:26
old "{#weekday}Friday"
new "{#weekday}fredag"
# renpy/common/00action_file.rpy:26
old "{#weekday}Saturday"
new "{#weekday}lørdag"
# renpy/common/00action_file.rpy:26
old "{#weekday}Sunday"
new "{#weekday}søndag"
# renpy/common/00action_file.rpy:37
old "{#weekday_short}Mon"
new "{#weekday_short}man"
# renpy/common/00action_file.rpy:37
old "{#weekday_short}Tue"
new "{#weekday_short}tir"
# renpy/common/00action_file.rpy:37
old "{#weekday_short}Wed"
new "{#weekday_short}ons"
# renpy/common/00action_file.rpy:37
old "{#weekday_short}Thu"
new "{#weekday_short}tor"
# renpy/common/00action_file.rpy:37
old "{#weekday_short}Fri"
new "{#weekday_short}fre"
# renpy/common/00action_file.rpy:37
old "{#weekday_short}Sat"
new "{#weekday_short}lør"
# renpy/common/00action_file.rpy:37
old "{#weekday_short}Sun"
new "{#weekday_short}søn"
# renpy/common/00action_file.rpy:47
old "{#month}January"
new "{#month}januar"
# renpy/common/00action_file.rpy:47
old "{#month}February"
new "{#month}februar"
# renpy/common/00action_file.rpy:47
old "{#month}March"
new "{#month}marts"
# renpy/common/00action_file.rpy:47
old "{#month}April"
new "{#month}april"
# renpy/common/00action_file.rpy:47
old "{#month}May"
new "{#month}maj"
# renpy/common/00action_file.rpy:47
old "{#month}June"
new "{#month}juni"
# renpy/common/00action_file.rpy:47
old "{#month}July"
new "{#month}juli"
# renpy/common/00action_file.rpy:47
old "{#month}August"
new "{#month}august"
# renpy/common/00action_file.rpy:47
old "{#month}September"
new "{#month}september"
# renpy/common/00action_file.rpy:47
old "{#month}October"
new "{#month}oktober"
# renpy/common/00action_file.rpy:47
old "{#month}November"
new "{#month}november"
# renpy/common/00action_file.rpy:47
old "{#month}December"
new "{#month}december"
# renpy/common/00action_file.rpy:63
old "{#month_short}Jan"
new "{#month_short}jan"
# renpy/common/00action_file.rpy:63
old "{#month_short}Feb"
new "{#month_short}feb"
# renpy/common/00action_file.rpy:63
old "{#month_short}Mar"
new "{#month_short}mar"
# renpy/common/00action_file.rpy:63
old "{#month_short}Apr"
new "{#month_short}apr"
# renpy/common/00action_file.rpy:63
old "{#month_short}May"
new "{#month_short}maj"
# renpy/common/00action_file.rpy:63
old "{#month_short}Jun"
new "{#month_short}jun"
# renpy/common/00action_file.rpy:63
old "{#month_short}Jul"
new "{#month_short}jul"
# renpy/common/00action_file.rpy:63
old "{#month_short}Aug"
new "{#month_short}aug"
# renpy/common/00action_file.rpy:63
old "{#month_short}Sep"
new "{#month_short}sep"
# renpy/common/00action_file.rpy:63
old "{#month_short}Oct"
new "{#month_short}okt"
# renpy/common/00action_file.rpy:63
old "{#month_short}Nov"
new "{#month_short}nov"
# renpy/common/00action_file.rpy:63
old "{#month_short}Dec"
new "{#month_short}dec"
# renpy/common/00action_file.rpy:258
old "%b %d, %H:%M"
new "%d. %b, %H:%M"
# renpy/common/00action_file.rpy:378
old "Save slot %s: [text]"
new "Gem plads %s: [text]"
# renpy/common/00action_file.rpy:461
old "Load slot %s: [text]"
new "Indlæs plads %s: [text]"
# renpy/common/00action_file.rpy:514
old "Delete slot [text]"
new "Slet plads [text]"
# renpy/common/00action_file.rpy:593
old "File page auto"
new "Filside auto"
# renpy/common/00action_file.rpy:595
old "File page quick"
new "Filside hurtig"
# renpy/common/00action_file.rpy:597
old "File page [text]"
new "Filside [text]"
# renpy/common/00action_file.rpy:796
old "Next file page."
new "Næste filside."
# renpy/common/00action_file.rpy:868
old "Previous file page."
new "Forrige filside."
# renpy/common/00action_file.rpy:929
old "Quick save complete."
new "Hurtiggemning fuldført."
# renpy/common/00action_file.rpy:944
old "Quick save."
new "Hurtiggem."
# renpy/common/00action_file.rpy:963
old "Quick load."
new "Hurtigindlæs."
# renpy/common/00action_other.rpy:383
old "Language [text]"
new "Sprog [text]"
# renpy/common/00action_other.rpy:724
old "Open [text] directory."
new "Åbn mappen [text]."
# renpy/common/00director.rpy:705
old "The interactive director is not enabled here."
new "Det interaktive instruktionsværktøj er ikke aktiveret her."
# renpy/common/00director.rpy:1504
old "⬆"
new "⬆"
# renpy/common/00director.rpy:1510
old "⬇"
new "⬇"
# renpy/common/00director.rpy:1574
old "Done"
new "Færdig"
# renpy/common/00director.rpy:1584
old "(statement)"
new "(sætning)"
# renpy/common/00director.rpy:1585
old "(tag)"
new "(mærke)"
# renpy/common/00director.rpy:1586
old "(attributes)"
new "(attributter)"
# renpy/common/00director.rpy:1587
old "(transform)"
new "(transformation)"
# renpy/common/00director.rpy:1612
old "(transition)"
new "(overgang)"
# renpy/common/00director.rpy:1624
old "(channel)"
new "(kanal)"
# renpy/common/00director.rpy:1625
old "(filename)"
new "(filnavn)"
# renpy/common/00director.rpy:1654
old "Change"
new "Ændr"
# renpy/common/00director.rpy:1656
old "Add"
new "Tilføj"
# renpy/common/00director.rpy:1659
old "Cancel"
new "Annuller"
# renpy/common/00director.rpy:1662
old "Remove"
new "Fjern"
# renpy/common/00director.rpy:1697
old "Statement:"
new "Sætning:"
# renpy/common/00director.rpy:1718
old "Tag:"
new "Mærke:"
# renpy/common/00director.rpy:1734
old "Attributes:"
new "Attributter:"
# renpy/common/00director.rpy:1745
old "Click to toggle attribute, right click to toggle negative attribute."
new "Klik for at slå attributter til/fra, højreklik for at slå negative attributter til/fra."
# renpy/common/00director.rpy:1757
old "Transforms:"
new "Transformationer:"
# renpy/common/00director.rpy:1768
old "Click to set transform, right click to add to transform list."
new "Klik for at stille transformation, højreklik for at tilføje til transformationsliste."
# renpy/common/00director.rpy:1780
old "Behind:"
new "Bagved:"
# renpy/common/00director.rpy:1789
old "Click to set, right click to add to behind list."
new "Klik for at stille, højreklik for at tilføje til bagvedliste."
# renpy/common/00director.rpy:1801
old "Transition:"
new "Overgang:"
# renpy/common/00director.rpy:1819
old "Channel:"
new "Kanal:"
# renpy/common/00director.rpy:1837
old "Audio Filename:"
new "Lydfilnavn:"
# renpy/common/00gui.rpy:446
old "Are you sure?"
new "Er du sikker?"
# renpy/common/00gui.rpy:447
old "Are you sure you want to delete this save?"
new "Vil du gerne slette denne gemmefil?"
# renpy/common/00gui.rpy:448
old "Are you sure you want to overwrite your save?"
new "Vil du gerne overskrive din gemmefil?"
# renpy/common/00gui.rpy:449
old "Loading will lose unsaved progress.\nAre you sure you want to do this?"
new "Ved indlæsning vil ugemte fremskridt gå tabt.\nVil du gerne gøre dette?"
# renpy/common/00gui.rpy:450
old "Are you sure you want to quit?"
new "Vil du gerne afslutte?"
# renpy/common/00gui.rpy:451
old "Are you sure you want to return to the main menu?\nThis will lose unsaved progress."
new "Vil du gerne vende tilbage til hovedmenuen?\nUgemte fremskridt vil gå tabt."
# renpy/common/00gui.rpy:452
old "Are you sure you want to end the replay?"
new "Vil du gerne afslutte genafspilningnen?"
# renpy/common/00gui.rpy:453
old "Are you sure you want to begin skipping?"
new "Vil du gerne påbegynde overspringning?"
# renpy/common/00gui.rpy:454
old "Are you sure you want to skip to the next choice?"
new "Vil du gerne springe videre til det næste valg?"
# renpy/common/00gui.rpy:455
old "Are you sure you want to skip unseen dialogue to the next choice?"
new "Vil du gerne springe over uset dialog til det næste valg?"
# renpy/common/00gui.rpy:456
old "This save was created on a different device. Maliciously constructed save files can harm your computer. Do you trust this save's creator and everyone who could have changed the file?"
new "Denne gemmefil blev oprettet på en anden enhed. Gemmefiler konstrueret med ondsindet hensigt kan skade din computer. Stoler du på den, der har oprettet gemmefilen, og på alle, der kan have ændret filen?"
# renpy/common/00gui.rpy:457
old "Do you trust the device the save was created on? You should only choose yes if you are the device's sole user."
new "Stoler du på enheden, som gemmefilen oprettedes på? Du bør kun vælge ja, hvis du er enhedens eneste bruger."
# renpy/common/00keymap.rpy:322
old "Failed to save screenshot as %s."
new "Kunne ikke gemme skærmbillede som %s."
# renpy/common/00keymap.rpy:334
old "Saved screenshot as %s."
new "Gemte skærmbillede som %s."
# renpy/common/00library.rpy:248
old "Skip Mode"
new "Overspringstilstand"
# renpy/common/00library.rpy:335
old "This program contains free software under a number of licenses, including the MIT License and GNU Lesser General Public License. A complete list of software, including links to full source code, can be found {a=https://www.renpy.org/l/license}here{/a}."
new "Dette program indeholder gratis software under forskellige licenser, herunder MIT-licensen og GNU Lesser General Public License. En komplet liste over software, inklusiv links til fulde kildekoder, kan findes {a=https://www.renpy.org/l/license}her{/a}."
# renpy/common/00preferences.rpy:271
old "display"
new "skærmvisning"
# renpy/common/00preferences.rpy:287
old "transitions"
new "overgange"
# renpy/common/00preferences.rpy:296
old "skip transitions"
new "spring overgange over"
# renpy/common/00preferences.rpy:298
old "video sprites"
new "videosprites"
# renpy/common/00preferences.rpy:307
old "show empty window"
new "vis tomt vindue"
# renpy/common/00preferences.rpy:316
old "text speed"
new "teksthastighed"
# renpy/common/00preferences.rpy:324
old "joystick"
new "styrepind"
# renpy/common/00preferences.rpy:324
old "joystick..."
new "styrepind..."
# renpy/common/00preferences.rpy:331
old "skip"
new "spring over"
# renpy/common/00preferences.rpy:334
old "skip unseen [text]"
new "spring over uset [text]"
# renpy/common/00preferences.rpy:339
old "skip unseen text"
new "spring over uset tekst"
# renpy/common/00preferences.rpy:341
old "begin skipping"
new "påbegynd overspringning"
# renpy/common/00preferences.rpy:345
old "after choices"
new "efter valg"
# renpy/common/00preferences.rpy:352
old "skip after choices"
new "spring over efter valg"
# renpy/common/00preferences.rpy:354
old "auto-forward time"
new "auto-fremad-tid"
# renpy/common/00preferences.rpy:368
old "auto-forward"
new "auto-fremad"
# renpy/common/00preferences.rpy:375
old "Auto forward"
new "auto-fremad"
# renpy/common/00preferences.rpy:378
old "auto-forward after click"
new "auto-fremad efter klik"
# renpy/common/00preferences.rpy:387
old "automatic move"
new "automatisk bevægelse"
# renpy/common/00preferences.rpy:396
old "wait for voice"
new "vent på stemme"
# renpy/common/00preferences.rpy:405
old "voice sustain"
new "stemmebevarelse"
# renpy/common/00preferences.rpy:414
old "self voicing"
new "automatisk oplæsning"
# renpy/common/00preferences.rpy:423
old "self voicing volume drop"
new "lydstyrkesænkning ved automatisk oplæsning"
# renpy/common/00preferences.rpy:431
old "clipboard voicing"
new "oplæsning af udklipsholder"
# renpy/common/00preferences.rpy:440
old "debug voicing"
new "oplæsning af fejlsøgning"
# renpy/common/00preferences.rpy:449
old "emphasize audio"
new "fremhæv lyd"
# renpy/common/00preferences.rpy:458
old "rollback side"
new "tilbagerulningsside"
# renpy/common/00preferences.rpy:468
old "gl powersave"
new "gl-strømbesparelse"
# renpy/common/00preferences.rpy:474
old "gl framerate"
new "gl-billedhastighed"
# renpy/common/00preferences.rpy:477
old "gl tearing"
new "gl-overrivning"
# renpy/common/00preferences.rpy:480
old "font transform"
new "skrifttransformation"
# renpy/common/00preferences.rpy:483
old "font size"
new "skriftstørrelse"
# renpy/common/00preferences.rpy:491
old "font line spacing"
new "linjeafstand for skrift"
# renpy/common/00preferences.rpy:499
old "system cursor"
new "systemmarkør"
# renpy/common/00preferences.rpy:508
old "renderer menu"
new "optegningsmenu"
# renpy/common/00preferences.rpy:511
old "accessibility menu"
new "tilgængelighedsmenu"
# renpy/common/00preferences.rpy:514
old "high contrast text"
new "tekst med høj kontrast"
# renpy/common/00preferences.rpy:523
old "audio when minimized"
new "lyd ved minimeret tilstand"
# renpy/common/00preferences.rpy:532
old "audio when unfocused"
new "lyd ved ufokuseret tilstand"
# renpy/common/00preferences.rpy:541
old "web cache preload"
new "forudindlæsning af webcache"
# renpy/common/00preferences.rpy:556
old "voice after game menu"
new "stemme efter spilmenu"
# renpy/common/00preferences.rpy:575
old "main volume"
new "primær lydstyrke"
# renpy/common/00preferences.rpy:576
old "music volume"
new "musiklydstyrke"
# renpy/common/00preferences.rpy:577
old "sound volume"
new "lydeffektlydstyrke"
# renpy/common/00preferences.rpy:578
old "voice volume"
new "stemmelydstyrke"
# renpy/common/00preferences.rpy:579
old "mute main"
new "slå primær lyd fra"
# renpy/common/00preferences.rpy:580
old "mute music"
new "slå musik fra"
# renpy/common/00preferences.rpy:581
old "mute sound"
new "slå lydeffekter fra"
# renpy/common/00preferences.rpy:582
old "mute voice"
new "slå stemmer fra"
# renpy/common/00preferences.rpy:583
old "mute all"
new "slå alle fra"
# renpy/common/00preferences.rpy:657
old "Clipboard voicing enabled. Press 'shift+C' to disable."
new "Oplæsning af udklipsholder aktiveret. Tryk på 'shift+C' for at deaktivere."
# renpy/common/00preferences.rpy:659
old "Self-voicing would say \"[renpy.display.tts.last]\". Press 'alt+shift+V' to disable."
new "Automatisk oplæsning ville sige \"[renpy.display.tts.last]\". Tryk på 'alt+shift+V' for at deaktivere."
# renpy/common/00preferences.rpy:661
old "Self-voicing enabled. Press 'v' to disable."
new "Automatisk oplæsning aktiveret. Tryk på 'v' for at deaktivere."
# renpy/common/00speechbubble.rpy:344
old "Speech Bubble Editor"
new "Talebobleredigering"
# renpy/common/00speechbubble.rpy:349
old "(hide)"
new "(skjul)"
# renpy/common/00sync.rpy:70
old "Sync downloaded."
new "Synkronisering hentet."
# renpy/common/00sync.rpy:190
old "Could not connect to the Ren'Py Sync server."
new "Kunne ikke oprette forbindelse til Ren'Py Sync-serveren."
# renpy/common/00sync.rpy:192
old "The Ren'Py Sync server timed out."
new "Der opstod et forbindelsestimeout til Ren'Py Sync-serveren."
# renpy/common/00sync.rpy:194
old "An unknown error occurred while connecting to the Ren'Py Sync server."
new "En ukendt fejl opstod under forbindelse til Ren'Py Sync-serveren."
# renpy/common/00sync.rpy:267
old "The Ren'Py Sync server does not have a copy of this sync. The sync ID may be invalid, or it may have timed out."
new "Ren'Py Sync-serveren har ikke en kopi af denne synkronisering. Synkroniserings-id'er er muligvis ugyldig eller udløbet."
# renpy/common/00sync.rpy:412
old "Please enter the sync ID you generated.\nNever enter a sync ID you didn't create yourself."
new "Indtast venligst dit genererede synkroniserings-id.\nIndtast aldrig et synkroniserings-id, som du ikke selv har oprettet."
# renpy/common/00sync.rpy:431
old "The sync ID is not in the correct format."
new "Synkroniserings-id'et er ikke i det korrekte format."
# renpy/common/00sync.rpy:451
old "The sync could not be decrypted."
new "Synkroniseringen kunne ikke afkodes."
# renpy/common/00sync.rpy:474
old "The sync belongs to a different game."
new "Synkroniseringen tilhører et andet spil."
# renpy/common/00sync.rpy:479
old "The sync contains a file with an invalid name."
new "Synkroniseringen indeholder en fil med et ugyldigt navn."
# renpy/common/00sync.rpy:532
old "This will upload your saves to the {a=https://sync.renpy.org}Ren'Py Sync Server{/a}.\nDo you want to continue?"
new "Dette sender dine gemmefiler til {a=https://sync.renpy.org}Ren'Py Sync-serveren{/a}.\nVil du gerne fortsætte?"
# renpy/common/00sync.rpy:561
old "Enter Sync ID"
new "Indtast Synkroniserings-id"
# renpy/common/00sync.rpy:572
old "This will contact the {a=https://sync.renpy.org}Ren'Py Sync Server{/a}."
new "Dette kontakter {a=https://sync.renpy.org}Ren'Py Sync-serveren{/a}."
# renpy/common/00sync.rpy:599
old "Sync Success"
new "Synkronisering lykkedes"
# renpy/common/00sync.rpy:602
old "The Sync ID is:"
new "Synkroniserings-id'et er:"
# renpy/common/00sync.rpy:608
old "You can use this ID to download your save on another device.\nThis sync will expire in an hour.\nRen'Py Sync is supported by {a=https://www.renpy.org/sponsors.html}Ren'Py's Sponsors{/a}."
new "Du kan bruge dette id til at hente din gemmefil på en anden enhed.\nDenne synkronisering udløber om en time.\nRen'Py Sync støttes af {a=https://www.renpy.org/sponsors.html}Ren'Pys sponsorer{/a}."
# renpy/common/00sync.rpy:612
old "Continue"
new "Fortsæt"
# renpy/common/00sync.rpy:634
old "Sync Error"
new "Synkroniseringsfejl"
# renpy/common/00iap.rpy:219
old "Contacting App Store\nPlease Wait..."
new "Kontakter App Store\nVent venligst..."
# renpy/common/00updater.rpy:419
old "The Ren'Py Updater is not supported on mobile devices."
new "Ren'Py-opdateringsprogrammet understøttes ikke på mobilenheder."
# renpy/common/00updater.rpy:548
old "An error is being simulated."
new "En fejl simuleres."
# renpy/common/00updater.rpy:738
old "Either this project does not support updating, or the update status file was deleted."
new "Enten understøtter dette projekt ikke opdatering, eller også er opdateringsstatusfilen blevet slettet."
# renpy/common/00updater.rpy:752
old "This account does not have permission to perform an update."
new "Denne bruger har ikke tilladelse til at udføre en opdatering."
# renpy/common/00updater.rpy:755
old "This account does not have permission to write the update log."
new "Denne bruger har ikke tilladelse til at skrive opdateringsloggen."
# renpy/common/00updater.rpy:783
old "Could not verify update signature."
new "Kunne ikke efterprøve opdateringssignaturen."
# renpy/common/00updater.rpy:1084
old "The update file was not downloaded."
new "Opdateringsfilen blev ikke hentet."
# renpy/common/00updater.rpy:1102
old "The update file does not have the correct digest - it may have been corrupted."
new "Opdateringsfilen har ikke det korrekte fingeraftryk - den er muligvis blevet korrupteret."
# renpy/common/00updater.rpy:1252
old "While unpacking {}, unknown type {}."
new "Under udpakning {}, ukendt type {}"
# renpy/common/00updater.rpy:1624
old "Updater"
new "Opdateringsprogram"
# renpy/common/00updater.rpy:1635
old "This program is up to date."
new "Dette program er ajour."
# renpy/common/00updater.rpy:1637
old "[u.version] is available. Do you want to install it?"
new "[u.version] er tilgængelig. Vil du gerne installere denne?"
# renpy/common/00updater.rpy:1639
old "Preparing to download the updates."
new "Forbereder hentning af opdateringerne."
# renpy/common/00updater.rpy:1641
old "Downloading the updates."
new "Henter opdateringerne."
# renpy/common/00updater.rpy:1643
old "Unpacking the updates."
new "Udpakker opdateringerne."
# renpy/common/00updater.rpy:1647
old "The updates have been installed. The program will restart."
new "Opdateringer er blevet installeret. Programmet genstarter."
# renpy/common/00updater.rpy:1649
old "The updates have been installed."
new "Opdateringerne er blevet installeret."
# renpy/common/00updater.rpy:1651
old "The updates were cancelled."
new "Opdateringerne blev annulleret."
# renpy/common/00gallery.rpy:627
old "Image [index] of [count] locked."
new "Billede [index] af [count] låst."
# renpy/common/00gallery.rpy:647
old "prev"
new "forrige"
# renpy/common/00gallery.rpy:648
old "next"
new "næste"
# renpy/common/00gallery.rpy:649
old "slideshow"
new "diasshow"
# renpy/common/00gallery.rpy:650
old "return"
new "tilbage"
# renpy/common/00director.rpy:1776
old "Customize director.transforms to add more transforms."
new "Tilpas director.transforms for at tilføje flere transformationer."
# renpy/common/00director.rpy:1821
old "Click to set."
new "Klik for at stille."
# renpy/common/00director.rpy:1822
old "Customize director.transitions to add more transitions."
new "Tilpas director.transitions for at tilføje flere overgange."
# renpy/common/00director.rpy:1845
old "Customize director.audio_channels to add more channels."
new "Tilpas director.audio_channels for at tilføje flere kanaler."
# renpy/common/00preferences.rpy:573
old "restore window position"
new "gendan vinduesplacering"
# renpy/common/00speechbubble.rpy:408
old "(clear retained bubbles)"
new "(opryd bibeholdte bobler)"
# renpy/common/00updater.rpy:504
old "No update methods found."
new "Ingen opdateringsmetoder fundet."
# renpy/common/00updater.rpy:551
old "Could not download file list: "
new "Kunne ikke hente filliste:"
# renpy/common/00updater.rpy:554
old "File list digest does not match."
new "Fillistens fingeraftryk matcher ikke."
# renpy/common/00updater.rpy:2072
old "Preparing to download the game data."
new "Forbereder hentning af spildataene."
# renpy/common/00updater.rpy:2074
old "Downloading the game data."
new "Henter spildataene."
# renpy/common/00updater.rpy:2076
old "The game data has been downloaded."
new "Spildataene er blevet hentet."
# renpy/common/00updater.rpy:2078
old "An error occurred when trying to download game data:"
new "En fejl opstod under forsøg på hentning af spildata:"
# renpy/common/00updater.rpy:2083
old "This game cannot be run until the game data has been downloaded."
new "Dette spil kan ikke køres, indtil spildataene er blevet hentet."
# renpy/common/00updater.rpy:2090
old "Retry"
new "Prøv igen"
# renpy/common/00accessibility.rpy:120
old "Accessibility Menu. Use up and down arrows to navigate, and enter to activate buttons and bars."
new "Tilgængelighedsmenu. Brug op- og ned-piletasterne til at navigere og retur til at aktivere knapper og bjælker."
# renpy/common/00gui.rpy:454
old "Are you sure you want to continue where you left off?"
new "Vil du gerne fortsætte fra hvor du slap?"
# renpy/common/00preferences.rpy:439
old "self voicing enable"
new "aktivér automatisk oplæsning"
# renpy/common/00preferences.rpy:441
old "self voicing disable"
new "deaktiver automatisk oplæsning"
# renpy/common/00preferences.rpy:456
old "clipboard voicing enable"
new "aktivér oplæsning af udklipsholder"
# renpy/common/00preferences.rpy:458
old "clipboard voicing disable"
new "deaktiver oplæsning af udklipsholder"
# renpy/common/00preferences.rpy:465
old "debug voicing enable"
new "aktivér oplæsning af fejlsøgning"
# renpy/common/00preferences.rpy:467
old "debug voicing disable"
new "deaktiver oplæsning af fejlsøgning"
# renpy/common/00preferences.rpy:596
old "reset"
new "nulstil"
-278
View File
@@ -1,278 +0,0 @@
translate danish strings:
# renpy/common/_developer/developer.rpym:38
old "Developer Menu"
new "Udviklermenu"
# renpy/common/_developer/developer.rpym:43
old "Interactive Director (D)"
new "Interaktivt instruktionsværktøj (D)"
# renpy/common/_developer/developer.rpym:45
old "Reload Game (Shift+R)"
new "Genindlæs spil (Shift+R)"
# renpy/common/_developer/developer.rpym:47
old "Console (Shift+O)"
new "Konsol (Shift+O)"
# renpy/common/_developer/developer.rpym:49
old "Variable Viewer"
new "Variabelviser"
# renpy/common/_developer/developer.rpym:51
old "Persistent Viewer"
new "Vedvarende data-viser"
# renpy/common/_developer/developer.rpym:53
old "Image Location Picker"
new "Billedplaceringsvælger"
# renpy/common/_developer/developer.rpym:55
old "Filename List"
new "Filnavneliste"
# renpy/common/_developer/developer.rpym:59
old "Show Image Load Log (F4)"
new "Vis billedindlæsningslog (F4)"
# renpy/common/_developer/developer.rpym:62
old "Hide Image Load Log (F4)"
new "Skjul billedindlæsningslog (F4)"
# renpy/common/_developer/developer.rpym:65
old "Image Attributes"
new "Billedattributter"
# renpy/common/_developer/developer.rpym:70
old "Speech Bubble Editor (Shift+B)"
new "Talebobleredigering (Shift+B)"
# renpy/common/_developer/developer.rpym:97
old "[name] [attributes] (hidden)"
new "[name] [attributes] (skjult)"
# renpy/common/_developer/developer.rpym:101
old "[name] [attributes]"
new "[name] [attributes]"
# renpy/common/_developer/developer.rpym:151
old "Nothing to inspect."
new "Intet at inspicere."
# renpy/common/_developer/developer.rpym:162
old "Hide deleted"
new "Skjul slettede"
# renpy/common/_developer/developer.rpym:162
old "Show deleted"
new "Vis slettede"
# renpy/common/_developer/developer.rpym:318
old "Rectangle copied to clipboard."
new "Rektangel kopieret til udklipsholder."
# renpy/common/_developer/developer.rpym:321
old "Position copied to clipboard."
new "Position kopieret til udklipsholder."
# renpy/common/_developer/developer.rpym:333
old "Rectangle: %r"
new "Rektangel: %r"
# renpy/common/_developer/developer.rpym:336
old "Mouse position: %r"
new "Museposition: %r"
# renpy/common/_developer/developer.rpym:341
old "Right-click or escape to quit."
new "Højreklik eller escape for at afslutte."
# renpy/common/_developer/developer.rpym:389
old "Type to filter: "
new "Skriv for at filtrere: "
# renpy/common/_developer/developer.rpym:507
old "Textures: [tex_count] ([tex_size_mb:.1f] MB)"
new "Teksturer: [tex_count] ([tex_size_mb:.1f] MB)"
# renpy/common/_developer/developer.rpym:511
old "Image cache: [cache_pct:.1f]% ([cache_size_mb:.1f] MB)"
new "Billedcache: [cache_pct:.1f]% ([cache_size_mb:.1f] MB)"
# renpy/common/_developer/developer.rpym:521
old "✔ "
new "✔ "
# renpy/common/_developer/developer.rpym:524
old "✘ "
new "✘ "
# renpy/common/_developer/developer.rpym:529
old "\n{color=#cfc}✔ predicted image (good){/color}\n{color=#fcc}✘ unpredicted image (bad){/color}\n{color=#fff}Drag to move.{/color}"
new "\n{color=#cfc}✔ forudset billede (godt){/color}\n{color=#fcc}✘ uforudset billede (dårligt){/color}\n{color=#fff}Træk for at flytte.{/color}"
# renpy/common/_developer/inspector.rpym:38
old "Displayable Inspector"
new "Inspektion af visbare elementer"
# renpy/common/_developer/inspector.rpym:61
old "Size"
new "Størrelse"
# renpy/common/_developer/inspector.rpym:65
old "Style"
new "Stil"
# renpy/common/_developer/inspector.rpym:71
old "Location"
new "Placering"
# renpy/common/_developer/inspector.rpym:122
old "Inspecting Styles of [displayable_name!q]"
new "Inspicerer stile for [displayable_name!q]"
# renpy/common/_developer/inspector.rpym:139
old "displayable:"
new "visbart element:"
# renpy/common/_developer/inspector.rpym:145
old " (no properties affect the displayable)"
new " (ingen egenskaber påvirker det visbare element)"
# renpy/common/_developer/inspector.rpym:147
old " (default properties omitted)"
new " (standardegenskaber udeladt)"
# renpy/common/_developer/inspector.rpym:185
old "<repr() failed>"
new "<repr() mislykkedes>"
# renpy/common/00console.rpy:537
old "Press <esc> to exit console. Type help for help.\n"
new "Tryk <esc> for at gå ud af konsol. Skriv help for hjælp.\n"
# renpy/common/00console.rpy:541
old "Ren'Py script enabled."
new "Ren'Py-script aktiveret" #manuskripterne eller scripting?
# renpy/common/00console.rpy:543
old "Ren'Py script disabled."
new "Ren'Py-script deaktiveret." #manuskripterne eller scripting?
# renpy/common/00console.rpy:793
old "help: show this help\n help <expr>: show signature and documentation of <expr>"
new "help: vis denne hjælp\n help <udtryk>: vis signaturen og dokumentationen for <udtryk>"
# renpy/common/00console.rpy:817
old "Help may display undocumented functions. Please check that the function or\nclass you want to use is documented.\n\n"
new "Hjælp viser muligvis udokumenterede funktioner. Tjek venligst, om den funktion\neller klasse, du gerne vil bruge, er dokumenteret.\n\n"
# renpy/common/00console.rpy:826
old "commands:\n"
new "kommandoer:\n"
# renpy/common/00console.rpy:836
old " <renpy script statement>: run the statement\n"
new " <renpy-script-ordre>: kør ordren\n"
# renpy/common/00console.rpy:838
old " <python expression or statement>: run the expression or statement"
new " <python-udtryk eller -ordre>: kør udtrykket eller ordren"
# renpy/common/00console.rpy:846
old "clear: clear the console history"
new "clear: ryd konsolhistorikken"
# renpy/common/00console.rpy:850
old "exit: exit the console"
new "exit: gå ud af konsollen"
# renpy/common/00console.rpy:858
old "stack: print the return stack"
new "stack: udskriv returstakken"
# renpy/common/00console.rpy:880
old "load <slot>: loads the game from slot"
new "load <plads>: indlæser spillet fra plads"
# renpy/common/00console.rpy:893
old "save <slot>: saves the game in slot"
new "save <plads>: gemmer spillet på plads"
# renpy/common/00console.rpy:904
old "reload: reloads the game, refreshing the scripts"
new "reload: genindlæser spiller og genopfrisker manuskripterne"
# renpy/common/00console.rpy:912
old "watch <expression>: watch a python expression\n watch short: makes the representation of traced expressions short (default)\n watch long: makes the representation of traced expressions as is"
new "watch <udtryk>: hold øje med et python-udtryk\n watch short: gør repræsentationen af sporede udtryk kort (standard)\n watch long: gør repræsentationen af sporede udtryk, som den er"
# renpy/common/00console.rpy:949
old "unwatch <expression>: stop watching an expression"
new "unwatch <udtryk>: stop med at holde øje med et udtryk"
# renpy/common/00console.rpy:995
old "unwatchall: stop watching all expressions"
new "unwatchall: stop med at holde øje med alle udtryk"
# renpy/common/00console.rpy:1016
old "jump <label>: jumps to label"
new "jump <label>: springer til mærkat"
# renpy/common/00console.rpy:1032
old "short: Shorten the representation of objects on the console (default)."
new "short: Forkort repræsentationen af objekter i konsollen (standard)."
# renpy/common/00console.rpy:1036
old "long: Print the full representation of objects on the console."
new "long: Skriv den fulde repræsentationen af objekter til konsollen."
# renpy/common/00console.rpy:1040
old "escape: Enables escaping of unicode symbols in unicode strings."
new "escape: Aktiverer omdannelsen af unicode-tegn til undvigesekvenser i unicode-strenge."
# renpy/common/00console.rpy:1044
old "unescape: Disables escaping of unicode symbols in unicode strings and print it as is (default)."
new "unescape: Deaktiverer omdannelsen af unicode-tegn til undvigesekvenser i unicode-strenge og skriver dem, som de er (standard)."
# renpy/common/_developer/developer.rpym:69
old "Show Translation Identifiers"
new "Vis oversættelsesidentifikatorer"
# renpy/common/_developer/developer.rpym:72
old "Hide Translation Identifiers"
new "Skjul oversættelsesidentifikatorer"
# renpy/common/_developer/developer.rpym:81
old "Show Filename and Line"
new "Vil filnavn og linje"
# renpy/common/_developer/developer.rpym:84
old "Hide Filename and Line"
new "Skjul filnavn og linje"
# renpy/common/_developer/developer.rpym:127
old "Layer [l]:"
new "Lag [l]:"
# renpy/common/_developer/developer.rpym:131
old " [name] [attributes] (hidden)"
new " [name] [attributes] (skjult)"
# renpy/common/_developer/developer.rpym:135
old " [name] [attributes]"
new " [name] [attributes]"
# renpy/common/_developer/developer.rpym:606
old "\n{color=#fff}Copied to clipboard.{/color}"
new "\n{color=#fff}Kopieret til udklipsholder.{/color}"
# renpy/common/_developer/developer.rpym:612
old "\n{color=#fff}Click to copy.\nDrag to move.{/color}"
new "\n{color=#fff}Klik for at kopiere.\nTræk for at flytte.{/color}"
# renpy/common/_developer/developer.rpym:657
old "Click to open in editor."
new "Klik for at åbne i tekstbehandler."
-202
View File
@@ -1,202 +0,0 @@
translate danish strings:
# renpy/common/00gltest.rpy:89
old "Renderer"
new "Optegner"
# renpy/common/00gltest.rpy:93
old "Automatically Choose"
new "Vælg automatisk"
# renpy/common/00gltest.rpy:100
old "Force GL Renderer"
new "Påtving GL-optegner"
# renpy/common/00gltest.rpy:105
old "Force ANGLE Renderer"
new "Påtving ANGLE-optegner"
# renpy/common/00gltest.rpy:110
old "Force GLES Renderer"
new "Påtving GLES-optegner"
# renpy/common/00gltest.rpy:116
old "Force GL2 Renderer"
new "Påtving GL2-optegner"
# renpy/common/00gltest.rpy:121
old "Force ANGLE2 Renderer"
new "Påtving ANGLE2-optegner"
# renpy/common/00gltest.rpy:126
old "Force GLES2 Renderer"
new "Påtving GLES2-optegner"
# renpy/common/00gltest.rpy:136
old "Enable (No Blocklist)"
new "Aktivér (ingen blocklist)"
# renpy/common/00gltest.rpy:159
old "Powersave"
new "Strømsparing"
# renpy/common/00gltest.rpy:173
old "Framerate"
new "Billedhastighed"
# renpy/common/00gltest.rpy:177
old "Screen"
new "Skærm"
# renpy/common/00gltest.rpy:181
old "60"
new "60"
# renpy/common/00gltest.rpy:185
old "30"
new "30"
# renpy/common/00gltest.rpy:191
old "Tearing"
new "Tearing (overrivning)"
# renpy/common/00gltest.rpy:207
old "Changes will take effect the next time this program is run."
new "Ændringer træder i kraft, næste gang dette program køres."
# renpy/common/00gltest.rpy:242
old "Performance Warning"
new "Ydeevneadvarsel"
# renpy/common/00gltest.rpy:247
old "This computer is using software rendering."
new "Denne computer bruger softwareoptegning."
# renpy/common/00gltest.rpy:249
old "This game requires use of GL2 that can't be initialised."
new "Dette spil kræver brugen af GL2, som ikke kan initialiseres."
# renpy/common/00gltest.rpy:251
old "This computer has a problem displaying graphics: [problem]."
new "Denne computer har et problem med visning af grafik: [problem]."
# renpy/common/00gltest.rpy:255
old "Its graphics drivers may be out of date or not operating correctly. This can lead to slow or incorrect graphics display."
new "Dens grafikdrivere er muligvis forældede eller fungerer ikke korrekt. Dette kan føre til langsom eller ukorrkekt grafikvisning."
# renpy/common/00gltest.rpy:259
old "The {a=edit:1:log.txt}log.txt{/a} file may contain information to help you determine what is wrong with your computer."
new "Filen {a=edit:1:log.txt}log.txt{/a} indeholder muligvis information, der kan hjælpe dig med at bestemme, hvad der er galt med din computer."
# renpy/common/00gltest.rpy:264
old "More details on how to fix this can be found in the {a=[url]}documentation{/a}."
new "Flere detaljer om, hvordan du fikser dette, kan findes i {a=[url]}dokumentationen{/a}."
# renpy/common/00gltest.rpy:269
old "Continue, Show this warning again"
new "Fortsæt, og vis denne advarsel igen"
# renpy/common/00gltest.rpy:273
old "Continue, Don't show warning again"
new "Fortsæt, og vis ikke advarsel igen"
# renpy/common/00gltest.rpy:281
old "Change render options"
new "Ændr optegningsindstillinger"
# renpy/common/00gamepad.rpy:32
old "Select Gamepad to Calibrate"
new "Vælg spillekontrol at kalibrere"
# renpy/common/00gamepad.rpy:35
old "No Gamepads Available"
new "Ingen spillekontroller tilgængelige"
# renpy/common/00gamepad.rpy:54
old "Calibrating [name] ([i]/[total])"
new "Kalibrerer [name] ([i]/[total])"
# renpy/common/00gamepad.rpy:58
old "Press or move the '[control!s]' [kind]."
new "Tryk eller bevæg '[control!s]' [kind]."
# renpy/common/00gamepad.rpy:68
old "Skip (A)"
new "Spring over (A)"
# renpy/common/00gamepad.rpy:71
old "Back (B)"
new "Tilbage (B)"
# renpy/common/_errorhandling.rpym:555
old "Open"
new "Åbn"
# renpy/common/_errorhandling.rpym:557
old "Opens the traceback.txt file in a text editor."
new "Åbner traceback.txt-filen i en tekstbehandler."
# renpy/common/_errorhandling.rpym:559
old "Copy BBCode"
new "Kopiér BBCode"
# renpy/common/_errorhandling.rpym:561
old "Copies the traceback.txt file to the clipboard as BBcode for forums like https://lemmasoft.renai.us/."
new "Kopierer traceback.txt-filen til udkplipsholderen som BBCode til forummer som https://lemmasoft.renai.us/."
# renpy/common/_errorhandling.rpym:563
old "Copy Markdown"
new "Kopiér Markdown"
# renpy/common/_errorhandling.rpym:565
old "Copies the traceback.txt file to the clipboard as Markdown for Discord."
new "Kopierer traceback.txt-filen til udkplipsholderen som Markdown til Discord."
# renpy/common/_errorhandling.rpym:594
old "An exception has occurred."
new "En undtagelse er opstået."
# renpy/common/_errorhandling.rpym:617
old "Rollback"
new "Tilbagerulning"
# renpy/common/_errorhandling.rpym:619
old "Attempts a roll back to a prior time, allowing you to save or choose a different choice."
new "Forsøger en tilbagerulning til et forrigt tidspunkt og gør det muligt for dig at gemme eller træffe et andet valg."
# renpy/common/_errorhandling.rpym:622
old "Ignore"
new "Ignorer"
# renpy/common/_errorhandling.rpym:626
old "Ignores the exception, allowing you to continue."
new "Ignorerer undtagelsen og gør dig i stand til at fortsætte."
# renpy/common/_errorhandling.rpym:628
old "Ignores the exception, allowing you to continue. This often leads to additional errors."
new "Ignorerer undtagelsen og gør dig i stand til at fortsætte. Dette fører ofte til yderligere fejl."
# renpy/common/_errorhandling.rpym:632
old "Reload"
new "Genindlæs"
# renpy/common/_errorhandling.rpym:634
old "Reloads the game from disk, saving and restoring game state if possible."
new "Genindlæser spillet fra disken, gemmer og gendanner spilstatussen hvis muligt."
# renpy/common/_errorhandling.rpym:637
old "Console"
new "Konsol"
# renpy/common/_errorhandling.rpym:639
old "Opens a console to allow debugging the problem."
new "Åbner en konsol for at muliggøre problemløsning."
# renpy/common/_errorhandling.rpym:652
old "Quits the game."
new "Afslutter spillet."
# renpy/common/_errorhandling.rpym:673
old "Parsing the script failed."
new "Fortolkning af manuskript mislykkedes."
-442
View File
@@ -1,442 +0,0 @@
translate danish strings:
# gui/game/gui.rpy:2
old "## Initialization"
new "## Initialisering"
# gui/game/gui.rpy:5
old "## The init offset statement causes the initialization statements in this file to run before init statements in any other file."
new "## Erklæringen init offset får initialiseringserklæringerne i denne fil til at køre før init-erklæringer i enhver anden fil."
# gui/game/gui.rpy:9
old "## Calling gui.init resets the styles to sensible default values, and sets the width and height of the game."
new "## Ved at kalde gui.init nulstilles stilene til fornuftige standardværdier, og bredden og højden af spillet indstilles."
# gui/game/gui.rpy:14
old "## Enable checks for invalid or unstable properties in screens or transforms"
new "## Aktiver tjek efter ugyldige eller ustabile egenskaber i skærme eller transformationer"
# gui/game/gui.rpy:19
old "## GUI Configuration Variables"
new "## GUI-konfigurationsvariabler"
# gui/game/gui.rpy:23
old "## Colors"
new "## Farver"
# gui/game/gui.rpy:25
old "## The colors of text in the interface."
new "## Farverne på tekst i skærmfladen."
# gui/game/gui.rpy:27
old "## An accent color used throughout the interface to label and highlight text."
new "## En accentfarve brugt gennem hele skærmfladen til at markere og fremhæve tekst."
# gui/game/gui.rpy:31
old "## The color used for a text button when it is neither selected nor hovered."
new "## Farven brugt til en tekstknap, når den hverken er valgt eller holdes henover."
# gui/game/gui.rpy:34
old "## The small color is used for small text, which needs to be brighter/darker to achieve the same effect."
new "## Small-farven bruges til lille tekst, som skal være lysere/mørkere for at opnå samme effekt."
# gui/game/gui.rpy:38
old "## The color that is used for buttons and bars that are hovered."
new "## Farven, der bruges til knapper og bjælker, hvor markøren holdes henover."
# gui/game/gui.rpy:41
old "## 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."
new "## Farven, der bruges til en tekstknap, når den er valgt men ikke i fokus. En knap er valgt, hvis den er den nuværende skærm- eller præferenceværdi."
# gui/game/gui.rpy:45
old "## The color used for a text button when it cannot be selected."
new "## Farven, der bruges til en tekstknap, når den ikke kan vælges."
# gui/game/gui.rpy:48
old "## 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."
new "## Farven, der bruges til de dele af bjælker, der ikke er udfyldt. Disse bruges ikke direkte, men bruges, når bjælkebilledfilerne regenereres."
# gui/game/gui.rpy:53
old "## The colors used for dialogue and menu choice text."
new "## Farverne brugt til dialogtekst og tekst i menupunkter."
# gui/game/gui.rpy:58
old "## Fonts and Font Sizes"
new "## Skrifttyper og -størrelser"
# gui/game/gui.rpy:60
old "## The font used for in-game text."
new "## Skrifttypen brugt til tekst i spillet."
# gui/game/gui.rpy:63
old "## The font used for character names."
new "## Skrifttypen brugt til figurnavne."
# gui/game/gui.rpy:66
old "## The font used for out-of-game text."
new "## Skrifttypen brugt til tekst uden for spillet."
# gui/game/gui.rpy:69
old "## The size of normal dialogue text."
new "## Størrelsen på normal dialogtekst."
# gui/game/gui.rpy:72
old "## The size of character names."
new "## Størrelsen på figurnavne."
# gui/game/gui.rpy:75
old "## The size of text in the game's user interface."
new "## Størrelsen på tekst i spillets brugerflade."
# gui/game/gui.rpy:78
old "## The size of labels in the game's user interface."
new "## Størrelsen på mærkater i spillets brugeflade."
# gui/game/gui.rpy:81
old "## The size of text on the notify screen."
new "## Størrelsen på teksten på notifikationsskærmen."
# gui/game/gui.rpy:84
old "## The size of the game's title."
new "## Størrelsen på spillets titel."
# gui/game/gui.rpy:88
old "## Main and Game Menus"
new "## Hoved- og spilmenuer"
# gui/game/gui.rpy:90
old "## The images used for the main and game menus."
new "## Billederne brugt til hoved- og spilmenuerne."
# gui/game/gui.rpy:95
old "## Dialogue"
new "## Dialog"
# gui/game/gui.rpy:97
old "## These variables control how dialogue is displayed on the screen one line at a time."
new "## Disse variabler kontrollerer, hvordan dialog vises på skærmen én linje ad gangen."
# gui/game/gui.rpy:100
old "## The height of the textbox containing dialogue."
new "## Højden af tekstboksen, der indeholder dialog."
# gui/game/gui.rpy:103
old "## The placement of the textbox vertically on the screen. 0.0 is the top, 0.5 is center, and 1.0 is the bottom."
new "## Placeringen af tekstboksen lodret på skærmen. 0.0 er i toppen, 0.5 er midti og 1.0 er i bunden."
# gui/game/gui.rpy:108
old "## 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."
new "## Placeringen af den talende figurs navn relativt til tekstboksen. Disse kan være et helt antal af pixels fra venstre eller toppen, eller 0.5 for centrering."
# gui/game/gui.rpy:113
old "## 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."
new "## Den vandrette justering af figurens navn. Denne kan være 0.0 for venstrejusteret, 0.5 for centreret og 1.0 for højrejusteret."
# gui/game/gui.rpy:117
old "## The width, height, and borders of the box containing the character's name, or None to automatically size it."
new "## Bredden, højden og kanterne på boksen, der indeholder figurens navn, eller None for automatisk at indstille størrelsen."
# gui/game/gui.rpy:122
old "## The borders of the box containing the character's name, in left, top, right, bottom order."
new "## Kanterne på boksen, der indeholder figurens navn, i rækkefølgen venstre, top, højre, bund."
# gui/game/gui.rpy:126
old "## If True, the background of the namebox will be tiled, if False, the background of the namebox will be scaled."
new "## Ved True bliver navneboksens baggrund gentaget. Ved False bliver navneboksens baggrund skaleret."
# gui/game/gui.rpy:131
old "## 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."
new "## Placeringen af dialogen relativt til tekstboksen. Disse kan være et heltal af pixels relativt til venstre eller øverste side af tekstboksen, eller 0.5 for at centrere."
# gui/game/gui.rpy:137
old "## The maximum width of dialogue text, in pixels."
new "## Den maksimale bredde af dialogtekst i pixels."
# gui/game/gui.rpy:140
old "## 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."
new "## Den vandrette justering af dialogteksten. Dette kan være 0.0 for venstrejusteret, 0.5 for centreret og 1.0 for højrejusteret."
# gui/game/gui.rpy:145
old "## Buttons"
new "## Knapper"
# gui/game/gui.rpy:147
old "## These variables, along with the image files in gui/button, control aspects of how buttons are displayed."
new "## Disse variable kontrollerer sammen med billedfilerne i gui/button aspekter af, hvordan knapper vises."
# gui/game/gui.rpy:150
old "## The width and height of a button, in pixels. If None, Ren'Py computes a size."
new "## Bredden og højden af en knap i pixels. Hvis None beregner Ren'Py en størrelse."
# gui/game/gui.rpy:154
old "## The borders on each side of the button, in left, top, right, bottom order."
new "## Kanterne på hver side af knappen i rækkefølgen venstre, top, højre, bund."
# gui/game/gui.rpy:157
old "## If True, the background image will be tiled. If False, the background image will be linearly scaled."
new "## Ved True gentages baggrundsbilledet. Ved False bliver baggrundsbilledet skaleret lineært."
# gui/game/gui.rpy:161
old "## The font used by the button."
new "## Skrifttypen brugt af knappen."
# gui/game/gui.rpy:164
old "## The size of the text used by the button."
new "## Størrelsen af tekst brugt af knappen."
# gui/game/gui.rpy:167
old "## The color of button text in various states."
new "## Farven af knappen i forskellige tilstande."
# gui/game/gui.rpy:173
old "## The horizontal alignment of the button text. (0.0 is left, 0.5 is center, 1.0 is right)."
new "## Den vandrette justering af knapteksten. (0.0 er venstre, 0.5 er midtfor, 1.0 er højre)."
# gui/game/gui.rpy:178
old "## 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."
new "## Disse variabler tilsidesætter indstillinger for forskellige typer knapper. Se venligst GUI-dokumentationen for typen af knapper, der er tilgængelige, og hvad hver af dem bruges til."
# gui/game/gui.rpy:182
old "## These customizations are used by the default interface:"
new "## Disse tilpasninger bruger af standardgrænsefladen:"
# gui/game/gui.rpy:197
old "## 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."
new "## Du kan også tilføje dine egne tilpasninger ved at tilføje variabler med det korrekte navn. For eksempel kan du afkommentere følgende linje for at angive bredden på en navigationsknap."
# gui/game/gui.rpy:204
old "## Choice Buttons"
new "## Valgmulighedsknapper"
# gui/game/gui.rpy:206
old "## Choice buttons are used in the in-game menus."
new "## Valgmulighedsknapper bruges i menuerne i spillet."
# gui/game/gui.rpy:220
old "## File Slot Buttons"
new "## Filpladsknapper"
# gui/game/gui.rpy:222
old "## 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."
new "## En filpladsknap er en speciel slags knap. Den indeholder et miniaturebillede og tekst, der beskriver indholdet af gemmepladsen. En gemmeplads bruger billedfiler i gui/button ligesom de andre slags knapper."
# gui/game/gui.rpy:226
old "## The save slot button."
new "## Gemmepladsknappen."
# gui/game/gui.rpy:236
old "## The width and height of thumbnails used by the save slots."
new "## Bredden og højden af miniaturebilleder brugt af gemmepladserne."
# gui/game/gui.rpy:240
old "## The number of columns and rows in the grid of save slots."
new "## Antallet af søjler og rækker i gitteret med gemmepladser."
# gui/game/gui.rpy:245
old "## Positioning and Spacing"
new "## Positionering og afstand"
# gui/game/gui.rpy:247
old "## These variables control the positioning and spacing of various user interface elements."
new "## Disse variable kontrollerer placering af og afstanden mellem forskellige brugerfladeelementer."
# gui/game/gui.rpy:250
old "## The position of the left side of the navigation buttons, relative to the left side of the screen."
new "## Placeringen af venstre side af navigationsknapperne, relativt til venstre side af skærmen."
# gui/game/gui.rpy:254
old "## The vertical position of the skip indicator."
new "## Den lodrette placering af overspringningsindikatoren."
# gui/game/gui.rpy:257
old "## The vertical position of the notify screen."
new "## Den lodrette placering af notifikationsskærmen."
# gui/game/gui.rpy:260
old "## The spacing between menu choices."
new "## Afstanden mellem menuvalg."
# gui/game/gui.rpy:263
old "## Buttons in the navigation section of the main and game menus."
new "## Knapper i navigationssektionen af hoved- og spilmenuerne."
# gui/game/gui.rpy:266
old "## Controls the amount of spacing between preferences."
new "## Kontrollerer mængden af afstand mellem præferencer."
# gui/game/gui.rpy:269
old "## Controls the amount of spacing between preference buttons."
new "## Kontrollerer mængden af afstand mellem præferenceknapper."
# gui/game/gui.rpy:272
old "## The spacing between file page buttons."
new "## Afstanden mellem filsideknapper."
# gui/game/gui.rpy:275
old "## The spacing between file slots."
new "## Afstanden mellem filpladser."
# gui/game/gui.rpy:278
old "## The position of the main menu text."
new "## Placeringen af hovedmenuteksten."
# gui/game/gui.rpy:282
old "## Frames"
new "## Rammer"
# gui/game/gui.rpy:284
old "## These variables control the look of frames that can contain user interface components when an overlay or window is not present."
new "## Disse variable kontrollerer udseendet på rammer, der kan indeholde brugefladekomponenter, når en overlejring eller et vindue ikke er til stede."
# gui/game/gui.rpy:287
old "## Generic frames."
new "## Generiske rammer."
# gui/game/gui.rpy:290
old "## The frame that is used as part of the confirm screen."
new "## Rammen, der bruges som del af bekræftelsesskærmen."
# gui/game/gui.rpy:293
old "## The frame that is used as part of the skip screen."
new "## Rammen, der bruges som del af overspringningsskærmen."
# gui/game/gui.rpy:296
old "## The frame that is used as part of the notify screen."
new "## Rammen, der bruges som del af notifikationsskærmen."
# gui/game/gui.rpy:299
old "## Should frame backgrounds be tiled?"
new "## Bør rammebaggrunde gentages?"
# gui/game/gui.rpy:303
old "## Bars, Scrollbars, and Sliders"
new "## Bjælker, rullebjælker og skydere"
# gui/game/gui.rpy:305
old "## These control the look and size of bars, scrollbars, and sliders."
new "## Disse kontrollerer udseendet og størrelsen på bjælker, rullebjælker og skydere."
# gui/game/gui.rpy:307
old "## The default GUI only uses sliders and vertical scrollbars. All of the other bars are only used in creator-written screens."
new "## Standard-GUI'en bruger kun skydere og lodrette rullebjælker. Alle de andre bjælker bruges kun i skærme skrevet af spiludvikleren."
# gui/game/gui.rpy:310
old "## The height of horizontal bars, scrollbars, and sliders. The width of vertical bars, scrollbars, and sliders."
new "## Højden på vandrette bjælker, rullebjælker og skydere. Bredden på lodrette bjælker, rullebjælker og skydere."
# gui/game/gui.rpy:316
old "## True if bar images should be tiled. False if they should be linearly scaled."
new "## True, hvis bjælkebilleder skal gentages. False, hvis de skal skaleres lineært."
# gui/game/gui.rpy:321
old "## Horizontal borders."
new "## Vandrette kanter."
# gui/game/gui.rpy:326
old "## Vertical borders."
new "## Lodrette kanter."
# gui/game/gui.rpy:331
old "## What to do with unscrollable scrollbars in the gui. \"hide\" hides them, while None shows them."
new "## Hvad der skal gøres ved urullelige rullebjælker i gui'en. \"hide\" skjuler dem, mens None viser dem."
# gui/game/gui.rpy:336
old "## History"
new "## Historik"
# gui/game/gui.rpy:338
old "## The history screen displays dialogue that the player has already dismissed."
new "## Historikskærmen viser dialog, som spilleren allerede er gået videre fra."
# gui/game/gui.rpy:340
old "## The number of blocks of dialogue history Ren'Py will keep."
new "## Antallet af blokke dialoghistorik, som Ren'Py gemmer."
# gui/game/gui.rpy:343
old "## The height of a history screen entry, or None to make the height variable at the cost of performance."
new "## Højden på et historikskærmsindlæg, eller None for at gøre højden variabel på bekostning af ydeevne."
# gui/game/gui.rpy:347
old "## The position, width, and alignment of the label giving the name of the speaking character."
new "## Placeringen, bredden og justeringen af mærkatet, der angiver navnet på den talende figur."
# gui/game/gui.rpy:354
old "## The position, width, and alignment of the dialogue text."
new "## Placeringen, bredden og justeringen af dialogteksten."
# gui/game/gui.rpy:361
old "## NVL-Mode"
new "## NVL-tilstand"
# gui/game/gui.rpy:363
old "## The NVL-mode screen displays the dialogue spoken by NVL-mode characters."
new "## NVL-tilstandsskærmen viser dialogen sagt af NVL-tilstandsfigurer."
# gui/game/gui.rpy:365
old "## The borders of the background of the NVL-mode background window."
new "## Kanterne på baggrunden af baggrundsvinduet i NVL-tilstand."
# gui/game/gui.rpy:368
old "## 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."
new "## Det maksimale antal af NVL-tilstandsindlæg, som Ren'Py viser. Når flere indlæg end disse skal vises, bliver det ældste indlæg fjernet."
# gui/game/gui.rpy:372
old "## The height of an NVL-mode entry. Set this to None to have the entries dynamically adjust height."
new "## Højden på et NVL-tilstandsindlæg. Stil denne til None for at få indlæggene til dynamisk at justere højde."
# gui/game/gui.rpy:376
old "## The spacing between NVL-mode entries when gui.nvl_height is None, and between NVL-mode entries and an NVL-mode menu."
new "## Afstanden mellem NVL-tilstandsindlæg, når gui.nvl_height er None, og mellem NVL-tilstandsindlæg og en menu i NVL-tilstand."
# gui/game/gui.rpy:393
old "## The position, width, and alignment of nvl_thought text (the text said by the nvl_narrator character.)"
new "## Placeringen, bredden og justeringen af nvl_thought-tekst (teksten sagt af figuren nvl_narrator.)"
# gui/game/gui.rpy:400
old "## The position of nvl menu_buttons."
new "## Placeringen af nvl menu_buttons."
# gui/game/gui.rpy:405
old "## Localization"
new "## Lokalisering"
# gui/game/gui.rpy:407
old "## 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"
new "## Dette kontrollerer, hvor et linjeskift er tilladt. Standardinstilligen er egnet til de fleste sprog. En liste over tilgængelige værdier kan findes på https://www.renpy.org/doc/html/style_properties.html#style-property-language"
# gui/game/gui.rpy:415
old "## Mobile devices"
new "## Mobilenheder"
# gui/game/gui.rpy:420
old "## This increases the size of the quick buttons to make them easier to touch on tablets and phones."
new "## Dette øger størrelsen på hurtigknapperne for at gøre dem lettere at ramme på tablets og mobiler."
# gui/game/gui.rpy:427
old "## This changes the size and spacing of various GUI elements to ensure they are easily visible on phones."
new "## Dette ændrer størrelsen på og afstanden mellem forskellige GUI-elementer for at sørge for, at de er let synlige på mobiler."
# gui/game/gui.rpy:432
old "## Font sizes."
new "## Skriftstørrelser."
# gui/game/gui.rpy:440
old "## Adjust the location of the textbox."
new "## Juster tekstboksens placering."
# gui/game/gui.rpy:446
old "## Change the size and spacing of various things."
new "## Ændr størrelsen på og afstanden mellem forskellige ting."
# gui/game/gui.rpy:460
old "## File button layout."
new "## Filknapudformning."
# gui/game/gui.rpy:464
old "## NVL-mode."
new "## NVL-tilstand."
# gui/game/gui.rpy:347
old "## Additional space to add between history screen entries."
new "## Yderligere afstand at tilføje mellem historikskærmsindlæg."
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More