summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Xu <alex_y_xu@yahoo.ca>2014-03-30 13:42:48 -0400
committerAlex Xu <alex_y_xu@yahoo.ca>2014-03-30 14:03:21 -0400
commitffebbcd2dee04b8c06e90432618e0e013ac5b7dc (patch)
tree7d7d0125b38c67592eb3252b580ecadb6b752512
parent3adce8c047fb433702223e3fca1c5463f98e4805 (diff)
downloadhtml5ks-ffebbcd2dee04b8c06e90432618e0e013ac5b7dc.tar.xz
html5ks-ffebbcd2dee04b8c06e90432618e0e013ac5b7dc.zip
unrpyc -> ast2json
-rw-r--r--.gitignore4
-rw-r--r--.travis.yml2
-rw-r--r--Makefile185
-rw-r--r--README.rst7
-rw-r--r--ast2json/.gitignore2
-rw-r--r--ast2json/Makefile294
-rw-r--r--ast2json/ast2json.py62
-rw-r--r--ast2json/decompiler.py500
-rw-r--r--ast2json/renpy/LICENSE.txt (renamed from unrpyc/renpy/LICENSE.txt)0
-rw-r--r--ast2json/renpy/__init__.py (renamed from unrpyc/renpy/__init__.py)0
-rw-r--r--ast2json/renpy/ast.py (renamed from unrpyc/renpy/ast.py)2
-rw-r--r--ast2json/renpy/atl.py (renamed from unrpyc/renpy/atl.py)2
-rw-r--r--ast2json/renpy/game.py (renamed from unrpyc/renpy/game.py)2
-rw-r--r--ast2json/renpy/object.py (renamed from unrpyc/renpy/object.py)0
-rwxr-xr-xast2json/rpyc2json.py98
-rwxr-xr-xast2json/script2json.py147
-rwxr-xr-xconfigure55
-rw-r--r--unrpyc/README14
-rw-r--r--unrpyc/decompiler.py493
-rwxr-xr-xunrpyc/find-gzip.sh11
-rwxr-xr-xunrpyc/fix.js6
-rw-r--r--unrpyc/renpy/display/__init__.py31
-rw-r--r--unrpyc/renpy/display/accelerator.pyx296
-rw-r--r--unrpyc/renpy/display/anim.py634
-rw-r--r--unrpyc/renpy/display/behavior.py1531
-rw-r--r--unrpyc/renpy/display/core.py2463
-rw-r--r--unrpyc/renpy/display/dragdrop.py731
-rw-r--r--unrpyc/renpy/display/error.py159
-rw-r--r--unrpyc/renpy/display/focus.py449
-rw-r--r--unrpyc/renpy/display/im.py1562
-rw-r--r--unrpyc/renpy/display/image.py397
-rw-r--r--unrpyc/renpy/display/imagelike.py382
-rw-r--r--unrpyc/renpy/display/imagemap.py233
-rw-r--r--unrpyc/renpy/display/joystick.py126
-rw-r--r--unrpyc/renpy/display/layout.py1744
-rw-r--r--unrpyc/renpy/display/minigame.py25
-rw-r--r--unrpyc/renpy/display/module.py275
-rw-r--r--unrpyc/renpy/display/motion.py1526
-rw-r--r--unrpyc/renpy/display/movetransition.py640
-rw-r--r--unrpyc/renpy/display/particle.py615
-rw-r--r--unrpyc/renpy/display/pgrender.py167
-rw-r--r--unrpyc/renpy/display/predict.py156
-rw-r--r--unrpyc/renpy/display/presplash.py113
-rw-r--r--unrpyc/renpy/display/render.pxd47
-rw-r--r--unrpyc/renpy/display/render.pyx1174
-rw-r--r--unrpyc/renpy/display/scale.py109
-rw-r--r--unrpyc/renpy/display/screen.py639
-rw-r--r--unrpyc/renpy/display/swdraw.py1102
-rw-r--r--unrpyc/renpy/display/transition.py922
-rw-r--r--unrpyc/renpy/display/video.py234
-rw-r--r--unrpyc/unrpyc.py97
51 files changed, 1236 insertions, 19229 deletions
diff --git a/.gitignore b/.gitignore
index 7d6b447..de34822 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,8 +3,8 @@
/www/json
/www/dump
/rpy
-/unrpyc/*.json*
-/unrpyc/*.rpyc
+/ast2json/*.json*
+/ast2json/*.rpyc
# generated files
/www/js/all.min.js*
diff --git a/.travis.yml b/.travis.yml
index c9b77ea..55a2bb6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,7 @@ language: c
python: '3.3'
node_js: '0.11'
env: MINIMAL=1 CFLAGS="-O2 -march=native -pipe" CXXFLAGS="-O2 -march=native -pipe"
- MAKEOPTS="-j8" FFMPEGFLAGS="-t 00:00:05"
+ MAKEOPTS="-j4" FFMPEGFLAGS="-t 00:00:05"
before_install: ./.travis.sh before_install
install: ./.travis.sh install
script: ./.travis.sh script
diff --git a/Makefile b/Makefile
index 48d7503..adfaf4e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,38 +1,17 @@
null :=
SPACE := $(null) $(null)
-FFMPEG ?= ffmpeg
-FFMPEG := $(FFMPEG) -v warning -y $(FFMPEGFLAGS)
-CWEBP ?= cwebp
-CWEBP := $(CWEBP) -quiet -alpha_cleanup -m 6 $(CWEBPFLAGS)
-WEBPMUX ?= webpmux
-WEBPMUX := $(WEBPMUX) $(WEBPMUXFLAGS)
-CONVERT ?= convert
-CONVERT := $(CONVERT) $(CONVERTFLAGS)
-APNGASM ?= apngasm
-APNGASM := $(APNGASM) $(APNGASMFLAGS)
-NPM ?= npm
-NPM := $(NPM) --quiet $(NPMFLAGS)
-JS_COMPRESSOR ?= uglifyjs
-LOCAL_UGLIFYJS := node_modules/.bin/uglifyjs
-UGLIFYJS ?= $(LOCAL_UGLIFYJS)
-UGLIFYJS := $(UGLIFYJS) $(UGLIFYJSFLAGS)
-PACKR ?= packr
-CLOSURE_COMPILER ?= java -jar compiler.jar
-ifndef MINIMAL
-ZOPFLIPNG ?= zopflipng
-ZOPFLIPNG := $(ZOPFLIPNG) $(ZOPFLIPNGFLAGS)
-#DEFLOPT ?= wine DeflOpt
-#DEFLOPT := $(DEFLOPT) $(DEFLOPTFLAGS)
-DEFLUFF ?= defluff
-DEFLUFF := $(DEFLUFF) $(DEFLUFFFLAGS)
-PNGQUANT ?= pngquant
-PNGQUANT := $(PNGQUANT) $(PNGQUANTFLAGS)
-endif
-GZIP := $(shell unrpyc/find-gzip.sh)
-
all: js json video audio images
+Makefile: Makefile.inc
+
+Makefile.inc: ./configure
+ $^ $@
+
+include Makefile.inc
+
+UGLIFYJS := node_modules/.bin/uglifyjs $(UGLIFYJSFLAGS)
+
# === JS ===
MYJS := www/js/html5ks.js www/js/menu.js www/js/api.js \
@@ -45,22 +24,10 @@ JSOUT := www/js/all.min.js
js: $(JSOUT)
-ifeq ($(JS_COMPRESSOR), uglifyjs)
-$(JSOUT): $(JS) $(UGLIFYJS)
- $(UGLIFYJS) $(JS) -o "$@" --source-map "$@".map --source-map-url ./all.min.js.map --screw-ie8 -p 2 -m -c unsafe=true,drop_debugger=false
-else
-$(JSOUT): $(JS)
-# note that packr doesn't actually work
-ifeq ($(JS_COMPRESSOR), packr)
- $(PACKR) $(JS) -o "$@"
-else
-ifeq ($(JS_COMPRESSOR), closure_compiler)
- $(CLOSURE_COMPILER) --compilation_level SIMPLE_OPTIMIZATIONS --create_source_map "$@".map --js $(subst $(SPACE), --js ,$(JS)) --js_output_file "$@"
-endif # ($(JS_COMPRESSOR), closure_compiler)
-endif # ($(JS_COMPRESSOR), packr)
-endif # ($(JS_COMPRESSOR), uglifyjs)
-
-$(LOCAL_UGLIFYJS): package.json
+$(JSOUT): $(UGLIFYJS) $(JS)
+ $^ -o "$@" --source-map "$@".map --source-map-url ./all.min.js.map --screw-ie8 -p 2 -m -c unsafe=true,drop_debugger=false
+
+$(UGLIFYJS): package.json
$(NPM) update
touch "$@"
@@ -72,78 +39,64 @@ www/js/lib/when.js: when
export PYTHON=python2; cd when && $(NPM) update && $(NPM) run browserify-debug
# === JSON ===
-JSON := www/json/script.json www/json/script.json.gz www/json/imachine.json \
- www/json/imachine.json.gz www/json/ui-strings.json \
- www/json/ui-strings.json.gz www/json/ui-strings_FR.json \
- www/json/ui-strings_FR.json.gz
-
-RPYC := unrpyc/script-a1-friday_FR.rpyc unrpyc/script-a1-friday.rpyc \
- unrpyc/script-a1-monday_FR.rpyc unrpyc/script-a1-monday.rpyc \
- unrpyc/script-a1-saturday_FR.rpyc unrpyc/script-a1-saturday.rpyc \
- unrpyc/script-a1-sunday_FR.rpyc unrpyc/script-a1-sunday.rpyc \
- unrpyc/script-a1-thursday_FR.rpyc unrpyc/script-a1-thursday.rpyc \
- unrpyc/script-a1-tuesday_FR.rpyc unrpyc/script-a1-tuesday.rpyc \
- unrpyc/script-a1-wednesday_FR.rpyc unrpyc/script-a1-wednesday.rpyc \
- unrpyc/script-a2-emi_FR.rpyc unrpyc/script-a2-emi.rpyc \
- unrpyc/script-a2-hanako_FR.rpyc unrpyc/script-a2-hanako.rpyc \
- unrpyc/script-a2-lilly_FR.rpyc unrpyc/script-a2-lilly.rpyc \
- unrpyc/script-a2-rin_FR.rpyc unrpyc/script-a2-rin.rpyc \
- unrpyc/script-a2-shizune_FR.rpyc unrpyc/script-a2-shizune.rpyc \
- unrpyc/script-a3-emi_FR.rpyc unrpyc/script-a3-emi.rpyc \
- unrpyc/script-a3-hanako_FR.rpyc unrpyc/script-a3-hanako.rpyc \
- unrpyc/script-a3-lilly_FR.rpyc unrpyc/script-a3-lilly.rpyc \
- unrpyc/script-a3-rin_FR.rpyc unrpyc/script-a3-rin.rpyc \
- unrpyc/script-a3-shizune_FR.rpyc unrpyc/script-a3-shizune.rpyc \
- unrpyc/script-a4-emi_FR.rpyc unrpyc/script-a4-emi.rpyc \
- unrpyc/script-a4-hanako_FR.rpyc unrpyc/script-a4-hanako.rpyc \
- unrpyc/script-a4-lilly_FR.rpyc unrpyc/script-a4-lilly.rpyc \
- unrpyc/script-a4-rin_FR.rpyc unrpyc/script-a4-rin.rpyc \
- unrpyc/script-a4-shizune_FR.rpyc unrpyc/script-a4-shizune.rpyc
-SJSON := $(patsubst unrpyc/%.rpyc,www/json/%.json,$(RPYC))
-JSONO := $(patsubst unrpyc/%.rpyc,unrpyc/%.json.o,%(RPYC))
+ORPYC := ast2json/imachine.rpyc ast2json/imachine_replay.rpyc \
+ ast2json/ui-strings.rpyc ast2json/ui-strings_FR.rpyc
+OJSON := $(ORPYC:ast2json/%.rpyc=www/json/%.json)
+
+SRPYC := ast2json/script-a1-friday_FR.rpyc ast2json/script-a1-friday.rpyc \
+ ast2json/script-a1-monday_FR.rpyc ast2json/script-a1-monday.rpyc \
+ ast2json/script-a1-saturday_FR.rpyc ast2json/script-a1-saturday.rpyc \
+ ast2json/script-a1-sunday_FR.rpyc ast2json/script-a1-sunday.rpyc \
+ ast2json/script-a1-thursday_FR.rpyc ast2json/script-a1-thursday.rpyc \
+ ast2json/script-a1-tuesday_FR.rpyc ast2json/script-a1-tuesday.rpyc \
+ ast2json/script-a1-wednesday_FR.rpyc ast2json/script-a1-wednesday.rpyc \
+ ast2json/script-a2-emi_FR.rpyc ast2json/script-a2-emi.rpyc \
+ ast2json/script-a2-hanako_FR.rpyc ast2json/script-a2-hanako.rpyc \
+ ast2json/script-a2-lilly_FR.rpyc ast2json/script-a2-lilly.rpyc \
+ ast2json/script-a2-rin_FR.rpyc ast2json/script-a2-rin.rpyc \
+ ast2json/script-a2-shizune_FR.rpyc ast2json/script-a2-shizune.rpyc \
+ ast2json/script-a3-emi_FR.rpyc ast2json/script-a3-emi.rpyc \
+ ast2json/script-a3-hanako_FR.rpyc ast2json/script-a3-hanako.rpyc \
+ ast2json/script-a3-lilly_FR.rpyc ast2json/script-a3-lilly.rpyc \
+ ast2json/script-a3-rin_FR.rpyc ast2json/script-a3-rin.rpyc \
+ ast2json/script-a3-shizune_FR.rpyc ast2json/script-a3-shizune.rpyc \
+ ast2json/script-a4-emi_FR.rpyc ast2json/script-a4-emi.rpyc \
+ ast2json/script-a4-hanako_FR.rpyc ast2json/script-a4-hanako.rpyc \
+ ast2json/script-a4-lilly_FR.rpyc ast2json/script-a4-lilly.rpyc \
+ ast2json/script-a4-rin_FR.rpyc ast2json/script-a4-rin.rpyc \
+ ast2json/script-a4-shizune_FR.rpyc ast2json/script-a4-shizune.rpyc
+JSONI := $(SRPYC:%.rpyc=%.json.i)
+SJSON := $(SRPYC:ast2json/%.rpyc=www/json/%.json)
+
+JSON := $(OJSON) $(SJSON)
+JSONGZ := $(JSON:=.gz)
+AJSON := $(JSON) $(JSONGZ)
# FIXME
-# json: $(JSON)
-json:
+json: $(AJSON)
-%.json.gz: %.json
- $(GZIP) -c $< > $@
- touch $< $@
+# the rules here are NOT BROKEN. think about it.
+%.json.i: ast2json/rpyc2json.py %.rpyc
+ $^ $@
-www/json/script.json: $(SJSON)
- cat $^ > "$@"
- sed -i -e 's/^/{/;s/,$$/}/' "$@"
+www/json/script-%.json: ast2json/script2json.py ast2json/script-%.json.i
+ $^ $@
-www/json/%.json: unrpyc/%.json.o
- uglifyjs "$<" --expr > "$@"
+$(OJSON): www/json/%.json: ast2json/rpyc2json.py ast2json/%.rpyc
+ $^ $@
-define unrpyc =
- python3 unrpyc/unrpyc.py --clobber "$<" "$@"
-endef
-
-unrpyc/ui-strings.json.o unrpyc/ui-strings_FR.json.o: unrpyc/ui-strings%.json.o: unrpyc/ui-strings%.rpyc unrpyc/*.py
- $(unrpyc)
- sed -i -e 's/ \["init_language", "[a-z]*", \],//;s/^\]}$$/}/' "$@"
-
-unrpyc/ui_settings.json.o: ui_settings.rpyc *.py
- python3 unrpyc.py --clobber "$<" --ignore-python "$@"
- sed -i -e 's/,,/,/g;/: *,$$/d' "$@"
-
-unrpyc/%.json.o: unrpyc/%.rpyc unrpyc/*.py
- $(unrpyc)
-
-www/json/script-%.json: unrpyc/script-%.json.o
- uglifyjs "$<" --expr > "$@"
- sed -i -e 's/^{//;s/}$$/,/' "$@"
+%.json.gz: %.json
+ $(GZIP) -c $< > $@
+ touch $< $@
# === VIDEO ===
VIDEO := $(wildcard www/dump/video/*.mkv)
-Y4M := $(patsubst %.mkv,%.y4m,$(VIDEO))
-MP4 := $(patsubst %.mkv,%.mp4,$(VIDEO))
-WEBM := $(patsubst %.mkv,%.webm,$(VIDEO))
-VP9 := $(patsubst %.mkv,%.vp9.webm,$(VIDEO))
-OGV := $(patsubst %.mkv,%.ogv,$(VIDEO))
+Y4M := $(VIDEO:.mkv=.y4m)
+MP4 := $(VIDEO:.mkv=.mp4)
+WEBM := $(VIDEO:.mkv=.webm)
+VP9 := $(VIDEO:.mkv=.vp9.webm)
+OGV := $(VIDEO:.mkv=.ogv)
CVIDEO := $(MP4) $(WEBM) $(VP9) $(OGV)
video: $(CVIDEO)
@@ -166,9 +119,9 @@ $(if $(NOTEMP),%.ogv: %.mkv,%.ogv: %.y4m)
# === AUDIO ===
AUDIO := $(wildcard www/dump/bgm/*.ogg) $(wildcard www/dump/sfx/*.ogg)
-OPUS := $(patsubst %.ogg,%.opus,$(AUDIO))
-M4A := $(patsubst %.ogg,%.m4a,$(AUDIO))
-WAV := $(patsubst %.ogg,%.wav,$(AUDIO))
+OPUS := $(AUDIO:.ogg=.opus)
+M4A := $(AUDIO:.ogg=.m4a)
+WAV := $(AUDIO:.ogg=.wav)
CAUDIO := $(OPUS) $(M4A)
audio: $(CAUDIO)
@@ -186,8 +139,8 @@ audio: $(CAUDIO)
PNG := $(shell find www/dump -name '*.png' ! -name 'ctc_strip.png')
JPG := $(shell find www/dump -name '*.jpg')
-WEBP := $(patsubst %.png,%.webp,$(PNG)) \
- $(patsubst %.jpg,%.webp,$(JPG))
+WEBP := $(PNG:.png=.webp) \
+ $(JPG:.jpg=.webp)
CTC_ANIM_SRC := www/dump/ui/ctc_strip.png
CTC_ANIM_MORE_TMP := www/dump/ui/ctc_strip-1.png \
www/dump/ui/ctc_strip-2.png www/dump/ui/ctc_strip-3.png \
@@ -222,7 +175,7 @@ CTC_ANIM_MORE_TMP := www/dump/ui/ctc_strip-1.png \
www/dump/ui/ctc_strip-60.png www/dump/ui/ctc_strip-61.png \
www/dump/ui/ctc_strip-62.png www/dump/ui/ctc_strip-63.png
CTC_ANIM_TMP := www/dump/ui/ctc_strip-0.png $(CTC_ANIM_MORE_TMP)
-CTC_ANIM_TMP_WEBP := $(patsubst %.png,%.webp,$(CTC_ANIM_TMP))
+CTC_ANIM_TMP_WEBP := $(CTC_ANIM_TMP:%.png=%.webp)
CTC_ANIM_TMP_ALL := $(CTC_ANIM_TMP) $(CTC_ANIM_TMP_WEBP)
CTC_ANIM := www/dump/ui/ctc_anim.png www/dump/ui/ctc_anim.webp
@@ -246,7 +199,6 @@ endef
$(CWEBP) -q 90 "$<" -o "$@"
www/dump/ui/ctc_strip.webp: www/dump/ui/ctc_strip.png
- @
www/favicon.ico: www/dump/ui/icon.png
$(CONVERT) "$<" -resize 256x256 -transparent white "$@"
@@ -259,7 +211,6 @@ www/dump/ui/ctc_strip-0.png: $(CTC_ANIM_SRC)
$(CONVERT) "$<" -crop 16x16 www/dump/ui/ctc_strip-%d.png
$(CTC_ANIM_MORE_TMP): $(CTC_ANIM_SRC) www/dump/ui/ctc_strip-0.png
- @
# depend on webp to wait for recompression
www/dump/ui/ctc_anim.png: $(CTC_ANIM_TMP_WEBP)
@@ -291,4 +242,4 @@ MAKEFLAGS=-LRr
.PRECIOUS: $(WAV)
.INTERMEDIATE: $(WAV) $(JSONO) $(Y4M) $(CTC_ANIM_TMP_ALL) $(CTC_ANIM_TMP_WEBP)
-.PHONY: video audio images js jshint clean space watch
+.PHONY: video audio images js jshint space watch
diff --git a/README.rst b/README.rst
index 53b131b..facaf3f 100644
--- a/README.rst
+++ b/README.rst
@@ -45,10 +45,11 @@ Build steps
1. Copy \*.rpyc from Katawa Shoujo/game into unrpyc/ directory.
2. Extract files from Katawa Shoujo/game/data.rpa with an rpa extractor, e.g. unrpa. Put files in www/dump.
3. Install prerequisites, download DeflOpt and defluff and place exes in this directory.
-4. Run make. If you're on Windoze, sucks for you. Use a better OS. Patches may or may not be accepted.
-5. Run nginx.sh to start nginx, then navigate to localhost:8080 in your browser.
+4. Run ./configure.
+5. Run make.
+6. Run nginx.sh to start nginx, then navigate to localhost:8080 in your browser.
-- OR --
-5. Open www/index.html in a browser.
+6. Open www/index.html in a browser.
Disabling unused conversions
----------------------------
diff --git a/ast2json/.gitignore b/ast2json/.gitignore
new file mode 100644
index 0000000..466d742
--- /dev/null
+++ b/ast2json/.gitignore
@@ -0,0 +1,2 @@
+/*.json*
+/*.rpy*
diff --git a/ast2json/Makefile b/ast2json/Makefile
new file mode 100644
index 0000000..48d7503
--- /dev/null
+++ b/ast2json/Makefile
@@ -0,0 +1,294 @@
+null :=
+SPACE := $(null) $(null)
+
+FFMPEG ?= ffmpeg
+FFMPEG := $(FFMPEG) -v warning -y $(FFMPEGFLAGS)
+CWEBP ?= cwebp
+CWEBP := $(CWEBP) -quiet -alpha_cleanup -m 6 $(CWEBPFLAGS)
+WEBPMUX ?= webpmux
+WEBPMUX := $(WEBPMUX) $(WEBPMUXFLAGS)
+CONVERT ?= convert
+CONVERT := $(CONVERT) $(CONVERTFLAGS)
+APNGASM ?= apngasm
+APNGASM := $(APNGASM) $(APNGASMFLAGS)
+NPM ?= npm
+NPM := $(NPM) --quiet $(NPMFLAGS)
+JS_COMPRESSOR ?= uglifyjs
+LOCAL_UGLIFYJS := node_modules/.bin/uglifyjs
+UGLIFYJS ?= $(LOCAL_UGLIFYJS)
+UGLIFYJS := $(UGLIFYJS) $(UGLIFYJSFLAGS)
+PACKR ?= packr
+CLOSURE_COMPILER ?= java -jar compiler.jar
+ifndef MINIMAL
+ZOPFLIPNG ?= zopflipng
+ZOPFLIPNG := $(ZOPFLIPNG) $(ZOPFLIPNGFLAGS)
+#DEFLOPT ?= wine DeflOpt
+#DEFLOPT := $(DEFLOPT) $(DEFLOPTFLAGS)
+DEFLUFF ?= defluff
+DEFLUFF := $(DEFLUFF) $(DEFLUFFFLAGS)
+PNGQUANT ?= pngquant
+PNGQUANT := $(PNGQUANT) $(PNGQUANTFLAGS)
+endif
+GZIP := $(shell unrpyc/find-gzip.sh)
+
+all: js json video audio images
+
+# === JS ===
+
+MYJS := www/js/html5ks.js www/js/menu.js www/js/api.js \
+ www/js/characters.js www/js/imachine.js www/js/i18n.js
+JSLIBS := www/js/lib/fastclick.js www/js/lib/modernizr-build.js \
+ www/js/lib/when.js www/js/lib/spin.js
+JSDATA := www/js/play.js www/js/images.js
+JS := $(JSLIBS) $(MYJS) $(JSDATA)
+JSOUT := www/js/all.min.js
+
+js: $(JSOUT)
+
+ifeq ($(JS_COMPRESSOR), uglifyjs)
+$(JSOUT): $(JS) $(UGLIFYJS)
+ $(UGLIFYJS) $(JS) -o "$@" --source-map "$@".map --source-map-url ./all.min.js.map --screw-ie8 -p 2 -m -c unsafe=true,drop_debugger=false
+else
+$(JSOUT): $(JS)
+# note that packr doesn't actually work
+ifeq ($(JS_COMPRESSOR), packr)
+ $(PACKR) $(JS) -o "$@"
+else
+ifeq ($(JS_COMPRESSOR), closure_compiler)
+ $(CLOSURE_COMPILER) --compilation_level SIMPLE_OPTIMIZATIONS --create_source_map "$@".map --js $(subst $(SPACE), --js ,$(JS)) --js_output_file "$@"
+endif # ($(JS_COMPRESSOR), closure_compiler)
+endif # ($(JS_COMPRESSOR), packr)
+endif # ($(JS_COMPRESSOR), uglifyjs)
+
+$(LOCAL_UGLIFYJS): package.json
+ $(NPM) update
+ touch "$@"
+
+www/js/lib/modernizr-build.js: Modernizr config-all.json
+ ln -fs ../../config-all.json "$<"/lib/config-all.json
+ cd "$<" && $(NPM) update && node_modules/.bin/grunt build
+
+www/js/lib/when.js: when
+ export PYTHON=python2; cd when && $(NPM) update && $(NPM) run browserify-debug
+
+# === JSON ===
+JSON := www/json/script.json www/json/script.json.gz www/json/imachine.json \
+ www/json/imachine.json.gz www/json/ui-strings.json \
+ www/json/ui-strings.json.gz www/json/ui-strings_FR.json \
+ www/json/ui-strings_FR.json.gz
+
+RPYC := unrpyc/script-a1-friday_FR.rpyc unrpyc/script-a1-friday.rpyc \
+ unrpyc/script-a1-monday_FR.rpyc unrpyc/script-a1-monday.rpyc \
+ unrpyc/script-a1-saturday_FR.rpyc unrpyc/script-a1-saturday.rpyc \
+ unrpyc/script-a1-sunday_FR.rpyc unrpyc/script-a1-sunday.rpyc \
+ unrpyc/script-a1-thursday_FR.rpyc unrpyc/script-a1-thursday.rpyc \
+ unrpyc/script-a1-tuesday_FR.rpyc unrpyc/script-a1-tuesday.rpyc \
+ unrpyc/script-a1-wednesday_FR.rpyc unrpyc/script-a1-wednesday.rpyc \
+ unrpyc/script-a2-emi_FR.rpyc unrpyc/script-a2-emi.rpyc \
+ unrpyc/script-a2-hanako_FR.rpyc unrpyc/script-a2-hanako.rpyc \
+ unrpyc/script-a2-lilly_FR.rpyc unrpyc/script-a2-lilly.rpyc \
+ unrpyc/script-a2-rin_FR.rpyc unrpyc/script-a2-rin.rpyc \
+ unrpyc/script-a2-shizune_FR.rpyc unrpyc/script-a2-shizune.rpyc \
+ unrpyc/script-a3-emi_FR.rpyc unrpyc/script-a3-emi.rpyc \
+ unrpyc/script-a3-hanako_FR.rpyc unrpyc/script-a3-hanako.rpyc \
+ unrpyc/script-a3-lilly_FR.rpyc unrpyc/script-a3-lilly.rpyc \
+ unrpyc/script-a3-rin_FR.rpyc unrpyc/script-a3-rin.rpyc \
+ unrpyc/script-a3-shizune_FR.rpyc unrpyc/script-a3-shizune.rpyc \
+ unrpyc/script-a4-emi_FR.rpyc unrpyc/script-a4-emi.rpyc \
+ unrpyc/script-a4-hanako_FR.rpyc unrpyc/script-a4-hanako.rpyc \
+ unrpyc/script-a4-lilly_FR.rpyc unrpyc/script-a4-lilly.rpyc \
+ unrpyc/script-a4-rin_FR.rpyc unrpyc/script-a4-rin.rpyc \
+ unrpyc/script-a4-shizune_FR.rpyc unrpyc/script-a4-shizune.rpyc
+SJSON := $(patsubst unrpyc/%.rpyc,www/json/%.json,$(RPYC))
+JSONO := $(patsubst unrpyc/%.rpyc,unrpyc/%.json.o,%(RPYC))
+
+# FIXME
+# json: $(JSON)
+json:
+
+%.json.gz: %.json
+ $(GZIP) -c $< > $@
+ touch $< $@
+
+www/json/script.json: $(SJSON)
+ cat $^ > "$@"
+ sed -i -e 's/^/{/;s/,$$/}/' "$@"
+
+www/json/%.json: unrpyc/%.json.o
+ uglifyjs "$<" --expr > "$@"
+
+define unrpyc =
+ python3 unrpyc/unrpyc.py --clobber "$<" "$@"
+endef
+
+unrpyc/ui-strings.json.o unrpyc/ui-strings_FR.json.o: unrpyc/ui-strings%.json.o: unrpyc/ui-strings%.rpyc unrpyc/*.py
+ $(unrpyc)
+ sed -i -e 's/ \["init_language", "[a-z]*", \],//;s/^\]}$$/}/' "$@"
+
+unrpyc/ui_settings.json.o: ui_settings.rpyc *.py
+ python3 unrpyc.py --clobber "$<" --ignore-python "$@"
+ sed -i -e 's/,,/,/g;/: *,$$/d' "$@"
+
+unrpyc/%.json.o: unrpyc/%.rpyc unrpyc/*.py
+ $(unrpyc)
+
+www/json/script-%.json: unrpyc/script-%.json.o
+ uglifyjs "$<" --expr > "$@"
+ sed -i -e 's/^{//;s/}$$/,/' "$@"
+
+# === VIDEO ===
+
+VIDEO := $(wildcard www/dump/video/*.mkv)
+Y4M := $(patsubst %.mkv,%.y4m,$(VIDEO))
+MP4 := $(patsubst %.mkv,%.mp4,$(VIDEO))
+WEBM := $(patsubst %.mkv,%.webm,$(VIDEO))
+VP9 := $(patsubst %.mkv,%.vp9.webm,$(VIDEO))
+OGV := $(patsubst %.mkv,%.ogv,$(VIDEO))
+CVIDEO := $(MP4) $(WEBM) $(VP9) $(OGV)
+
+video: $(CVIDEO)
+
+%.y4m: %.mkv
+ $(FFMPEG) -i "$<" -c:a copy "$@"
+
+$(if $(NOTEMP),%.mp4: %.mkv,%.mp4: %.y4m)
+ $(FFMPEG) -i "$<" -c:v libx264 -preset slower -tune animation -movflags empty_moov -profile:v baseline -c:a libfdk_aac -b:a 60k "$@"
+
+$(if $(NOTEMP),%.webm: %.mkv,%.webm: %.y4m)
+ $(FFMPEG) -i "$<" -crf 10 -b:v 1M -c:a copy "$@"
+
+$(if $(NOTEMP),%.vp9.webm: %.mkv,%.vp9.webm: %.y4m)
+ $(FFMPEG) -i "$<" -strict -2 -c:v libvpx-vp9 -crf 8 -b:v 1M -c:a libopus -vbr on -b:a 64k "$@"
+
+$(if $(NOTEMP),%.ogv: %.mkv,%.ogv: %.y4m)
+ $(FFMPEG) -i "$<" -c:v libtheora -qscale:v 10 -c:a copy "$@"
+
+# === AUDIO ===
+
+AUDIO := $(wildcard www/dump/bgm/*.ogg) $(wildcard www/dump/sfx/*.ogg)
+OPUS := $(patsubst %.ogg,%.opus,$(AUDIO))
+M4A := $(patsubst %.ogg,%.m4a,$(AUDIO))
+WAV := $(patsubst %.ogg,%.wav,$(AUDIO))
+CAUDIO := $(OPUS) $(M4A)
+
+audio: $(CAUDIO)
+
+%.wav: %.ogg
+ $(FFMPEG) -i "$<" -c:a pcm_s16le "$@"
+
+%.opus: %.wav
+ $(FFMPEG) -i "$<" -c:a libopus -vbr on -b:a 64k "$@"
+
+%.m4a: %.wav
+ $(FFMPEG) -i "$<" -b:a 60k "$@"
+
+# === IMAGES ===
+
+PNG := $(shell find www/dump -name '*.png' ! -name 'ctc_strip.png')
+JPG := $(shell find www/dump -name '*.jpg')
+WEBP := $(patsubst %.png,%.webp,$(PNG)) \
+ $(patsubst %.jpg,%.webp,$(JPG))
+CTC_ANIM_SRC := www/dump/ui/ctc_strip.png
+CTC_ANIM_MORE_TMP := www/dump/ui/ctc_strip-1.png \
+ www/dump/ui/ctc_strip-2.png www/dump/ui/ctc_strip-3.png \
+ www/dump/ui/ctc_strip-4.png www/dump/ui/ctc_strip-5.png \
+ www/dump/ui/ctc_strip-6.png www/dump/ui/ctc_strip-7.png \
+ www/dump/ui/ctc_strip-8.png www/dump/ui/ctc_strip-9.png \
+ www/dump/ui/ctc_strip-10.png www/dump/ui/ctc_strip-11.png \
+ www/dump/ui/ctc_strip-12.png www/dump/ui/ctc_strip-13.png \
+ www/dump/ui/ctc_strip-14.png www/dump/ui/ctc_strip-15.png \
+ www/dump/ui/ctc_strip-16.png www/dump/ui/ctc_strip-17.png \
+ www/dump/ui/ctc_strip-18.png www/dump/ui/ctc_strip-19.png \
+ www/dump/ui/ctc_strip-20.png www/dump/ui/ctc_strip-21.png \
+ www/dump/ui/ctc_strip-22.png www/dump/ui/ctc_strip-23.png \
+ www/dump/ui/ctc_strip-24.png www/dump/ui/ctc_strip-25.png \
+ www/dump/ui/ctc_strip-26.png www/dump/ui/ctc_strip-27.png \
+ www/dump/ui/ctc_strip-28.png www/dump/ui/ctc_strip-29.png \
+ www/dump/ui/ctc_strip-30.png www/dump/ui/ctc_strip-31.png \
+ www/dump/ui/ctc_strip-32.png www/dump/ui/ctc_strip-33.png \
+ www/dump/ui/ctc_strip-34.png www/dump/ui/ctc_strip-35.png \
+ www/dump/ui/ctc_strip-36.png www/dump/ui/ctc_strip-37.png \
+ www/dump/ui/ctc_strip-38.png www/dump/ui/ctc_strip-39.png \
+ www/dump/ui/ctc_strip-40.png www/dump/ui/ctc_strip-41.png \
+ www/dump/ui/ctc_strip-42.png www/dump/ui/ctc_strip-43.png \
+ www/dump/ui/ctc_strip-44.png www/dump/ui/ctc_strip-45.png \
+ www/dump/ui/ctc_strip-46.png www/dump/ui/ctc_strip-47.png \
+ www/dump/ui/ctc_strip-48.png www/dump/ui/ctc_strip-49.png \
+ www/dump/ui/ctc_strip-50.png www/dump/ui/ctc_strip-51.png \
+ www/dump/ui/ctc_strip-52.png www/dump/ui/ctc_strip-53.png \
+ www/dump/ui/ctc_strip-54.png www/dump/ui/ctc_strip-55.png \
+ www/dump/ui/ctc_strip-56.png www/dump/ui/ctc_strip-57.png \
+ www/dump/ui/ctc_strip-58.png www/dump/ui/ctc_strip-59.png \
+ www/dump/ui/ctc_strip-60.png www/dump/ui/ctc_strip-61.png \
+ www/dump/ui/ctc_strip-62.png www/dump/ui/ctc_strip-63.png
+CTC_ANIM_TMP := www/dump/ui/ctc_strip-0.png $(CTC_ANIM_MORE_TMP)
+CTC_ANIM_TMP_WEBP := $(patsubst %.png,%.webp,$(CTC_ANIM_TMP))
+CTC_ANIM_TMP_ALL := $(CTC_ANIM_TMP) $(CTC_ANIM_TMP_WEBP)
+CTC_ANIM := www/dump/ui/ctc_anim.png www/dump/ui/ctc_anim.webp
+
+CIMAGE := $(WEBP) $(CTC_ANIM) www/favicon.ico
+
+images: $(CIMAGE)
+
+define png2webp =
+ $(CWEBP) -q 99 "$<" -o "$@"
+ $(if $(PNGQUANT), $(PNGQUANT) --force --speed 1 --ext .png "$<")
+ $(if $(ZOPFLIPNG), $(ZOPFLIPNG) -m -y "$<" "$<")
+ $(if $(DEFLOPT), $(DEFLOPT) "$<")
+ $(if $(DEFLUFF), $(DEFLUFF) < "$<" > "$<".tmp
+ mv -f "$<".tmp "$<")
+endef
+
+%.webp: %.png
+ $(png2webp)
+
+%.webp: %.jpg
+ $(CWEBP) -q 90 "$<" -o "$@"
+
+www/dump/ui/ctc_strip.webp: www/dump/ui/ctc_strip.png
+ @
+
+www/favicon.ico: www/dump/ui/icon.png
+ $(CONVERT) "$<" -resize 256x256 -transparent white "$@"
+
+www/dump/ui/bt-cf-unchecked.webp www/dump/ui/bt-cf-checked.webp: %.webp: %.png
+ $(CONVERT) -trim "$<" "$<"
+ $(png2webp)
+
+www/dump/ui/ctc_strip-0.png: $(CTC_ANIM_SRC)
+ $(CONVERT) "$<" -crop 16x16 www/dump/ui/ctc_strip-%d.png
+
+$(CTC_ANIM_MORE_TMP): $(CTC_ANIM_SRC) www/dump/ui/ctc_strip-0.png
+ @
+
+# depend on webp to wait for recompression
+www/dump/ui/ctc_anim.png: $(CTC_ANIM_TMP_WEBP)
+ $(APNGASM) "$@" $(CTC_ANIM_TMP) 3 100
+
+www/dump/ui/ctc_anim.webp: $(CTC_ANIM_TMP_WEBP)
+ $(WEBPMUX) -frame $(subst $(SPACE), +30 -frame ,$^) +30 -loop 0 -o "$@"
+
+# === MISC ===
+
+test: $(MYJS)
+ jshint --show-non-errors $^
+
+space:
+ $(RM) -r www/dump/font
+ $(RM) $(WAV) $(VIDEO) $(CTC_ANIM_TMP) $(CTC_ANIM_TMP_WEBP) $(JSOUT) $(JSOUT).map
+
+dev:
+ $(MAKE)
+ ./nginx.sh
+ while inotifywait -r -e modify,delete,move --exclude="^\./\.git" --exclude="\.swp.?$$" .; do \
+ $(MAKE); \
+ done
+
+# disable implicit rules, increases `make` speed by 3 seconds
+# also check symlink targets (for js)
+MAKEFLAGS=-LRr
+.SUFFIXES:
+
+.PRECIOUS: $(WAV)
+.INTERMEDIATE: $(WAV) $(JSONO) $(Y4M) $(CTC_ANIM_TMP_ALL) $(CTC_ANIM_TMP_WEBP)
+.PHONY: video audio images js jshint clean space watch
diff --git a/ast2json/ast2json.py b/ast2json/ast2json.py
new file mode 100644
index 0000000..7f52687
--- /dev/null
+++ b/ast2json/ast2json.py
@@ -0,0 +1,62 @@
+# Copyright (c) 2013, Laurent Peuch <cortex@worlddomination.be>
+#
+# All rights reserved.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the University of California, Berkeley nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from _ast import AST
+from ast import parse
+
+
+def ast2json(node):
+ assert isinstance(node, AST)
+ to_return = {}
+ to_return['_type'] = node.__class__.__name__
+ for attr in dir(node):
+ if attr.startswith("_"):
+ continue
+ to_return[attr] = get_value(getattr(node, attr))
+
+ return to_return
+
+
+def str2json(string):
+ return ast2json(parse(string))
+
+
+def get_value(attr_value):
+ if attr_value is None:
+ return attr_value
+ if isinstance(attr_value, (int, str, float, complex, bool)):
+ return attr_value
+ if isinstance(attr_value, list):
+ return [get_value(x) for x in attr_value]
+ if isinstance(attr_value, AST):
+ return ast2json(attr_value)
+ else:
+ raise Exception("unknow case for '%s' of type '%s'" % (attr_value, type(attr_value)))
+
+
+if __name__ == '__main__':
+ import json
+ print(json.dumps(ast2json(parse(open(__file__, "r").read())), indent=4))
diff --git a/ast2json/decompiler.py b/ast2json/decompiler.py
new file mode 100644
index 0000000..0a0b916
--- /dev/null
+++ b/ast2json/decompiler.py
@@ -0,0 +1,500 @@
+# Copyright (c) 2012 Yuri K. Schlesner
+#
+# 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 renpy.ast as ast
+import renpy.atl as atl
+import codegen
+import screendecompiler
+import code
+
+DECOMPILE_SCREENS = True
+
+def pretty_print_ast(out_file, ast):
+ code.interact(local=locals())
+ for stmt in ast:
+ print_statement(out_file, stmt, 0)
+
+def indent(f, level):
+ # Print indentation
+ f.write(u' ' * level)
+
+def print_statement(f, statement, indent_level=0):
+ indent(f, indent_level)
+
+ func = statement_printer_dict.get(type(statement), print_Unknown)
+ func(f, statement, indent_level)
+
+def escape_string(s):
+ s = s.replace('"', '\\"')
+ s = s.replace('\n', '\\n')
+ s = s.replace('\t', '\\t')
+ return s
+
+# TODO "choice" and "parallel" blocks are greedily combined
+# so we need a "pass" statement to separate them if
+# multiple of the same block are immediately after
+# each other.
+def print_atl(f, atl_block, indent_level):
+ if not atl_block.statements:
+ indent(f, indent_level)
+ f.write(u'pass\n')
+ for stmt in atl_block.statements:
+ indent(f, indent_level)
+
+ if type(stmt) is atl.RawMultipurpose:
+ # warper
+ if stmt.warp_function:
+ f.write(u"warp %s" % (stmt.warp_function.strip(), ))
+ f.write(u" %s " % (stmt.duration.strip(), ))
+ elif stmt.warper:
+ f.write(stmt.warper)
+ f.write(u" %s " % (stmt.duration.strip(), ))
+ elif stmt.duration.strip() != '0':
+ f.write(u'pause')
+ f.write(u" %s" % (stmt.duration.strip(), ))
+
+ # revolution
+ if stmt.revolution:
+ f.write(u"%s " % (stmt.revolution, ))
+
+ # circles
+ if stmt.circles != "0":
+ f.write(u"circles %s " % (stmt.circles.strip(), ))
+
+ # splines
+ for (name, exprs) in stmt.splines:
+ f.write(u"%s " % (name, ))
+ for expr in exprs:
+ f.write(u"knot %s " % (expr.strip(), ))
+
+ # properties
+ for (k, v) in stmt.properties:
+ f.write(u"%s %s " % (k, v.strip()))
+
+ # with
+ for (expr, with_expr) in stmt.expressions:
+ f.write(u"%s " % (expr.strip(), ))
+ if with_expr:
+ f.write(u"with %s " % (with_expr, ))
+
+ f.write(u"\n")
+
+ elif type(stmt) is atl.RawBlock:
+ # what does stmt.animation do?
+ f.write(u"block:\n")
+ print_atl(f, stmt, indent_level + 1)
+
+ elif type(stmt) is atl.RawChoice:
+ first = True
+ for (chance, block) in stmt.choices:
+ if first:
+ first = False
+ else:
+ indent(f, indent_level)
+
+ f.write(u"choice")
+ if chance != "1.0":
+ f.write(u" %s" % (chance, ))
+ f.write(u":\n")
+ print_atl(f, block, indent_level + 1)
+
+ elif type(stmt) is atl.RawContainsExpr:
+ f.write(u"contains %s\n" % (stmt.expression, ))
+
+ elif type(stmt) is atl.RawEvent:
+ f.write(u"event %s\n" % (stmt.name, ))
+
+ elif type(stmt) is atl.RawFunction:
+ f.write(u"function %s\n" % (stmt.expr, ))
+
+ elif type(stmt) is atl.RawOn:
+ first = True
+ for name, block in stmt.handlers.iteritems():
+ if first:
+ first = False
+ else:
+ indent(f, indent_level)
+
+ f.write(u"on %s:\n" % (name, ))
+ print_atl(f, block, indent_level + 1)
+
+ elif type(stmt) is atl.RawParallel:
+ first = True
+ for block in stmt.blocks:
+ if first:
+ first = False
+ else:
+ indent(f, indent_level)
+
+ f.write(u"parallel:\n")
+ print_atl(f, block, indent_level + 1)
+
+ elif type(stmt) is atl.RawRepeat:
+ f.write(u"repeat")
+ if stmt.repeats:
+ f.write(u" %s" % (stmt.repeats, )) # not sure if this is even a string
+ f.write(u"\n")
+
+ elif type(stmt) is atl.RawTime:
+ f.write(u"time %s\n" % (stmt.time, ))
+
+ else:
+ f.write(u"TODO atl.%s\n" % type(stmt).__name__)
+
+def print_imspec(f, imspec):
+ if imspec[1] is not None: # Expression
+ f.write(u'expression ')
+ f.write(imspec[1])
+ else: # Image name
+ f.write(' '.join(imspec[0]))
+
+ # at
+ if len(imspec[3]) > 0:
+ f.write(u" at %s" % (', '.join(imspec[3])))
+
+ # as
+ if imspec[2] is not None:
+ f.write(u" as %s" % (imspec[2], ))
+
+ # behind
+ if len(imspec[6]) > 0:
+ f.write(u" behind %s" % (', '.join(imspec[6])))
+
+ # onlayer
+ if imspec[4] != 'master':
+ f.write(u" onlayer %s" % (imspec[4], ))
+
+ # zorder
+ # This isn't in the docs, but it's in the parser
+ if imspec[5] is not None:
+ f.write(u" zorder %s" % (imspec[5], ))
+
+def print_Label(f, stmt, indent_level):
+ f.write(u"label %s" % (stmt.name, ))
+ if stmt.parameters is not None:
+ print_params(f, stmt.parameters)
+ f.write(u':\n')
+
+ for sub_stmt in stmt.block:
+ print_statement(f, sub_stmt, indent_level + 1)
+
+def print_Say(f, stmt, indent_level):
+ if stmt.who is not None:
+ f.write(u"%s " % (stmt.who, ))
+ f.write(u"\"%s\"" % (escape_string(stmt.what), ))
+ if stmt.with_ is not None:
+ f.write(u" with %s" % (stmt.with_, ))
+ f.write(u'\n')
+
+def print_Jump(f, stmt, indent_level):
+ f.write(u"jump ")
+ if stmt.expression:
+ # TODO expression
+ f.write(u"expression TODO")
+ else:
+ f.write(stmt.target)
+ f.write(u'\n')
+
+def print_Scene(f, stmt, indent_level):
+ f.write(u"scene")
+ if stmt.imspec is None:
+ if stmt.layer != 'master':
+ f.write(u" onlayer %s" % (stmt.layer, ))
+ else:
+ f.write(u' ')
+ print_imspec(f, stmt.imspec)
+
+ # with isn't handled here, but split in several statements
+
+ if stmt.atl is not None:
+ f.write(u':\n')
+ print_atl(f, stmt.atl, indent_level+1)
+ else:
+ f.write('\n')
+
+def print_With(f, stmt, indent_level):
+ f.write(u"with %s\n" % (stmt.expr, ))
+
+def print_Show(f, stmt, indent_level):
+ f.write(u"show ")
+ print_imspec(f, stmt.imspec)
+
+ # with isn't handled here, but split in several statements
+
+ if stmt.atl is not None:
+ f.write(u':\n')
+ print_atl(f, stmt.atl, indent_level+1)
+ else:
+ f.write('\n')
+
+def print_Hide(f, stmt, indent_level):
+ f.write(u"hide ")
+ print_imspec(f, stmt.imspec)
+
+ # with isn't handled here, but split in several statements
+
+ f.write('\n')
+
+def print_Python(f, stmt, indent_level, early=False):
+ code_src = stmt.code.source
+
+ stripped_code = code_src.strip()
+
+ if stripped_code.count('\n') == 0:
+ f.write(u"$ %s\n" % (stripped_code, ))
+ else:
+ f.write(u"python")
+ if early:
+ f.write(u" early")
+ if stmt.hide:
+ f.write(u" hide")
+ f.write(u":\n")
+
+ for line in code_src.splitlines(True):
+ indent(f, indent_level + 1)
+ f.write(line)
+
+def print_Return(f, stmt, indent_level):
+ f.write(u"return")
+
+ if stmt.expression is not None:
+ f.write(u" %s" % (stmt.expression, ))
+
+ f.write(u'\n')
+
+def print_UserStatement(f, stmt, indent_level):
+ f.write(u"%s\n" % (stmt.line, ))
+
+def print_Init(f, stmt, indent_level):
+ if len(stmt.block) == 1 and (hasattr(ast, 'Screen') and isinstance(stmt.block[0], ast.Screen)) and stmt.priority == -500:
+ print_screen(f, stmt.block[0], indent_level)
+ else:
+ f.write(u"init")
+ if stmt.priority != 0:
+ f.write(u" %d" % (stmt.priority, ))
+ f.write(u":\n")
+ for s in stmt.block:
+ print_statement(f, s, indent_level + 1)
+
+def print_Image(f, stmt, indent_level):
+ f.write(u"image %s" % (' '.join(stmt. imgname), ))
+ if stmt.code is not None:
+ f.write(u" = %s\n" % (stmt.code.source, ))
+ else:
+ f.write(u":\n")
+ print_atl(f, stmt.atl, indent_level + 1)
+
+def print_Transform(f, stmt, indent_level):
+ f.write(u"transform %s" % (stmt.varname, ))
+ if stmt.parameters is not None:
+ print_params(f, stmt.parameters)
+
+ f.write(":\n")
+ print_atl(f, stmt.atl, indent_level + 1)
+
+def print_Menu(f, stmt, indent_level):
+ f.write(u"menu:\n")
+
+ if stmt.with_ is not None:
+ indent(f, indent_level + 1)
+ f.write(u"with %s\n" % (stmt.with_, ))
+
+ if stmt.set is not None:
+ indent(f, indent_level + 1)
+ f.write(u"set %s\n" % (stmt.with_, ))
+
+ for item in stmt.items:
+ indent(f, indent_level + 1)
+
+ # caption
+ f.write(u"\"%s\"" % (escape_string(item[0]), ))
+
+ if item[2] is not None:
+ # condition
+ if item[1] != 'True':
+ f.write(u" if %s" % (item[1], ))
+
+ f.write(u':\n')
+
+ for inner_stmt in item[2]:
+ print_statement(f, inner_stmt, indent_level + 2)
+ else:
+ f.write(u'\n')
+
+def print_Pass(f, stmt, indent_level):
+ f.write(u"pass\n")
+
+def print_Call(f, stmt, indent_level):
+ f.write(u"call ")
+ if stmt.expression:
+ f.write(u"expression %s" % (stmt.label, ))
+ else:
+ f.write(stmt.label)
+
+ if stmt.arguments is not None:
+ if stmt.expression:
+ f.write(u"pass ")
+ print_args(f, stmt.arguments)
+
+ f.write(u'\n')
+
+def print_If(f, stmt, indent_level):
+ f.write(u"if %s:\n" % (stmt.entries[0][0], ))
+ for inner_stmt in stmt.entries[0][1]:
+ print_statement(f, inner_stmt, indent_level + 1)
+
+ if len(stmt.entries) >= 2:
+ if stmt.entries[-1][0].strip() == 'True':
+ else_entry = stmt.entries[-1]
+ elif_entries = stmt.entries[1:-1]
+ else:
+ else_entry = None
+ elif_entries = stmt.entries
+
+ for case in elif_entries:
+ indent(f, indent_level)
+ f.write(u"elif %s:\n" % (case[0], ))
+ for inner_stmt in case[1]:
+ print_statement(f, inner_stmt, indent_level + 1)
+
+ if else_entry is not None:
+ indent(f, indent_level)
+ f.write(u"else:\n")
+ for inner_stmt in else_entry[1]:
+ print_statement(f, inner_stmt, indent_level + 1)
+
+def print_EarlyPython(f, stmt, indent_level):
+ print_Python(f, stmt, indent_level, early=True)
+
+# TODO extrapos, extrakw?
+def print_args(f, arginfo):
+ if arginfo is None:
+ return
+
+ f.write(u"(")
+
+ first = True
+ for (name, val) in arginfo.arguments:
+ if first:
+ first = False
+ else:
+ f.write(u", ")
+
+ if name is not None:
+ f.write(u"%s = " % (name, ))
+ f.write(val)
+
+ f.write(u")")
+
+# TODO positional?
+def print_params(f, paraminfo):
+ f.write(u"(")
+
+ first = True
+ for param in paraminfo.parameters:
+ if first:
+ first = False
+ else:
+ f.write(u", ")
+
+ f.write(param[0])
+
+ if (param[1] is not None) and ('None' not in param[1]):
+ f.write(u" = %s" % param[1])
+ if paraminfo.extrapos:
+ f.write(u", ")
+ f.write(u"*%s" % paraminfo.extrapos)
+ if paraminfo.extrakw:
+ f.write(u", ")
+ f.write(u"**%s" % paraminfo.extrakw)
+
+ f.write(u")")
+
+# Print while command, from http://forum.cheatengine.org/viewtopic.php?p=5377683
+def print_While(f, stmt, indent_level):
+ f.write(u"while %s:\n" % (stmt.condition, ))
+ for inner_stmt in stmt.block:
+ print_statement(f, inner_stmt, indent_level + 1)
+
+# Print define command, by iAmGhost
+def print_Define(f, stmt, indent_level):
+ f.write(u"define %s = %s\n" % (stmt.varname, stmt.code.source,))
+
+# Print Screen code (or at least code which does exactly the same. can't be picky, don't have source)
+# We'll read the ast out here, but leave the decompilation of the resulting python code to screendecompiler.py
+# It'd just bloat up this file
+def print_screen(f, stmt, indent_level):
+ screen = stmt.screen
+ # The creator of the python ast module also created a script to revert it.
+ sourcecode = codegen.to_source(screen.code.source, u" "*4)
+ # why suddenly ast in the source code field
+ f.write(u"screen %s" % screen.name)
+ if hasattr(screen, 'parameters') and screen.parameters:
+ print_params(f, screen.parameters)
+ f.write(u":\n")
+ if screen.tag:
+ indent(f, indent_level+1)
+ f.write(u"tag %s\n" % screen.tag)
+ if screen.zorder and screen.zorder != '0':
+ indent(f, indent_level+1)
+ f.write(u"zorder %s\n" % screen.zorder)
+ if screen.modal:
+ indent(f, indent_level+1)
+ f.write(u"modal %s\n" % screen.modal)
+ if screen.variant != "None":
+ indent(f, indent_level+1)
+ f.write(u"variant %s\n" % screen.variant)
+ # actual screen decompilation is HARD
+ if DECOMPILE_SCREENS:
+ screendecompiler.print_screen(f, sourcecode, indent_level+1)
+ else:
+ indent(f, indent_level+1)
+ f.write('python:\n')
+ f.write('\n'.join([" "*(indent_level+2)+line for line in sourcecode.splitlines()]))
+ f.write(u"\n")
+
+statement_printer_dict = {
+ ast.Label: print_Label,
+ ast.Say: print_Say,
+ ast.Jump: print_Jump,
+ ast.Scene: print_Scene,
+ ast.With: print_With,
+ ast.Show: print_Show,
+ ast.Hide: print_Hide,
+ ast.Python: print_Python,
+ ast.Return: print_Return,
+ ast.UserStatement: print_UserStatement,
+ ast.Init: print_Init,
+ ast.Image: print_Image,
+ ast.Transform: print_Transform,
+ ast.Menu: print_Menu,
+ ast.Pass: print_Pass,
+ ast.Call: print_Call,
+ ast.If: print_If,
+ ast.While: print_While,
+ ast.Define: print_Define,
+ ast.EarlyPython: print_EarlyPython,
+ }
+if hasattr(ast, 'Screen'): #backwards compatability
+ statement_printer_dict.update({ast.Screen: print_screen})
+
+def print_Unknown(f, stmt, indent_level):
+ print "Unknown AST node: %s" % (type(stmt).__name__, )
+ f.write(u"<<<UNKNOWN NODE %s>>>\n" % (type(stmt).__name__, ))
diff --git a/unrpyc/renpy/LICENSE.txt b/ast2json/renpy/LICENSE.txt
index 383a780..383a780 100644
--- a/unrpyc/renpy/LICENSE.txt
+++ b/ast2json/renpy/LICENSE.txt
diff --git a/unrpyc/renpy/__init__.py b/ast2json/renpy/__init__.py
index f2bd463..f2bd463 100644
--- a/unrpyc/renpy/__init__.py
+++ b/ast2json/renpy/__init__.py
diff --git a/unrpyc/renpy/ast.py b/ast2json/renpy/ast.py
index 2f714ce..6fbd58f 100644
--- a/unrpyc/renpy/ast.py
+++ b/ast2json/renpy/ast.py
@@ -26,7 +26,7 @@
# When updating this file, consider if lint.py or warp.py also need
# updating.
-import renpy.display
+import renpy
import re
import time
diff --git a/unrpyc/renpy/atl.py b/ast2json/renpy/atl.py
index f314e3e..a321e6b 100644
--- a/unrpyc/renpy/atl.py
+++ b/ast2json/renpy/atl.py
@@ -19,7 +19,7 @@
# 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 renpy.display
+import renpy
import random
def compiling(loc):
diff --git a/unrpyc/renpy/game.py b/ast2json/renpy/game.py
index 028da26..d46043f 100644
--- a/unrpyc/renpy/game.py
+++ b/ast2json/renpy/game.py
@@ -23,7 +23,7 @@
# It's purpose is to store in one global all of the data that would
# be to annoying to lug around otherwise.
-import renpy.display
+import renpy
# The basepath.
basepath = None
diff --git a/unrpyc/renpy/object.py b/ast2json/renpy/object.py
index c595e8c..c595e8c 100644
--- a/unrpyc/renpy/object.py
+++ b/ast2json/renpy/object.py
diff --git a/ast2json/rpyc2json.py b/ast2json/rpyc2json.py
new file mode 100755
index 0000000..e57ed83
--- /dev/null
+++ b/ast2json/rpyc2json.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2012 Yuri K. Schlesner
+#
+# 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 ast2json
+import glob
+import itertools
+import simplejson as json
+import optparse
+import os.path
+import pickle
+import sys
+import zlib
+
+class Dummy:
+ def record_pycode(self, *args, **kwargs):
+ return
+ all_pycode = []
+
+# Needed for pickle to read the AST
+import renpy
+import renpy.object
+import renpy.game
+renpy.game.script = Dummy()
+import renpy.ast
+import renpy.atl
+
+def pretty_print_ast(out_file, ast):
+ json.dump(rast2json(ast), out_file, separators=(',', ':'))
+
+def node2json(node):
+ to_return = {}
+ to_return['_type'] = node.__class__.__name__
+ for attr in node.__slots__:
+ to_return[attr] = get_value(getattr(node, attr))
+
+ return to_return
+
+def rast2json(ast):
+ return list(map(node2json, ast))
+
+def get_value(attr_value):
+ if attr_value is None:
+ return attr_value
+ if isinstance(attr_value, (int, str, float, complex, bool)):
+ return attr_value
+ if isinstance(attr_value, list) or isinstance(attr_value, tuple):
+ return [get_value(x) for x in attr_value]
+ if isinstance(attr_value, renpy.ast.Node):
+ return node2json(attr_value)
+ if isinstance(attr_value, renpy.ast.PyCode):
+ return ast2json.str2json(attr_value.source)
+ if isinstance(attr_value, renpy.ast.ArgumentInfo):
+ return list(map(lambda x: getattr(attr_value, x), ["arguments", "extrapos", "extrakw"]))
+ if isinstance(attr_value, renpy.atl.RawBlock):
+ return 'ATL not implemented'
+
+ raise Exception("I don't know how to decompile '%s' of type '%s'!" % (attr_value, type(attr_value)))
+
+def read_ast_from_file(in_file):
+ raw_contents = zlib.decompress(in_file.read())
+ _, stmts = pickle.loads(raw_contents)
+ return stmts
+
+def decompile_rpyc(input_filename, out_filename):
+
+ print("Decompiling %s to %s..." % (input_filename, out_filename))
+
+ with open(input_filename, 'rb') as in_file:
+ ast = read_ast_from_file(in_file)
+
+ with open(out_filename, 'w', encoding='utf-8') as out_file:
+ pretty_print_ast(out_file, ast)
+
+if __name__ == "__main__":
+
+ if len(sys.argv) != 3: raise Exception()
+
+ decompile_rpyc(sys.argv[1], sys.argv[2])
+
diff --git a/ast2json/script2json.py b/ast2json/script2json.py
new file mode 100755
index 0000000..60be575
--- /dev/null
+++ b/ast2json/script2json.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python3
+
+import json
+import sys
+
+def script2json(script):
+ ret = {}
+ if script[0]['_type'] != 'Label': raise TypeError('obj does not start with Label, wrong file?')
+ for inst in script:
+ inst_type = inst['_type']
+ if inst_type == 'Label':
+ label = []
+ ret[inst['name']] = label
+ continue
+
+ func = statement_printer_dict.get(inst_type, print_Unknown)
+ label.append([inst_type] + func(inst))
+ return ret
+
+def print_Say(stmt):
+ return [stmt['who'], stmt['what'], stmt['with_']]
+
+def print_Jump(stmt):
+ if stmt['expression']:
+ raise NotImplementedError()
+ return [stmt['target']]
+
+def print_Scene(stmt):
+ return [stmt['imspec'], stmt['atl']]
+
+def print_With(stmt):
+ return [stmt['expr']]
+
+def print_Show(stmt):
+ return [stmt['imspec'], stmt['atl']]
+
+def print_Hide(stmt):
+ return [stmt['imspec']]
+
+def print_Python(stmt):
+ return [stmt['code']]
+
+def print_Return(stmt):
+ return [stmt['expression']]
+
+def print_UserStatement(stmt):
+ return [stmt['line']]
+
+def print_Init(stmt):
+ raise NotImplementedError()
+
+def print_Image(stmt):
+ raise NotImplementedError()
+
+def print_Transform(stmt):
+ raise NotImplementedError()
+
+def print_Menu(stmt):
+ if stmt['with_'] != 'menueffect':
+ raise NotImplementedError()
+
+ if stmt['set'] is not None:
+ raise NotImplementedError()
+
+ return []
+
+ for item in stmt['items']:
+ # caption
+ f.write(u"\"%s\"" % (escape_string(item[0]), ))
+
+ if item[2] is not None:
+ # condition
+ if item[1] != 'True':
+ f.write(u" if %s" % (item[1], ))
+
+ f.write(u':\n')
+
+ for inner_stmt in item[2]:
+ print_statement(f, inner_stmt, indent_level + 2)
+ else:
+ f.write(u'\n')
+
+def print_Pass(stmt):
+ return []
+
+def print_Call(stmt):
+ f.write(u"call ")
+ if stmt['expression']:
+ raise NotImplementedError()
+
+ return [stmt['label'], stmt['arguments']]
+
+def print_If(stmt):
+ f.write(u"if %s:\n" % (stmt['entries'][0][0], ))
+ for inner_stmt in stmt['entries'][0][1]:
+ print_statement(f, inner_stmt, indent_level + 1)
+
+ if len(stmt['entries']) >= 2:
+ if stmt['entries'][-1][0].strip() == 'True':
+ else_entry = stmt['entries'][-1]
+ elif_entries = stmt['entries'][1:-1]
+ else:
+ else_entry = None
+ elif_entries = stmt['entries']
+
+ for case in elif_entries:
+ indent(f, indent_level)
+ f.write(u"elif %s:\n" % (case[0], ))
+ for inner_stmt in case[1]:
+ print_statement(f, inner_stmt, indent_level + 1)
+
+ if else_entry is not None:
+ indent(f, indent_level)
+ f.write(u"else:\n")
+ for inner_stmt in else_entry[1]:
+ print_statement(f, inner_stmt, indent_level + 1)
+
+def print_EarlyPython(stmt):
+ print_Python(stmt, early=True)
+
+statement_printer_dict = {
+ "Say": print_Say,
+ "Jump": print_Jump,
+ "Scene": print_Scene,
+ "With": print_With,
+ "Show": print_Show,
+ "Hide": print_Hide,
+ "Python": print_Python,
+ "Return": print_Return,
+ "UserStatement": print_UserStatement,
+ "Init": print_Init,
+ "Image": print_Image,
+ "Transform": print_Transform,
+ "Menu": print_Menu,
+ "Pass": print_Pass,
+ "Call": print_Call,
+ "If": print_If,
+ "EarlyPython": print_EarlyPython,
+ }
+
+def print_Unknown(stmt):
+ raise NotImplementedError("Unknown AST node: %s" % type(stmt).__name__)
+
+with open(sys.argv[1], 'r') as f:
+ output = script2json(json.load(f))
+
+json.dump(output, open(sys.argv[2], 'w'), separators=(',', ':'))
diff --git a/configure b/configure
new file mode 100755
index 0000000..e5c7fe7
--- /dev/null
+++ b/configure
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+set -e
+
+OUT=Makefile.inc
+
+> "${OUT}"
+
+checking() {
+ printf "checking for %s... " "$1" >&2
+}
+
+check() {
+ checking="$1"
+ shift
+ var="${VAR:-${checking^^}}"
+ cmd="${!var}"
+ varflags="${var}FLAGS"
+ varflags="$@ ${!varflags}"
+ if [[ -z "${cmd}" ]]; then
+ cmd="${checking}"
+ fi
+ get=$(command -v "${cmd}")
+ e=$?
+ if [[ -n "$get" ]]; then
+ echo ${get}
+ echo ${var} := ${get} ${varflags} >> "${OUT}"
+ fi
+ return $e
+}
+
+rcheck() {
+ checking "$1"
+ if ! check "$@"; then
+ echo no
+ return 1
+ fi
+}
+
+ocheck() {
+ rcheck "$@" || true
+}
+
+checking "zopfli or gzip"
+VAR=GZIP check zopfli || check gzip -9
+
+rcheck apngasm
+rcheck convert
+rcheck cwebp -quiet -alpha_cleanup -m 6
+rcheck ffmpeg -v warning -y
+rcheck npm --quiet
+rcheck webpmux
+ocheck defluff
+ocheck pngquant
+ocheck zopflipng
diff --git a/unrpyc/README b/unrpyc/README
deleted file mode 100644
index 6c337f6..0000000
--- a/unrpyc/README
+++ /dev/null
@@ -1,14 +0,0 @@
-This is a !@#$ed up version of unrpyc to generate sort-of JSON from Ren'Py .rpyc files.
-
-It's made for Katawa Shoujo script files; trying to use it on anything else will probably break horribly.
-
-How to use:
-
-1. Put the rpyc files from the official KS distribution in here.
-2. Run "make install".
-
-If you're on Windows, sucks for you. Use a better OS.
-
-If you want to hack on the code, have fun. The code sucks, and this is all the documentation you get.
-
-https://github.com/yuriks/unrpyc
diff --git a/unrpyc/decompiler.py b/unrpyc/decompiler.py
deleted file mode 100644
index de80147..0000000
--- a/unrpyc/decompiler.py
+++ /dev/null
@@ -1,493 +0,0 @@
-import ast as python_ast
-import renpy.ast as ast
-import renpy.atl as atl
-import pdb
-import json
-
-DECOMPILE_SCREENS = False
-firstLabel = True
-warnedATL = False
-ignorePython = False
-
-def pretty_print_ast(out_file, ast, ignore_python):
- global ignorePython
- ignorePython = ignore_python
- out_file.write('{')
- for stmt in ast:
- print_statement(out_file, stmt, 0)
- if ignore_python:
- out_file.write('}')
- else:
- out_file.write(']}')
-
-def indent(f, level):
- # Print indentation
- f.write(' ' * level)
-
-def print_statement(f, statement, indent_level=0):
- indent(f, indent_level)
-
- func = statement_printer_dict.get(type(statement), print_Unknown)
- func(f, statement, indent_level)
-
-def escape_string(s):
- s = s.replace('"', '\\"')
- s = s.replace('\n', '\\n')
- s = s.replace('\t', '\\t')
- return s
-
-# TODO "choice" and "parallel" blocks are greedily combined
-# so we need a "pass" statement to separate them if
-# multiple of the same block are immediately after
-# each other.
-def print_atl(f, atl_block, indent_level):
- if not atl_block.statements:
- indent(f, indent_level)
- for stmt in atl_block.statements:
- indent(f, indent_level)
-
- if type(stmt) is atl.RawMultipurpose:
- ## warper
- #if stmt.warp_function:
- # f.write("warp %s" % (stmt.warp_function.strip(), ))
- # f.write(" %s " % (stmt.duration.strip(), ))
- #elif stmt.warper:
- # f.write(stmt.warper)
- # f.write(" %s " % (stmt.duration.strip(), ))
- #elif stmt.duration.strip() != '0':
- # f.write('pause')
- # f.write(" %s" % (stmt.duration.strip(), ))
-
- ## revolution
- #if stmt.revolution:
- # f.write("%s " % (stmt.revolution, ))
-
- ## circles
- #if stmt.circles != "0":
- # f.write("circles %s " % (stmt.circles.strip(), ))
-
- ## splines
- #for (name, exprs) in stmt.splines:
- # f.write("%s " % (name, ))
- # for expr in exprs:
- # f.write("knot %s " % (expr.strip(), ))
-
- ## properties
- #for (k, v) in stmt.properties:
- # f.write("%s %s " % (k, v.strip()))
-
- # with
- for (expr, with_expr) in stmt.expressions:
- if expr.strip()[0] == '"':
- f.write("%s " % (expr.strip(), ))
- #if with_expr:
- # f.write("with %s " % (with_expr, ))
-
- #elif type(stmt) is atl.RawBlock:
- # # what does stmt.animation do?
- # f.write("block:\n")
- # print_atl(f, stmt, indent_level + 1)
-
- #elif type(stmt) is atl.RawChoice:
- # first = True
- # for (chance, block) in stmt.choices:
- # if first:
- # first = False
- # else:
- # indent(f, indent_level)
-
- # f.write("choice")
- # if chance != "1.0":
- # f.write(" %s" % (chance, ))
- # f.write(":\n")
- # print_atl(f, block, indent_level + 1)
-
- #elif type(stmt) is atl.RawContainsExpr:
- # f.write("contains %s\n" % (stmt.expression, ))
-
- #elif type(stmt) is atl.RawEvent:
- # f.write("event %s\n" % (stmt.name, ))
-
- #elif type(stmt) is atl.RawFunction:
- # f.write("function %s\n" % (stmt.expr, ))
-
- #elif type(stmt) is atl.RawOn:
- # first = True
- # for name, block in list(stmt.handlers.items()):
- # if first:
- # first = False
- # else:
- # indent(f, indent_level)
-
- # f.write("on %s:\n" % (name, ))
- # print_atl(f, block, indent_level + 1)
-
- #elif type(stmt) is atl.RawParallel:
- # first = True
- # for block in stmt.blocks:
- # if first:
- # first = False
- # else:
- # indent(f, indent_level)
-
- # f.write("parallel:\n")
- # print_atl(f, block, indent_level + 1)
-
- #elif type(stmt) is atl.RawRepeat:
- # f.write("repeat")
- # if stmt.repeats:
- # f.write(" %s" % (stmt.repeats, )) # not sure if this is even a string
- # f.write("\n")
-
- #elif type(stmt) is atl.RawTime:
- # f.write("time %s\n" % (stmt.time, ))
-
- #else:
- # f.write("TODO atl.%s\n" % type(stmt).__name__)
-
-def print_imspec(f, imspec):
- if imspec[1] is not None: # Expression
- f.write('expression ')
- f.write(escape_string(imspec[1]))
- else: # Image name
- delim = ''
- for s in imspec[0]:
- f.write(delim + escape_string(s))
- delim = '", "'
-
- # at
- if len(imspec[3]) > 0:
- f.write('", "')
- delim = ''
- for s in imspec[3]:
- f.write(delim + escape_string(s))
- delim = ', '
-
- # as
- if imspec[2] is not None:
- f.write(" as %s" % (escape_string(imspec[2]), ))
-
- # behind
- if len(imspec[6]) > 0:
- f.write('", "behind", "')
- delim = ''
- for s in imspec[6]:
- f.write(delim + escape_string(s))
- delim = ', '
-
- f.write('"],')
-
-def print_Label(f, stmt, indent_level):
- if firstLabel:
- global firstLabel
- firstLabel = False
- else:
- f.write("],\n")
- f.write("\"%s\": [\n" % (stmt.name, ))
- if stmt.parameters is not None:
- print("Error: label params")
-
- for sub_stmt in stmt.block:
- print_statement(f, sub_stmt, indent_level + 1)
-
-def print_Say(f, stmt, indent_level):
- if stmt.who is not None:
- f.write('["%s", ' % (escape_string(stmt.who), ))
- else:
- f.write("[")
- f.write("\"%s\"]," % (escape_string(stmt.what), ))
- if stmt.with_ is not None:
- pass
- #f.write(" with %s" % (stmt.with_, ))
- f.write('\n')
-
-def print_Jump(f, stmt, indent_level):
- f.write("jump ")
- if stmt.expression:
- # TODO expression
- f.write("expression TODO")
- else:
- f.write(stmt.target)
- f.write('\n')
-
-def print_Scene(f, stmt, indent_level):
- f.write('["scene", "')
- print_imspec(f, stmt.imspec)
-
- # with isn't handled here, but split in several statements
-
- f.write('\n')
- if stmt.atl is not None:
- print_atl(f, stmt.atl, indent_level+1)
-
-def print_With(f, stmt, indent_level):
- f.seek(-3, 1)
- f.write(', "%s"],\n' % (escape_string(stmt.expr), ))
-
-def print_Show(f, stmt, indent_level):
- f.write('["show", "')
- print_imspec(f, stmt.imspec)
-
- # with isn't handled here, but split in several statements
-
- if stmt.atl is not None:
- f.write('\n')
- print_atl(f, stmt.atl, indent_level+1)
- else:
- f.write('\n')
-
-def print_Hide(f, stmt, indent_level):
- f.write('["hide", "')
- print_imspec(f, stmt.imspec)
-
- # with isn't handled here, but split in several statements
-
- f.write('\n')
-
-class PrintRenPython(python_ast.NodeVisitor):
- def __init__(self, f):
- self.f = f
-
- def visit_Attribute(self, node):
- return '"%s"' % node.attr
-
- def visit_Assign(self, node):
- if isinstance(node.targets[0], python_ast.Subscript):
- return
- if isinstance(node.targets[0], python_ast.Name):
- id = node.targets[0].id
- if id == 'suppress_window_before_timeskip' or id == 'suppress_window_after_timeskip' or id == '_window':
- return
- return "%s: %s,\n" % (self.visit(node.targets[0]), self.visit(node.value))
-
- def visit_Call(self, node):
- return '[%s, %s, %s],' % (self.visit(node.func), ', '.join(map(self.visit, node.args)), ', '.join(map(self.visit, node.keywords)))
-
- def visit_Compare(self, node):
- return '[%s, %s], ' % (self.visit(node.left), self.visit(node.comparators[0]))
-
- def visit_Expression(self, node):
- return self.visit(node.body)
-
- def quote(self, string):
- return '"%s"' % string
-
- def visit_Dict(self, node):
- return self.quote(python_ast.dump(node))
-
- def visit_List(self, node):
- ret = '['
- delim = ''
- for elt in node.elts:
- ret += delim
- ret += self.visit(elt)
- delim = ','
- ret += ']'
- return ret
-
- def visit_Num(self, node):
- return json.dumps(node.n)
-
- def visit_Name(self, node):
- return json.dumps(node.id)
-
- def visit_Str(self, node):
- return json.dumps(node.s)
-
- def visit_Tuple(self, node):
- return self.visit_List(node)
-
- def visit_keyword(self, node):
- return self.visit(node.value)
-
-def print_Python(f, stmt, indent_level, early=False):
- if ignorePython:
- return
-
- code_src = stmt.code.source
-
- stripped_code = code_src.strip()
-
- stmt = compile(code_src, '<unknown>', 'exec', python_ast.PyCF_ONLY_AST)
- PrintRenPython(f).visit(stmt)
-
-def print_Return(f, stmt, indent_level):
- if stmt.expression is not None:
- f.write(' "%s",' % (stmt.expression, ))
-
- f.write('\n')
-
-def print_UserStatement(f, stmt, indent_level):
- f.write('["%s"],\n' % (escape_string(stmt.line).replace(' ', '", "'), ))
-
-def print_Init(f, stmt, indent_level):
- for s in stmt.block:
- print_statement(f, s, indent_level + 1)
-
-def print_Image(f, stmt, indent_level):
- f.write('"%s": ' % ('_'.join(stmt.imgname), ))
- if stmt.code is not None:
- ret = PrintRenPython(f).visit(compile(stmt.code.source, '<unknown>', 'eval', python_ast.PyCF_ONLY_AST))
- f.write(ret)
- else:
- print_atl(f, stmt.atl, indent_level + 1)
- f.write(',\n')
-
-def print_Transform(f, stmt, indent_level):
- return
- f.write("transform %s" % (stmt.varname, ))
- if stmt.parameters is not None:
- print_params(f, stmt.parameters)
-
- f.write("\n")
- print_atl(f, stmt.atl, indent_level + 1)
-
-def print_Menu(f, stmt, indent_level):
- f.write('["menu", ')
-
- first = True
-
- for item in stmt.items:
- indent(f, indent_level + 1)
-
- if first and item[2] is not None:
- first = False
- f.write(' {\n')
-
- # caption
- f.write("\"%s\"" % (escape_string(item[0]), ))
-
- if first and item[2] is None:
- first = False
- f.write(', {')
-
- if item[2] is not None:
- f.write(':')
- for inner_stmt in item[2]:
- print_statement(f, inner_stmt, indent_level + 2)
-
- f.write('}]\n')
-
-def print_Pass(f, stmt, indent_level):
- f.write("\n")
-
-def print_Call(f, stmt, indent_level):
- f.write("[")
- if stmt.expression:
- f.write("expression %s" % (stmt.label, ))
- else:
- f.write('"%s"' % stmt.label)
-
- if stmt.arguments is not None:
- print_args(f, stmt.arguments)
-
- f.write('],\n')
-
-def print_If(f, stmt, indent_level):
- f.write('["if", ')
- if_stmt = compile(stmt.entries[0][0], '<unknown>', 'exec', python_ast.PyCF_ONLY_AST).body[0]
- PrintRenPython(f).visit(if_stmt)
- f.write('[\n')
- for inner_stmt in stmt.entries[0][1]:
- print_statement(f, inner_stmt, indent_level + 1)
-
- if len(stmt.entries) >= 2:
- if stmt.entries[-1][0].strip() == 'True':
- else_entry = stmt.entries[-1]
- elif_entries = stmt.entries[1:-1]
- else:
- else_entry = None
- elif_entries = stmt.entries
-
- for case in elif_entries:
- indent(f, indent_level)
- f.write('], "elif", ')
- elif_stmt = compile(case[0], '<unknown>', 'exec', python_ast.PyCF_ONLY_AST).body[0]
- PrintRenPython(f).visit(if_stmt)
- f.write('[\n')
- for inner_stmt in case[1]:
- print_statement(f, inner_stmt, indent_level + 1)
-
- if else_entry is not None:
- indent(f, indent_level)
- f.write('], "else", [\n')
- for inner_stmt in else_entry[1]:
- print_statement(f, inner_stmt, indent_level + 1)
-
- f.write(']],\n')
-
-def print_EarlyPython(f, stmt, indent_level):
- print_Python(f, stmt, indent_level, early=True)
-
-# TODO extrapos, extrakw?
-def print_args(f, arginfo):
- if arginfo is None:
- return
-
- for (name, val) in arginfo.arguments:
- f.write(', ')
-# if name is not None:
-# f.write("%s = " % json.dumps(name))
- f.write(json.dumps(eval(val)))
-
-# TODO positional?
-def print_params(f, paraminfo):
- f.write("(")
-
- first = True
- for param in paraminfo.parameters:
- if first:
- first = False
- else:
- f.write(", ")
-
- f.write(param[0])
-
- if (param[1] is not None) and ('None' not in param[1]):
- f.write(" = %s" % param[1])
- if paraminfo.extrapos:
- f.write(", ")
- f.write("*%s" % paraminfo.extrapos)
- if paraminfo.extrakw:
- f.write(", ")
- f.write("**%s" % paraminfo.extrakw)
-
- f.write(")")
-
-# Print while command, from http://forum.cheatengine.org/viewtopic.php?p=5377683
-def print_While(f, stmt, indent_level):
- f.write("while %s:\n" % (stmt.condition, ))
- for inner_stmt in stmt.block:
- print_statement(f, inner_stmt, indent_level + 1)
-
-# Print define command, by iAmGhost
-def print_Define(f, stmt, indent_level):
- f.write("define %s = %s\n" % (stmt.varname, stmt.code.source,))
-
-
-statement_printer_dict = {
- ast.Label: print_Label,
- ast.Say: print_Say,
- ast.Jump: print_Jump,
- ast.Scene: print_Scene,
- ast.With: print_With,
- ast.Show: print_Show,
- ast.Hide: print_Hide,
- ast.Python: print_Python,
- ast.Return: print_Return,
- ast.UserStatement: print_UserStatement,
- ast.Init: print_Init,
- ast.Image: print_Image,
- ast.Transform: print_Transform,
- ast.Menu: print_Menu,
- ast.Pass: print_Pass,
- ast.Call: print_Call,
- ast.If: print_If,
- ast.While: print_While,
- ast.Define: print_Define,
- ast.EarlyPython: print_EarlyPython,
- }
-
-def print_Unknown(f, stmt, indent_level):
- print(("Unknown AST node: %s" % (type(stmt).__name__, )))
- f.write("<<<UNKNOWN NODE %s>>>\n" % (type(stmt).__name__, ))
diff --git a/unrpyc/find-gzip.sh b/unrpyc/find-gzip.sh
deleted file mode 100755
index c70b75a..0000000
--- a/unrpyc/find-gzip.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh
-if command -v zopfli; then
- :
-elif G=$(command -v gzip); [ -n "$G" ]; then
- echo >&2 "Consider obtaining zopfli (https://github.com/Hello71/zopfli) for higher"
- echo >&2 "compression ratios (about 5% decrease in size of script.json.gz)."
- echo "$G" -9
-else
- echo >&2 "Could not find zopfli or gzip, aborting."
- exit 1
-fi
diff --git a/unrpyc/fix.js b/unrpyc/fix.js
deleted file mode 100755
index 88a9646..0000000
--- a/unrpyc/fix.js
+++ /dev/null
@@ -1,6 +0,0 @@
-var fs = require('fs');
-
-fs.readFile(process.argv[2], function (err, data) {
- if (err) throw err;
- fs.writeFile(process.argv[3], JSON.stringify(eval('(' + data + ')')));
-});
diff --git a/unrpyc/renpy/display/__init__.py b/unrpyc/renpy/display/__init__.py
deleted file mode 100644
index 3420500..0000000
--- a/unrpyc/renpy/display/__init__.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2004-2013 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.
-
-# The draw object through which all drawing is routed. This object
-# contains all of the distinction between the software and GL
-# renderers.
-draw = None
-
-# The interface object.
-interface = None
-
-# Should we disable imagedissolve-type transitions?
-less_imagedissolve = False
diff --git a/unrpyc/renpy/display/accelerator.pyx b/unrpyc/renpy/display/accelerator.pyx
deleted file mode 100644
index 8c4172c..0000000
--- a/unrpyc/renpy/display/accelerator.pyx
+++ /dev/null
@@ -1,296 +0,0 @@
-#cython: profile=False
-# Copyright 2004-2013 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 renpy
-import math
-from renpy.display.render cimport Render, Matrix2D, render
-
-
-################################################################################
-# Surface copying
-################################################################################
-
-from pygame cimport *
-
-def nogil_copy(src, dest):
- """
- Does a gil-less blit of src to dest, with minimal locking.
- """
-
- cdef SDL_Surface *src_surf
- cdef SDL_Surface *dst_surf
-
- src_surf = PySurface_AsSurface(src)
- dest_surf = PySurface_AsSurface(dest)
-
- old_alpha = src_surf.flags & SDL_SRCALPHA
-
- if old_alpha:
- SDL_SetAlpha(src_surf, 0, 255)
-
- with nogil:
- SDL_BlitSurface(src_surf, NULL, dest_surf, NULL)
-
- if old_alpha:
- SDL_SetAlpha(src_surf, SDL_SRCALPHA, 255)
-
-
-
-################################################################################
-# Transform render function
-################################################################################
-
-cdef Matrix2D IDENTITY
-IDENTITY = renpy.display.render.IDENTITY
-
-# This file contains implementations of methods of classes that
-# are found in other files, for performance reasons.
-
-def transform_render(self, widtho, heighto, st, at):
-
- cdef double rxdx, rxdy, rydx, rydy
- cdef double cosa, sina
- cdef double xo, x1, x2, x3, px
- cdef double yo, y1, y2, y3, py
- cdef float zoom, xzoom, yzoom
- cdef double cw, ch, nw, nh
- cdef Render rv, cr
- cdef double angle
- cdef double alpha
- cdef double width = widtho
- cdef double height = heighto
-
- # Should we perform clipping?
- clipping = False
-
- # Prevent time from ticking backwards, as can happen if we replace a
- # transform but keep its state.
- if st + self.st_offset <= self.st:
- self.st_offset = self.st - st
- if at + self.at_offset <= self.at:
- self.at_offset = self.at - at
-
- self.st = st = st + self.st_offset
- self.at = at = at + self.at_offset
-
- # Update the state.
- self.update_state()
-
- # Render the child.
- child = self.child
-
- if child is None:
- raise Exception("Transform does not have a child.")
-
- state = self.state
-
- if state.size:
- widtho, heighto = state.size
-
- cr = render(child, widtho, heighto, st - self.child_st_base, at)
-
- width = cr.width
- height = cr.height
-
- self.child_size = width, height
-
- # The reverse matrix.
- rxdx = 1
- rxdy = 0
- rydx = 0
- rydy = 1
-
- xo = 0
- yo = 0
-
- # Cropping.
- crop = state.crop
- if (state.corner1 is not None) and (crop is None) and (state.corner2 is not None):
- x1, y1 = state.corner1
- x2, y2 = state.corner2
-
- if x1 > x2:
- x3 = x1
- x1 = x2
- x2 = x3
- if y1 > y2:
- y3 = y1
- y1 = y2
- y2 = y3
-
- crop = (x1, y1, x2-x1, y2-y1)
-
- if crop is not None:
-
- negative_xo, negative_yo, width, height = crop
-
- if state.rotate:
- clipcr = Render(width, height)
- clipcr.subpixel_blit(cr, (-negative_xo, -negative_yo))
- clipcr.clipping = True
- cr = clipcr
- else:
- xo = -negative_xo
- yo = -negative_yo
- clipping = True
-
- # Size.
- size = state.size
- if (size is not None) and (size != (width, height)):
- nw, nh = size
- xzoom = 1.0 * nw / width
- yzoom = 1.0 * nh / height
-
- rxdx = xzoom
- rydy = yzoom
-
- xo *= xzoom
- yo *= yzoom
-
- width, height = size
-
- # zoom
- zoom = state.zoom
- xzoom = zoom * <double> state.xzoom
- yzoom = zoom * <double> state.yzoom
-
- if xzoom != 1:
-
- rxdx *= xzoom
-
- if xzoom < 0:
- width *= -xzoom
- else:
- width *= xzoom
-
- xo *= xzoom
- # origin corrections for flipping
- if xzoom < 0:
- xo += width
-
- if yzoom != 1:
-
- rydy *= yzoom
-
- if yzoom < 0:
- height *= -yzoom
- else:
- height *= yzoom
-
- yo *= yzoom
- # origin corrections for flipping
- if yzoom < 0:
- yo += height
-
- # Rotation.
- rotate = state.rotate
- if rotate is not None:
-
- cw = width
- ch = height
-
- angle = rotate * 3.1415926535897931 / 180
-
- cosa = math.cos(angle)
- sina = math.sin(angle)
-
- # reverse = Matrix2D(xdx, xdy, ydx, ydy) * reverse
-
- # We know that at this point, rxdy and rydx are both 0, so
- # we can simplify these formulae a bit.
- rxdy = rydy * -sina
- rydx = rxdx * sina
- rxdx *= cosa
- rydy *= cosa
-
- # first corner point (changes with flipping)
- px = cw / 2.0
- if xzoom < 0:
- px = -px
- py = ch / 2.0
- if yzoom < 0:
- py = -py
-
- if state.rotate_pad:
- width = height = math.hypot(cw, ch)
-
- xo = -px * cosa + py * sina
- yo = -px * sina - py * cosa
-
- else:
- xo = -px * cosa + py * sina
- yo = -px * sina - py * cosa
-
- x2 = -px * cosa - py * sina
- y2 = -px * sina + py * cosa
-
- x3 = px * cosa - py * sina
- y3 = px * sina + py * cosa
-
- x4 = px * cosa + py * sina
- y4 = px * sina - py * cosa
-
- width = max(xo, x2, x3, x4) - min(xo, x2, x3, x4)
- height = max(yo, y2, y3, y4) - min(yo, y2, y3, y4)
-
- xo += width / 2.0
- yo += height / 2.0
-
- alpha = state.alpha
-
- rv = Render(width, height)
-
- # Default case - no transformation matrix.
- if rxdx == 1 and rxdy == 0 and rydx == 0 and rydy == 1:
- self.forward = IDENTITY
- self.reverse = IDENTITY
-
- else:
-
- self.reverse = rv.reverse = Matrix2D(rxdx, rxdy, rydx, rydy)
-
- inv_det = rxdx * rydy - rxdy * rydx
-
- if not inv_det:
- self.forward = rv.forward = Matrix2D(0, 0, 0, 0)
- else:
- self.forward = rv.forward = Matrix2D(
- rydy / inv_det,
- -rxdy / inv_det,
- -rydx / inv_det,
- rxdx / inv_det)
-
- rv.alpha = alpha
- rv.clipping = clipping
-
- pos = (xo, yo)
-
- if state.subpixel:
- rv.subpixel_blit(cr, pos)
- else:
- rv.blit(cr, pos)
-
- self.offsets = [ pos ]
- self.render_size = (width, height)
-
- return rv
-
diff --git a/unrpyc/renpy/display/anim.py b/unrpyc/renpy/display/anim.py
deleted file mode 100644
index de1398a..0000000
--- a/unrpyc/renpy/display/anim.py
+++ /dev/null
@@ -1,634 +0,0 @@
-# Copyright 2004-2013 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.
-
-# This file contains support for state-machine controlled animations.
-
-import renpy.display
-import random
-
-class State(object):
- """
- This creates a state that can be used in a SMAnimation.
- """
-
-
- def __init__(self, name, image, *atlist, **properties):
- """
- @param name: A string giving the name of this state.
-
- @param image: The displayable that is shown to the user while
- we are in (entering) this state. For convenience, this can
- also be a string or tuple, which is interpreted with Image.
-
- image should be None when this State is used with motion,
- to indicate that the image will be replaced with the child of
- the motion.
-
- @param atlist: A list of functions to call on the image. (In
- general, if something can be used in an at clause, it can be
- used here as well.)
-
- If any keyword arguments are given, they are used to construct a
- Position object, that modifies the position of the image.
- """
-
- if image and not isinstance(image, renpy.display.core.Displayable):
- image = renpy.easy.displayable(image)
-
- self.name = name
- self.image = image
- self.atlist = atlist
- self.properties = properties
-
-
- def add(self, sma):
- sma.states[self.name] = self
-
- def get_image(self):
- rv = self.image
-
- for i in self.atlist:
- rv = i(rv)
-
- if self.properties:
- rv = renpy.display.layout.Position(rv, **self.properties)
-
- return rv
-
- def motion_copy(self, child):
-
- if self.image is not None:
- child = self.image
-
- return State(self.name, child, *self.atlist)
-
-
-class Edge(object):
- """
- This creates an edge that can be used with a SMAnimation.
- """
-
- def __init__(self, old, delay, new, trans=None, prob=1):
- """
- @param old: The name (a string) of the state that this transition is from.
-
- @param delay: The number of seconds that this transition takes.
-
- @param new: The name (a string) of the state that this transition is to.
-
- @param trans: The transition that will be used to show the
- image found in the new state. If None, the image is show
- immediately.
-
- When used with an SMMotion, the transition should probably be
- move.
-
- @param prob: The number of times this edge is added. This can
- be used to make a transition more probable then others. For
- example, if one transition out of a state has prob=5, and the
- other has prob=1, then the one with prob=5 will execute 5/6 of
- the time, while the one with prob=1 will only occur 1/6 of the
- time. (Don't make this too large, as memory use is proportional to
- this value.)
- """
-
- self.old = old
- self.delay = delay
- self.new = new
- self.trans = trans
- self.prob = prob
-
- def add(self, sma):
- for _i in range(0, self.prob):
- sma.edges.setdefault(self.old, []).append(self)
-
-
-class SMAnimation(renpy.display.core.Displayable):
- """
- This creates a state-machine animation. Such an animation is
- created by randomly traversing the edges between states in a
- defined state machine. Each state corresponds to an image shown to
- the user, with the edges corresponding to the amount of time an
- image is shown, and the transition it is shown with.
-
- Images are shown, perhaps with a transition, when we are
- transitioning into a state containing that image.
- """
-
- def __init__(self, initial, *args, **properties):
- """
- @param initial: The name (a string) of the initial state we
- start in.
-
- @param showold: If the keyword parameter showold is True, then
- the old image is shown instead of the new image when in an
- edge.
-
- @param anim_timebase: If True, we use the animation
- timebase. If False, we use the displayable timebase.
-
- This accepts as additional arguments the anim.State and
- anim.Edge objects that are used to make up this state
- machine.
- """
-
- if 'delay' in properties:
- self.delay = properties['delay']
- del properties['delay']
- else:
- self.delay = None
-
- if 'showold' in properties:
- self.showold = properties['showold']
- del properties['showold']
- else:
- self.showold = False
-
- if 'anim_timebase' in properties:
- self.anim_timebase = properties['anim_timebase']
- del properties['anim_timebase']
- else:
- self.anim_timebase = True
-
- super(SMAnimation, self).__init__(**properties)
-
- self.properties = properties
-
- # The initial state.
- self.initial = initial
-
- # A map from state name to State object.
- self.states = { }
-
- # A map from state name to list of Edge objects.
- self.edges = { }
-
- for i in args:
- i.add(self)
-
- # The time at which the current edge started. If None, will be
- # set to st by render.
- self.edge_start = None
-
- # A cache for what the current edge looks like when rendered.
- self.edge_cache = None
-
- # The current edge.
- self.edge = None
-
- # The state we're in.
- self.state = None
-
- def visit(self):
- return [ i.image for i in self.states.values() ]
-
- def pick_edge(self, state):
- """
- This randomly picks an edge out of the given state, if
- one exists. It updates self.edge if a transition has
- been selected, or returns None if none can be found. It also
- updates self.image to be the new image on the selected edge.
- """
-
- if state not in self.edges:
- self.edge = None
- return
-
- edges = self.edges[state]
- self.edge = random.choice(edges)
- self.state = self.edge.new
-
- def update_cache(self):
- """
- Places the correct Displayable into the edge cache, based on
- what is contained in the given edge. This takes into account
- the old and new states, and any transition that is present.
- """
-
-
- if self.edge.trans:
- im = self.edge.trans(old_widget=self.states[self.edge.old].get_image(),
- new_widget=self.states[self.edge.new].get_image())
- elif self.showold:
- im = self.states[self.edge.old].get_image()
- else:
- im = self.states[self.edge.new].get_image()
-
- self.edge_cache = im
-
- def get_placement(self):
-
- if self.edge_cache:
- return self.edge_cache.get_placement()
-
- if self.state:
- return self.states[self.state].get_image().get_placement()
-
- return super(SMAnimation, self).get_placement()
-
- def render(self, width, height, st, at):
-
- if self.anim_timebase:
- t = at
- else:
- t = st
-
- if self.edge_start is None or t < self.edge_start:
- self.edge_start = t
- self.edge_cache = None
- self.pick_edge(self.initial)
-
- while self.edge and t > self.edge_start + self.edge.delay:
- self.edge_start += self.edge.delay
- self.edge_cache = None
- self.pick_edge(self.edge.new)
-
- # If edge is None, then we have a permanent, static picture. Deal
- # with that.
-
- if not self.edge:
- im = renpy.display.render.render(self.states[self.state].get_image(),
- width, height,
- st - self.edge_start, at)
-
-
- # Otherwise, we have another edge.
-
- else:
- if not self.edge_cache:
- self.update_cache()
-
- im = renpy.display.render.render(self.edge_cache, width, height, t - self.edge_start, at)
-
- if not renpy.game.less_updates:
- renpy.display.render.redraw(self.edge_cache, self.edge.delay - (t - self.edge_start))
-
-
- iw, ih = im.get_size()
-
- rv = renpy.display.render.Render(iw, ih)
- rv.blit(im, (0, 0))
-
- return rv
-
- def __call__(self, child=None, new_widget=None, old_widget=None):
- """
- Used when this SMAnimation is used as a SMMotion. This creates
- a duplicate of the animation, with all states containing None
- as the image having that None replaced with the image that is provided here.
- """
-
- if child is None:
- child = new_widget
-
- args = [ ]
-
- for state in self.states.values():
- args.append(state.motion_copy(child))
-
- for edges in self.edges.values():
- args.extend(edges)
-
- return SMAnimation(self.initial, delay=self.delay, *args, **self.properties)
-
-
-# class Animation(renpy.display.core.Displayable):
-# """
-# A Displayable that draws an animation, which is a series of images
-# that are displayed with time delays between them.
-# """
-
-# def __init__(self, *args, **properties):
-# """
-# Odd (first, third, fifth, etc.) arguments to Animation are
-# interpreted as image filenames, while even arguments are the
-# time to delay between each image. If the number of arguments
-# is odd, the animation will stop with the last image (well,
-# actually delay for a year before looping). Otherwise, the
-# animation will restart after the final delay time.
-
-# @param anim_timebase: If True, the default, use the animation
-# timebase. Otherwise, use the displayable timebase.
-# """
-
-# properties.setdefault('style', 'animation')
-# self.anim_timebase = properties.pop('anim_timebase', True)
-
-# super(Animation, self).__init__(**properties)
-
-# self.images = [ ]
-# self.delays = [ ]
-
-# for i, arg in enumerate(args):
-
-# if i % 2 == 0:
-# self.images.append(renpy.easy.displayable(arg))
-# else:
-# self.delays.append(arg)
-
-# if len(self.images) > len(self.delays):
-# self.delays.append(365.25 * 86400.0) # One year, give or take.
-
-# def render(self, width, height, st, at):
-
-# if self.anim_timebase:
-# t = at % sum(self.delays)
-# else:
-# t = st % sum(self.delays)
-
-# for image, delay in zip(self.images, self.delays):
-# if t < delay:
-# renpy.display.render.redraw(self, delay - t)
-
-# im = renpy.display.render.render(image, width, height, t, at)
-# width, height = im.get_size()
-# rv = renpy.display.render.Render(width, height)
-# rv.blit(im, (0, 0))
-
-# return rv
-
-# else:
-# t = t - delay
-
-# def visit(self):
-# return self.images
-
-def Animation(*args, **kwargs):
- newargs = [ ]
-
- for i, a in enumerate(args):
- newargs.append(a)
- if i % 2 == 1:
- newargs.append(None)
-
- return TransitionAnimation(*newargs, **kwargs)
-
-
-class TransitionAnimation(renpy.display.core.Displayable):
- """
- A displayable that draws an animation with each frame separated
- by a transition.
- """
-
- def __init__(self, *args, **properties):
- """
- This takes arguments such that the 1st, 4th, 7th, ...
- arguments are displayables, the 2nd, 5th, 8th, ... on arguments
- are times, and the 3rd, 6th, 9th, ... are transitions.
-
- This displays the first displayable for the given time, then
- transitions to the second displayable using the given
- transition, and shows it for the given time (the time of the
- transition is taken out of the time the frame is shown), and
- so on.
-
- The last argument may be a displayable (in which case that
- displayable is used to transition back to the first frame), or
- a displayable (which is shown forever).
-
- There is one keyword argument, apart from the style properties:
-
- @param anim_timebase: If True, the default, use the animation
- timebase. Otherwise, use the displayable timebase.
- """
-
- properties.setdefault('style', 'animation')
- self.anim_timebase = properties.pop('anim_timebase', True)
-
- super(TransitionAnimation, self).__init__(**properties)
-
- images = [ ]
- delays = [ ]
- transitions = [ ]
-
- for i, arg in enumerate(args):
-
- if i % 3 == 0:
- images.append(renpy.easy.displayable(arg))
- elif i % 3 == 1:
- delays.append(arg)
- else:
- transitions.append(arg)
-
- if len(images) > len(delays):
- delays.append(365.25 * 86400.0) # One year, give or take.
- if len(images) > len(transitions):
- transitions.append(None)
-
- self.images = images
- self.prev_images = [ images[-1] ] + images[:-1]
- self.delays = delays
- self.transitions = [ transitions[-1] ] + transitions[:-1]
-
-
- def render(self, width, height, st, at):
-
- if self.anim_timebase:
- orig_t = at
- else:
- orig_t = st
-
- t = orig_t % sum(self.delays)
-
- for image, prev, delay, trans in zip(self.images, self.prev_images, self.delays, self.transitions):
- if t < delay:
- if not renpy.game.less_updates:
- renpy.display.render.redraw(self, delay - t)
-
- if trans and orig_t >= self.delays[0]:
- image = trans(old_widget=prev, new_widget=image)
-
- im = renpy.display.render.render(image, width, height, t, at)
- width, height = im.get_size()
- rv = renpy.display.render.Render(width, height)
- rv.blit(im, (0, 0))
-
- return rv
-
- else:
- t = t - delay
-
- def visit(self):
- return self.images
-
-class Blink(renpy.display.core.Displayable):
- """
- """
-
- def __init__(self, image, on=0.5, off=0.5, rise=0.5, set=0.5, #@ReservedAssignment
- high=1.0, low=0.0, offset=0.0, anim_timebase=False, **properties):
-
- """
- This takes as an argument an image or widget, and blinks that image
- by varying its alpha. The sequence of phases is
- on - set - off - rise - on - ... All times are given in seconds, all
- alphas are fractions between 0 and 1.
-
- @param image: The image or widget that will be blinked.
-
- @param on: The amount of time the widget spends on, at high alpha.
-
- @param off: The amount of time the widget spends off, at low alpha.
-
- @param rise: The amount time the widget takes to ramp from low to high alpha.
-
- @param set: The amount of time the widget takes to ram from high to low.
-
- @param high: The high alpha.
-
- @param low: The low alpha.
-
- @param offset: A time offset, in seconds. Use this to have a
- blink that does not start at the start of the on phase.
-
- @param anim_timebase: If True, use the animation timebase, if false, the displayable timebase.
- """
-
- super(Blink, self).__init__(**properties)
-
- self.image = renpy.easy.displayable(image)
- self.on = on
- self.off = off
- self.rise = rise
- self.set = set
- self.high = high
- self.low = low
- self.offset = offset
- self.anim_timebase = anim_timebase
-
- self.cycle = on + set + off + rise
-
-
- def visit(self):
- return [ self.image ]
-
- def render(self, height, width, st, at):
-
- if self.anim_timebase:
- t = at
- else:
- t = st
-
- time = (self.offset + t) % self.cycle
- alpha = self.high
-
- if 0 <= time < self.on:
- delay = self.on - time
- alpha = self.high
-
- time -= self.on
-
- if 0 <= time < self.set:
- delay = 0
- frac = time / self.set
- alpha = self.low * frac + self.high * (1.0 - frac)
-
- time -= self.set
-
- if 0 <= time < self.off:
- delay = self.off - time
- alpha = self.low
-
- time -= self.off
-
- if 0 <= time < self.rise:
- delay = 0
- frac = time / self.rise
- alpha = self.high * frac + self.low * (1.0 - frac)
-
-
- rend = renpy.display.render.render(self.image, height, width, st, at)
- w, h = rend.get_size()
- rv = renpy.display.render.Render(w, h)
-
- rv.blit(rend, (0, 0))
- rv.alpha = alpha
-
- if not renpy.game.less_updates:
- renpy.display.render.redraw(self, delay)
-
- return rv
-
-
-
-def Filmstrip(image, framesize, gridsize, delay, frames=None, loop=True, **properties):
- """
- This creates an animation from a single image. This image
- must consist of a grid of frames, with the number of columns and
- rows in the grid being taken from gridsize, and the size of each
- frame in the grid being taken from framesize. This takes frames
- and sticks them into an Animation, with the given delay between
- each frame. The frames are taken by going from left-to-right
- across the first row, left-to-right across the second row, and
- so on until all frames are consumed, or a specified number of
- frames are taken.
-
- @param image: The image that the frames must be taken from.
-
- @param framesize: A (width, height) tuple giving the size of
- each of the frames in the animation.
-
- @param gridsize: A (columns, rows) tuple giving the number of
- columns and rows in the grid.
-
- @param delay: The delay, in seconds, between frames.
-
- @param frames: The number of frames in this animation. If None,
- then this defaults to colums * rows frames, that is, taking
- every frame in the grid.
-
- @param loop: If True, loop at the end of the animation. If False,
- this performs the animation once, and then stops.
-
- Other keyword arguments are as for anim.SMAnimation.
- """
-
- width, height = framesize
- cols, rows = gridsize
-
- if frames is None:
- frames = cols * rows
-
- i = 0
-
- # Arguments to Animation
- args = [ ]
-
- for r in range(0, rows):
- for c in range(0, cols):
-
- x = c * width
- y = r * height
-
- args.append(renpy.display.im.Crop(image, x, y, width, height))
- args.append(delay)
-
- i += 1
- if i == frames:
- break
-
- if i == frames:
- break
-
- if not loop:
- args.pop()
-
- return Animation(*args, **properties)
diff --git a/unrpyc/renpy/display/behavior.py b/unrpyc/renpy/display/behavior.py
deleted file mode 100644
index 02eccf2..0000000
--- a/unrpyc/renpy/display/behavior.py
+++ /dev/null
@@ -1,1531 +0,0 @@
-# Copyright 2004-2013 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.
-
-# This contains various Displayables that handle events.
-
-
-import renpy.display
-import renpy.audio
-
-from renpy.display.render import render, Render
-
-import pygame
-
-def compile_event(key, keydown):
- """
- Compiles a keymap entry into a python expression.
-
- keydown determines if we are dealing with keys going down (press),
- or keys going up (release).
- """
-
- # Lists or tuples get turned into or expressions.
- if isinstance(key, (list, tuple)):
- if not key:
- return "(False)"
-
- return "(" + " or ".join([compile_event(i, keydown) for i in key]) + ")"
-
- # If it's in config.keymap, compile what's in config.keymap.
- if key in renpy.config.keymap:
- return compile_event(renpy.config.keymap[key], keydown)
-
- if key is None:
- return "(False)"
-
- part = key.split("_")
-
- # Deal with the mouse.
- if part[0] == "mousedown":
- if keydown:
- return "(ev.type == %d and ev.button == %d)" % (pygame.MOUSEBUTTONDOWN, int(part[1]))
- else:
- return "(False)"
-
- if part[0] == "mouseup":
- if keydown:
- return "(ev.type == %d and ev.button == %d)" % (pygame.MOUSEBUTTONUP, int(part[1]))
- else:
- return "(False)"
-
- # Deal with the Joystick.
- if part[0] == "joy":
- if keydown:
- return "(ev.type == %d and ev.press and ev.press == renpy.game.preferences.joymap.get(%r, None))" % (renpy.display.core.JOYEVENT, key)
- else:
- return "(ev.type == %d and ev.release and ev.release == renpy.game.preferences.joymap.get(%r, None))" % (renpy.display.core.JOYEVENT, key)
-
- # Otherwise, deal with it as a key.
- if keydown:
- rv = "(ev.type == %d" % pygame.KEYDOWN
- else:
- rv = "(ev.type == %d" % pygame.KEYUP
-
- if part[0] == "alt":
- part.pop(0)
- rv += " and (ev.mod & %d)" % pygame.KMOD_ALT
- else:
- rv += " and not (ev.mod & %d)" % pygame.KMOD_ALT
-
- if part[0] == "meta":
- part.pop(0)
- rv += " and (ev.mod & %d)" % pygame.KMOD_META
- else:
- rv += " and not (ev.mod & %d)" % pygame.KMOD_META
-
- if part[0] == "shift":
- part.pop(0)
- rv += " and (ev.mod & %d)" % pygame.KMOD_SHIFT
-
- if part[0] == "noshift":
- part.pop(0)
- rv += " and not (ev.mod & %d)" % pygame.KMOD_SHIFT
-
- if len(part) == 1:
- if len(part[0]) != 1:
- if renpy.config.developer:
- raise Exception("Invalid key specifier %s" % key)
- else:
- return "(False)"
-
- rv += " and ev.unicode == %r)" % part[0]
-
- else:
- if part[0] != "K":
- if renpy.config.developer:
- raise Exception("Invalid key specifier %s" % key)
- else:
- return "(False)"
-
- key = "_".join(part)
-
- rv += " and ev.key == %d)" % (getattr(pygame.constants, key))
-
- return rv
-
-# These store a lambda for each compiled key in the system.
-event_cache = { }
-keyup_cache = { }
-
-def map_event(ev, name):
- """Returns true if the event matches the named keycode being pressed."""
-
- check_code = event_cache.get(name, None)
- if check_code is None:
- check_code = eval("lambda ev : " + compile_event(name, True), globals())
- event_cache[name] = check_code
-
- return check_code(ev)
-
-def map_keyup(ev, name):
- """Returns true if the event matches the named keycode being released."""
-
- check_code = keyup_cache.get(name, None)
- if check_code is None:
- check_code = eval("lambda ev : " + compile_event(name, False), globals())
- keyup_cache[name] = check_code
-
- return check_code(ev)
-
-
-def skipping(ev):
- """
- This handles setting skipping in response to the press of one of the
- CONTROL keys. The library handles skipping in response to TAB.
- """
-
- if not renpy.config.allow_skipping:
- return
-
- if map_event(ev, "skip"):
- renpy.config.skipping = "slow"
- renpy.exports.restart_interaction()
-
- if map_keyup(ev, "skip"):
- renpy.config.skipping = None
- renpy.exports.restart_interaction()
-
- return
-
-
-def inspector(ev):
- return map_event(ev, "inspector")
-
-
-##############################################################################
-# Utility functions for dealing with actions.
-
-def predict_action(var):
- """
- Predicts some of the actions that may be caused by a variable.
- """
-
- if var is None:
- return
-
- if isinstance(var, renpy.ui.Action):
- var.predict()
-
- if isinstance(var, (list, tuple)):
- for i in var:
- predict_action(i)
-
-def run(var, *args, **kwargs):
- """
- Runs a variable. This is done by calling all the functions, and
- iterating over the lists and tuples.
- """
-
- if var is None:
- return None
-
- if isinstance(var, (list, tuple)):
- rv = None
-
- for i in var:
- new_rv = run(i, *args, **kwargs)
-
- if new_rv is not None:
- rv = new_rv
-
- return rv
-
- return var(*args, **kwargs)
-
-def run_unhovered(var):
- """
- Calls the unhovered method on the variable, if it exists.
- """
-
- if var is None:
- return None
-
- if isinstance(var, (list, tuple)):
- for i in var:
- run_unhovered(i)
-
- return
-
- f = getattr(var, "unhovered", None)
- if f is not None:
- f()
-
-def run_periodic(var, st):
-
- if isinstance(var, (list, tuple)):
- rv = None
-
- for i in var:
- v = run_periodic(i, st)
-
- if rv is None or v < rv:
- rv = v
-
- return rv
-
- if isinstance(var, renpy.ui.Action):
- return var.periodic(st)
-
-
-def is_selected(clicked):
-
- if isinstance(clicked, (list, tuple)):
- return any(is_selected(i) for i in clicked)
-
- elif isinstance(clicked, renpy.ui.Action):
- return clicked.get_selected()
- else:
- return False
-
-
-def is_sensitive(clicked):
-
- if isinstance(clicked, (list, tuple)):
- return all(is_sensitive(i) for i in clicked)
-
- elif isinstance(clicked, renpy.ui.Action):
- return clicked.get_sensitive()
- else:
- return True
-
-
-##############################################################################
-# Special-Purpose Displayables
-
-class Keymap(renpy.display.layout.Null):
- """
- This is a behavior that maps keys to actions that are called when
- the key is pressed. The keys are specified by giving the appropriate
- k_constant from pygame.constants, or the unicode for the key.
- """
-
- def __init__(self, replaces=None, **keymap):
- super(Keymap, self).__init__(style='default')
- self.keymap = keymap
-
- def event(self, ev, x, y, st):
-
- for name, action in self.keymap.items():
- if map_event(ev, name):
-
- rv = run(action)
-
- if rv is not None:
- return rv
-
- raise renpy.display.core.IgnoreEvent()
-
- def predict_one_action(self):
- for i in self.keymap.values():
- predict_action(i)
-
-
-class RollForward(renpy.display.layout.Null):
- """
- This behavior implements rollforward.
- """
-
- def __init__(self, value, **properties):
- super(RollForward, self).__init__(**properties)
- self.value = value
-
-
- def event(self, ev, x, y, st):
-
- if map_event(ev, "rollforward"):
- renpy.game.interface.suppress_transition = True
- renpy.game.after_rollback = True
- renpy.game.log.rolled_forward = True
- return self.value
-
-
-class PauseBehavior(renpy.display.layout.Null):
- """
- This is a class implementing the Pause behavior, which is to
- return a value after a certain amount of time has elapsed.
- """
-
- def __init__(self, delay, result=False, **properties):
- super(PauseBehavior, self).__init__(**properties)
-
- self.delay = delay
- self.result = result
-
- def event(self, ev, x, y, st):
-
- if st >= self.delay:
-
- # If we have been drawn since the timeout, simply return
- # true. Otherwise, force a redraw, and return true when
- # it comes back.
- if renpy.game.interface.drawn_since(st - self.delay):
- return self.result
- else:
- renpy.game.interface.force_redraw = True
-
-
- renpy.game.interface.timeout(max(self.delay - st, 0))
-
-class SoundStopBehavior(renpy.display.layout.Null):
- """
- This is a class implementing the sound stop behavior,
- which is to return False when a sound is no longer playing
- on the named channel.
- """
-
- def __init__(self, channel, result=False, **properties):
- super(SoundStopBehavior, self).__init__(**properties)
-
- self.channel = channel
- self.result = result
-
-
- def event(self, ev, x, y, st):
-
- if not renpy.audio.music.get_playing(self.channel):
- return self.result
-
- renpy.game.interface.timeout(.025)
-
-
-class SayBehavior(renpy.display.layout.Null):
- """
- This is a class that implements the say behavior,
- which is to return True (ending the interaction) if
- the user presses space or enter, or clicks the left
- mouse button.
- """
-
- focusable = True
-
- def __init__(self, default=True, afm=None, dismiss=[ 'dismiss' ], allow_dismiss=None, **properties):
- super(SayBehavior, self).__init__(default=default, **properties)
-
- if not isinstance(dismiss, (list, tuple)):
- dismiss = [ dismiss ]
-
- if afm is not None:
- self.afm_length = len(afm)
- else:
- self.afm_length = None
-
- # What keybindings lead to dismissal?
- self.dismiss = dismiss
-
- self.allow_dismiss = allow_dismiss
-
- def set_afm_length(self, afm_length):
- self.afm_length = max(afm_length, 1)
-
- def event(self, ev, x, y, st):
-
- if self.afm_length and renpy.game.preferences.afm_time and renpy.game.preferences.afm_enable:
-
- afm_delay = ( 1.0 * ( renpy.config.afm_bonus + self.afm_length ) / renpy.config.afm_characters ) * renpy.game.preferences.afm_time
-
- if renpy.game.preferences.text_cps:
- afm_delay += 1.0 / renpy.game.preferences.text_cps * self.afm_length
-
- if st > afm_delay:
- if renpy.config.afm_callback:
- if renpy.config.afm_callback():
- return True
- else:
- renpy.game.interface.timeout(0.1)
- else:
- return True
- else:
- renpy.game.interface.timeout(afm_delay - st)
-
- for dismiss in self.dismiss:
-
- if map_event(ev, dismiss) and self.is_focused():
-
- if renpy.config.skipping:
- renpy.config.skipping = None
- renpy.exports.restart_interaction()
- raise renpy.display.core.IgnoreEvent()
-
- if renpy.game.preferences.using_afm_enable and renpy.game.preferences.afm_enable:
- renpy.game.preferences.afm_enable = False
- renpy.exports.restart_interaction()
- raise renpy.display.core.IgnoreEvent()
-
- if self.allow_dismiss:
- if not self.allow_dismiss():
- raise renpy.display.core.IgnoreEvent()
-
- return True
-
- skip_delay = renpy.config.skip_delay / 1000.0
-
- if renpy.config.allow_skipping and renpy.config.skipping:
-
- if st >= skip_delay:
- if renpy.game.preferences.skip_unseen:
- return True
- elif renpy.config.skipping == "fast":
- return True
- elif renpy.game.context().seen_current(True):
- return True
- else:
- renpy.game.interface.timeout(skip_delay - st)
-
-
- return None
-
-
-##############################################################################
-# Button
-
-class Button(renpy.display.layout.Window):
-
- keymap = { }
- action = None
-
- def __init__(self, child=None, style='button', clicked=None,
- hovered=None, unhovered=None, action=None, role=None,
- time_policy=None, keymap={},
- **properties):
-
- super(Button, self).__init__(child, style=style, **properties)
-
- if isinstance(clicked, renpy.ui.Action):
- action = clicked
-
- if action is not None:
- clicked = action
-
- if not is_sensitive(action):
- clicked = None
-
- if role is None:
- if action:
- if is_selected(action):
- role = 'selected_'
- else:
- role = ''
- else:
- role = ''
-
- self.action = action
- self.activated = False
- self.clicked = clicked
- self.hovered = hovered
- self.unhovered = unhovered
- self.focusable = clicked is not None
- self.role = role
- self.keymap = keymap
-
- self.time_policy_data = None
-
- def predict_one_action(self):
- predict_action(self.clicked)
- predict_action(self.hovered)
- predict_action(self.unhovered)
-
- if self.keymap:
- for v in self.keymap.values():
- predict_action(v)
-
- def render(self, width, height, st, at):
-
- if self.style.time_policy:
- st, self.time_policy_data = self.style.time_policy(st, self.time_policy_data, self.style)
-
- rv = super(Button, self).render(width, height, st, at)
-
- if self.clicked:
-
- rect = self.style.focus_rect
- if rect is not None:
- fx, fy, fw, fh = rect
- else:
- fx = self.style.left_margin
- fy = self.style.top_margin
- fw = rv.width - self.style.right_margin
- fh = rv.height - self.style.bottom_margin
-
- mask = self.style.focus_mask
-
- if mask is True:
- mask = rv
- elif mask is not None:
- mask = renpy.easy.displayable(mask)
- mask = renpy.display.render.render(mask, rv.width, rv.height, st, at)
-
- if mask is not None:
- fmx = 0
- fmy = 0
- else:
- fmx = None
- fmy = None
-
- rv.add_focus(self, None,
- fx, fy, fw, fh,
- fmx, fmy, mask)
-
- return rv
-
-
- def focus(self, default=False):
- super(Button, self).focus(default)
-
- if self.activated:
- return None
-
- rv = None
-
- if not default:
- rv = run(self.hovered)
-
- self.set_transform_event(self.role + "hover")
- self.child.set_transform_event(self.role + "hover")
-
- return rv
-
-
- def unfocus(self, default=False):
- super(Button, self).unfocus(default)
-
- if self.activated:
- return None
-
- if not default:
- run_unhovered(self.hovered)
- run(self.unhovered)
-
- self.set_transform_event(self.role + "idle")
- self.child.set_transform_event(self.role + "idle")
-
-
- def per_interact(self):
- if not self.clicked:
- self.set_style_prefix(self.role + "insensitive_", True)
- else:
- self.set_style_prefix(self.role + "idle_", True)
-
- super(Button, self).per_interact()
-
- def event(self, ev, x, y, st):
-
- # Call self.action.periodic()
- timeout = run_periodic(self.action, st)
-
- if timeout is not None:
- renpy.game.interface.timeout(timeout)
-
- # If we have a child, try passing the event to it. (For keyboard
- # events, this only happens if we're focused.)
- if self.is_focused() or not (ev.type == pygame.KEYDOWN or ev.type == pygame.KEYUP):
- rv = super(Button, self).event(ev, x, y, st)
- if rv is not None:
- return rv
-
- # If not focused, ignore all events.
- if not self.is_focused():
- return None
-
- # Check the keymap.
- for name, action in self.keymap.items():
- if map_event(ev, name):
- return run(action)
-
- # Ignore as appropriate:
- if map_event(ev, "button_ignore") and self.clicked:
- raise renpy.display.core.IgnoreEvent()
-
- # If clicked,
- if map_event(ev, "button_select") and self.clicked:
-
- self.activated = True
- self.style.set_prefix(self.role + 'activate_')
-
- if self.style.sound:
- renpy.audio.music.play(self.style.sound, channel="sound")
-
- rv = run(self.clicked)
-
- if rv is not None:
- return rv
- else:
- self.activated = False
-
- if self.is_focused():
- self.set_style_prefix(self.role + "hover_", True)
- else:
- self.set_style_prefix(self.role + "idle_", True)
-
- raise renpy.display.core.IgnoreEvent()
-
- return None
-
-
- def set_style_prefix(self, prefix, root):
- if root:
- super(Button, self).set_style_prefix(prefix, root)
-
-
-# Reimplementation of the TextButton widget as a Button and a Text
-# widget.
-def TextButton(text, style='button', text_style='button_text',
- clicked=None, **properties):
-
- text = renpy.text.text.Text(text, style=text_style) #@UndefinedVariable
- return Button(text, style=style, clicked=clicked, **properties)
-
-class ImageButton(Button):
- """
- Used to implement the guts of an image button.
- """
-
- def __init__(self,
- idle_image,
- hover_image,
- insensitive_image = None,
- activate_image = None,
- selected_idle_image = None,
- selected_hover_image = None,
- selected_insensitive_image = None,
- selected_activate_image = None,
- style='image_button',
- clicked=None,
- hovered=None,
- **properties):
-
- insensitive_image = insensitive_image or idle_image
- activate_image = activate_image or hover_image
-
- selected_idle_image = selected_idle_image or idle_image
- selected_hover_image = selected_hover_image or hover_image
- selected_insensitive_image = selected_insensitive_image or insensitive_image
- selected_activate_image = selected_activate_image or activate_image
-
- self.state_children = dict(
- idle_ = renpy.easy.displayable(idle_image),
- hover_ = renpy.easy.displayable(hover_image),
- insensitive_ = renpy.easy.displayable(insensitive_image),
- activate_ = renpy.easy.displayable(activate_image),
-
- selected_idle_ = renpy.easy.displayable(selected_idle_image),
- selected_hover_ = renpy.easy.displayable(selected_hover_image),
- selected_insensitive_ = renpy.easy.displayable(selected_insensitive_image),
- selected_activate_ = renpy.easy.displayable(selected_activate_image),
- )
-
- super(ImageButton, self).__init__(renpy.display.layout.Null(),
- style=style,
- clicked=clicked,
- hovered=hovered,
- **properties)
-
- def visit(self):
- return list(self.state_children.values())
-
- def get_child(self):
- return self.style.child or self.state_children[self.style.prefix]
-
-
-# This is used for an input that takes its focus from a button.
-class HoveredProxy(object):
- def __init__(self, a, b):
- self.a = a
- self.b = b
-
- def __call__(self):
- self.a()
- if self.b:
- return self.b()
-
-
-class Input(renpy.text.text.Text): #@UndefinedVariable
- """
- This is a Displayable that takes text as input.
- """
-
- changed = None
- prefix = ""
- suffix = ""
- caret_pos = 0
-
- def __init__(self,
- default="",
- length=None,
- style='input',
- allow=None,
- exclude=None,
- prefix="",
- suffix="",
- changed=None,
- button=None,
- replaces=None,
- editable=True,
- **properties):
-
- super(Input, self).__init__("", style=style, replaces=replaces, substitute=False, **properties)
-
- self.content = str(default)
- self.length = length
-
- self.allow = allow
- self.exclude = exclude
- self.prefix = prefix
- self.suffix = suffix
-
- self.changed = changed
-
- self.editable = editable
-
- caretprops = { 'color' : None }
-
- for i in properties:
- if i.endswith("color"):
- caretprops[i] = properties[i]
-
- self.caret = renpy.display.image.Solid(xmaximum=1, style=style, **caretprops)
- self.caret_pos = len(self.content)
-
- if button:
- self.editable = False
- button.hovered = HoveredProxy(self.enable, button.hovered)
- button.unhovered = HoveredProxy(self.disable, button.unhovered)
-
- if isinstance(replaces, Input):
- self.content = replaces.content
- self.editable = replaces.editable
- self.caret_pos = replaces.caret_pos
-
- self.update_text(self.content, self.editable)
-
-
- def update_text(self, content, editable):
-
- if content != self.content or editable != self.editable:
- renpy.display.render.redraw(self, 0)
-
- if content != self.content:
- self.content = content
-
- if self.changed:
- self.changed(content)
-
- if content == "":
- content = "\u200b"
-
- self.editable = editable
-
- # Choose the caret.
- caret = self.style.caret
- if caret is None:
- caret = self.caret
-
- if editable:
- l = len(content)
- self.set_text([self.prefix, content[0:self.caret_pos].replace("{", "{{"), caret,
- content[self.caret_pos:l].replace("{", "{{"), self.suffix])
- else:
- self.set_text([self.prefix, content.replace("{", "{{"), self.suffix ])
-
- # This is needed to ensure the caret updates properly.
- def set_style_prefix(self, prefix, root):
- if prefix != self.style.prefix:
- self.update_text(self.content, self.editable)
-
- super(Input, self).set_style_prefix(prefix, root)
-
- def enable(self):
- self.update_text(self.content, True)
-
- def disable(self):
- self.update_text(self.content, False)
-
- def event(self, ev, x, y, st):
-
- if not self.editable:
- return None
-
- l = len(self.content)
-
- if map_event(ev, "input_backspace"):
-
- if self.content and self.caret_pos > 0:
- content = self.content[0:self.caret_pos-1] + self.content[self.caret_pos:l]
- self.caret_pos -= 1
- self.update_text(content, self.editable)
-
- renpy.display.render.redraw(self, 0)
- raise renpy.display.core.IgnoreEvent()
-
- elif map_event(ev, "input_enter"):
- if not self.changed:
- return self.content
-
- elif map_event(ev, "input_left"):
- if self.caret_pos > 0:
- self.caret_pos -= 1
- self.update_text(self.content, self.editable)
-
- renpy.display.render.redraw(self, 0)
- raise renpy.display.core.IgnoreEvent()
-
- elif map_event(ev, "input_right"):
- if self.caret_pos < l:
- self.caret_pos += 1
- self.update_text(self.content, self.editable)
-
- renpy.display.render.redraw(self, 0)
- raise renpy.display.core.IgnoreEvent()
-
- elif map_event(ev, "input_delete"):
- if self.caret_pos < l:
- content = self.content[0:self.caret_pos] + self.content[self.caret_pos+1:l]
- self.update_text(content, self.editable)
-
- renpy.display.render.redraw(self, 0)
- raise renpy.display.core.IgnoreEvent()
-
- elif ev.type == pygame.KEYDOWN and ev.str:
- if ord(ev.str[0]) < 32:
- return None
-
- if self.length and len(self.content) >= self.length:
- raise renpy.display.core.IgnoreEvent()
-
- if self.allow and ev.str not in self.allow:
- raise renpy.display.core.IgnoreEvent()
-
- if self.exclude and ev.str in self.exclude:
- raise renpy.display.core.IgnoreEvent()
-
- content = self.content[0:self.caret_pos] + ev.str + self.content[self.caret_pos:l]
- self.caret_pos += 1
-
- self.update_text(content, self.editable)
-
- raise renpy.display.core.IgnoreEvent()
-
-# A map from adjustment to lists of displayables that want to be redrawn
-# if the adjustment changes.
-adj_registered = { }
-
-# This class contains information about an adjustment that can change the
-# position of content.
-class Adjustment(renpy.object.Object):
- """
- :doc: ui
- :name: ui.adjustment class
-
- Adjustment objects represent a value that can be adjusted by a bar
- or viewport. They contain information about the value, the range
- of the value, and how to adjust the value in small steps and large
- pages.
-
-
- """
-
- def __init__(self, range=1, value=0, step=None, page=0, changed=None, adjustable=None, ranged=None): #@ReservedAssignment
- """
- The following parameters correspond to fields or properties on
- the adjustment object:
-
- `range`
- The range of the adjustment, a number.
-
- `value`
- The value of the adjustment, a number.
-
- `step`
- The step size of the adjustment, a number. If None, then
- defaults to 1/10th of a page, if set. Otherwise, defaults
- to the 1/20th of the range.
-
- This is used when scrolling a viewport with the mouse wheel.
-
- `page`
- The page size of the adjustment. If None, this is set
- automatically by a viewport. If never set, defaults to 1/10th
- of the range.
-
- It's can be used when clicking on a scrollbar.
-
- The following parameters control the behavior of the adjustment.
-
- `adjustable`
- If True, this adjustment can be changed by a bar. If False,
- it can't.
-
- It defaults to being adjustable if a `changed` function
- is given or if the adjustment is associated with a viewport,
- and not adjustable otherwise.
-
- `changed`
- This function is called with the new value when the value of
- the adjustment changes.
-
- `ranged`
- This function is called with the adjustment object when
- the range of the adjustment is set by a viewport.
-
- .. method:: change(value)
-
- Changes the value of the adjustment to `value`, updating
- any bars and viewports that use the adjustment.
- """
-
-
- super(Adjustment, self).__init__()
-
- if adjustable is None:
- if changed:
- adjustable = True
-
- self._value = value
- self._range = range
- self._page = page
- self._step = step
- self.changed = changed
- self.adjustable = adjustable
- self.ranged = ranged
-
- def get_value(self):
- if self._value > self._range:
- return self._range
-
- return self._value
-
- def set_value(self, v):
- self._value = v
-
- value = property(get_value, set_value)
-
- def get_range(self):
- return self._range
-
- def set_range(self, v):
- self._range = v
- if self.ranged:
- self.ranged(self)
-
- range = property(get_range, set_range) #@ReservedAssignment
-
- def get_page(self):
- if self._page is not None:
- return self._page
-
- return self._range / 10
-
- def set_page(self, v):
- self._page = v
-
- page = property(get_page, set_page)
-
- def get_step(self):
- if self._step is not None:
- return self._step
-
- if self._page is not None and self.page > 0:
- return self._page / 10
-
- if isinstance(self._range, float):
- return self._range / 10
- else:
- return 1
-
- def set_step(self, v):
- self._step = v
-
- step = property(get_step, set_step)
-
- # Register a displayable to be redrawn when this adjustment changes.
- def register(self, d):
- adj_registered.setdefault(self, [ ]).append(d)
-
- def change(self, value):
-
- if value < 0:
- value = 0
- if value > self._range:
- value = self._range
-
- if value != self._value:
- self._value = value
- for d in adj_registered.setdefault(self, [ ]):
- renpy.display.render.redraw(d, 0)
- if self.changed:
- return self.changed(value)
-
- return None
-
-class Bar(renpy.display.core.Displayable):
- """
- Implements a bar that can display an integer value, and respond
- to clicks on that value.
- """
-
- __version__ = 2
-
- def after_upgrade(self, version):
-
- if version < 1:
- self.adjustment = Adjustment(self.range, self.value, changed=self.changed) # E1101
- self.adjustment.register(self)
- del self.range # E1101
- del self.value # E1101
- del self.changed # E1101
-
- if version < 2:
- self.value = None
-
- def __init__(self,
- range=None, #@ReservedAssignment
- value=None,
- width=None,
- height=None,
- changed=None,
- adjustment=None,
- step=None,
- page=None,
- bar=None,
- style=None,
- vertical=False,
- replaces=None,
- hovered=None,
- unhovered=None,
- **properties):
-
- self.value = None
-
- if adjustment is None:
- if isinstance(value, renpy.ui.BarValue):
-
- if isinstance(replaces, Bar):
- value.replaces(replaces.value)
-
- self.value = value
- adjustment = value.get_adjustment()
- renpy.game.interface.timeout(0)
- else:
- adjustment = Adjustment(range, value, step=step, page=page, changed=changed)
-
- if style is None:
- if self.value is not None:
- if vertical:
- style = self.value.get_style()[1]
- else:
- style = self.value.get_style()[0]
- else:
- if vertical:
- style = 'vbar'
- else:
- style = 'bar'
-
- if width is not None:
- properties['xmaximum'] = width
-
- if height is not None:
- properties['ymaximum'] = height
-
- super(Bar, self).__init__(style=style, **properties)
-
- self.adjustment = adjustment
- self.focusable = True
-
- # These are set when we are first rendered.
- self.thumb_dim = 0
- self.height = 0
- self.width = 0
- self.hidden = False
-
- self.hovered = hovered
- self.unhovered = unhovered
-
- def per_interact(self):
- self.focusable = self.adjustment.adjustable
- self.adjustment.register(self)
-
- def predict_one(self):
- pd = renpy.display.predict.displayable
- style = self.style
-
- pd(style.insensitive_fore_bar)
- pd(style.idle_fore_bar)
- pd(style.hover_fore_bar)
- pd(style.selected_idle_fore_bar)
- pd(style.selected_hover_fore_bar)
-
- pd(style.insensitive_aft_bar)
- pd(style.idle_aft_bar)
- pd(style.hover_aft_bar)
- pd(style.selected_idle_aft_bar)
- pd(style.selected_hover_aft_bar)
-
- pd(style.insensitive_thumb)
- pd(style.idle_thumb)
- pd(style.hover_thumb)
- pd(style.selected_idle_thumb)
- pd(style.selected_hover_thumb)
-
- pd(style.insensitive_thumb_shadow)
- pd(style.idle_thumb_shadow)
- pd(style.hover_thumb_shadow)
- pd(style.selected_idle_thumb_shadow)
- pd(style.selected_hover_thumb_shadow)
-
- def render(self, width, height, st, at):
-
- # Handle redrawing.
- if self.value is not None:
- redraw = self.value.periodic(st)
-
- if redraw is not None:
- renpy.display.render.redraw(self, redraw)
-
- # Store the width and height for the event function to use.
- self.width = width
- self.height = height
- range = self.adjustment.range #@ReservedAssignment
- value = self.adjustment.value
- page = self.adjustment.page
-
- if range <= 0:
- if self.style.unscrollable == "hide":
- self.hidden = True
- return renpy.display.render.Render(width, height)
- elif self.style.unscrollable == "insensitive":
- self.set_style_prefix("insensitive_", True)
-
- self.hidden = False
-
- if self.style.bar_invert ^ self.style.bar_vertical:
- value = range - value
-
- bar_vertical = self.style.bar_vertical
-
- if bar_vertical:
- dimension = height
- else:
- dimension = width
-
- fore_gutter = self.style.fore_gutter
- aft_gutter = self.style.aft_gutter
-
- active = dimension - fore_gutter - aft_gutter
- if range:
- thumb_dim = active * page / (range + page)
- else:
- thumb_dim = active
-
- thumb_offset = abs(self.style.thumb_offset)
-
- if bar_vertical:
- thumb = render(self.style.thumb, width, thumb_dim, st, at)
- thumb_shadow = render(self.style.thumb_shadow, width, thumb_dim, st, at)
- thumb_dim = thumb.height
- else:
- thumb = render(self.style.thumb, thumb_dim, height, st, at)
- thumb_shadow = render(self.style.thumb_shadow, thumb_dim, height, st, at)
- thumb_dim = thumb.width
-
- # Remove the offset from the thumb.
- thumb_dim -= thumb_offset * 2
- self.thumb_dim = thumb_dim
-
- active -= thumb_dim
-
- if range:
- fore_size = active * value / range
- else:
- fore_size = active
-
- fore_size = int(fore_size)
-
- aft_size = active - fore_size
-
- fore_size += fore_gutter
- aft_size += aft_gutter
-
- rv = renpy.display.render.Render(width, height)
-
- if bar_vertical:
-
- if self.style.bar_resizing:
- foresurf = render(self.style.fore_bar, width, fore_size, st, at)
- aftsurf = render(self.style.aft_bar, width, aft_size, st, at)
- rv.blit(thumb_shadow, (0, fore_size - thumb_offset))
- rv.blit(foresurf, (0, 0), main=False)
- rv.blit(aftsurf, (0, height-aft_size), main=False)
- rv.blit(thumb, (0, fore_size - thumb_offset))
-
- else:
- foresurf = render(self.style.fore_bar, width, height, st, at)
- aftsurf = render(self.style.aft_bar, width, height, st, at)
-
- rv.blit(thumb_shadow, (0, fore_size - thumb_offset))
- rv.blit(foresurf.subsurface((0, 0, width, fore_size)), (0, 0), main=False)
- rv.blit(aftsurf.subsurface((0, height - aft_size, width, aft_size)), (0, height - aft_size), main=False)
- rv.blit(thumb, (0, fore_size - thumb_offset))
-
- else:
- if self.style.bar_resizing:
- foresurf = render(self.style.fore_bar, fore_size, height, st, at)
- aftsurf = render(self.style.aft_bar, aft_size, height, st, at)
- rv.blit(thumb_shadow, (fore_size - thumb_offset, 0))
- rv.blit(foresurf, (0, 0), main=False)
- rv.blit(aftsurf, (width-aft_size, 0), main=False)
- rv.blit(thumb, (fore_size - thumb_offset, 0))
-
- else:
- foresurf = render(self.style.fore_bar, width, height, st, at)
- aftsurf = render(self.style.aft_bar, width, height, st, at)
-
- rv.blit(thumb_shadow, (fore_size - thumb_offset, 0))
- rv.blit(foresurf.subsurface((0, 0, fore_size, height)), (0, 0), main=False)
- rv.blit(aftsurf.subsurface((width - aft_size, 0, aft_size, height)), (width-aft_size, 0), main=False)
- rv.blit(thumb, (fore_size - thumb_offset, 0))
-
- if self.focusable:
- rv.add_focus(self, None, 0, 0, width, height)
-
- return rv
-
-
- def focus(self, default=False):
- super(Bar, self).focus(default)
- self.set_transform_event("hover")
-
- if not default:
- run(self.hovered)
-
-
- def unfocus(self, default=False):
- super(Bar, self).unfocus()
- self.set_transform_event("idle")
-
- if not default:
- run_unhovered(self.hovered)
- run(self.unhovered)
-
- def event(self, ev, x, y, st):
-
- if not self.focusable:
- return None
-
- if not self.is_focused():
- return None
-
- if self.hidden:
- return None
-
- range = self.adjustment.range #@ReservedAssignment
- old_value = self.adjustment.value
- value = old_value
-
- vertical = self.style.bar_vertical
- invert = self.style.bar_invert ^ vertical
- if invert:
- value = range - value
-
- grabbed = (renpy.display.focus.get_grab() is self)
- just_grabbed = False
-
- if not grabbed and map_event(ev, "bar_activate"):
- renpy.display.focus.set_grab(self)
- just_grabbed = True
- grabbed = True
-
- if grabbed:
-
- if vertical:
- increase = "bar_down"
- decrease = "bar_up"
- else:
- increase = "bar_right"
- decrease = "bar_left"
-
- if map_event(ev, decrease):
- value -= self.adjustment.step
-
- if map_event(ev, increase):
- value += self.adjustment.step
-
- if ev.type in (pygame.MOUSEMOTION, pygame.MOUSEBUTTONUP, pygame.MOUSEBUTTONDOWN):
-
- if vertical:
-
- tgutter = self.style.fore_gutter
- bgutter = self.style.aft_gutter
- zone_height = self.height - tgutter - bgutter - self.thumb_dim
- if zone_height:
- value = (y - tgutter - self.thumb_dim / 2) * range / zone_height
- else:
- value = 0
-
- else:
- lgutter = self.style.fore_gutter
- rgutter = self.style.aft_gutter
- zone_width = self.width - lgutter - rgutter - self.thumb_dim
- if zone_width:
- value = (x - lgutter - self.thumb_dim / 2) * range / zone_width
- else:
- value = 0
-
- if isinstance(range, int):
- value = int(value)
-
- if value < 0:
- value = 0
-
- if value > range:
- value = range
-
- if invert:
- value = range - value
-
- if grabbed and not just_grabbed and map_event(ev, "bar_deactivate"):
- renpy.display.focus.set_grab(None)
-
- if value != old_value:
- return self.adjustment.change(value)
-
- return None
-
-
-class Conditional(renpy.display.layout.Container):
- """
- This class renders its child if and only if the condition is
- true. Otherwise, it renders nothing. (Well, a Null).
-
- Warning: the condition MUST NOT update the game state in any
- way, as that would break rollback.
- """
-
- def __init__(self, condition, *args, **properties):
- super(Conditional, self).__init__(*args, **properties)
-
- self.condition = condition
- self.null = renpy.display.layout.Null()
-
- self.state = eval(self.condition, vars(renpy.store))
-
- def render(self, width, height, st, at):
- if self.state:
- return render(self.child, width, height, st, at)
- else:
- return render(self.null, width, height, st, at)
-
- def event(self, ev, x, y, st):
-
- state = eval(self.condition, vars(renpy.store))
-
- if state != self.state:
- renpy.display.render.redraw(self, 0)
-
- self.state = state
-
- if state:
- return self.child.event(ev, x, y, st)
-
-
-class TimerState(renpy.python.RevertableObject):
- """
- Stores the state of the timer, which may need to be rolled back.
- """
-
- # Prevents us from having to worry about our initialization being
- # rolled back.
- started = False
- next_event = None
-
-class Timer(renpy.display.layout.Null):
-
- __version__ = 1
-
- started = False
-
- def after_upgrade(self, version):
- if version < 1:
- self.state = TimerState()
- self.state.started = self.started
- self.state.next_event = self.next_event
-
- def __init__(self, delay, action=None, repeat=False, args=(), kwargs={}, replaces=None, **properties):
- super(Timer, self).__init__(**properties)
-
- if action is None:
- raise Exception("A timer must have an action supplied.")
-
- if delay <= 0:
- raise Exception("A timer's delay must be > 0.")
-
- # The delay.
- self.delay = delay
-
- # Should we repeat the event?
- self.repeat = repeat
-
- # The time the next event should occur.
- self.next_event = None
-
- # The function and its arguments.
- self.function = action
- self.args = args
- self.kwargs = kwargs
-
- # Did we start the timer?
- self.started = False
-
- if replaces is not None:
- self.state = replaces.state
- else:
- self.state = TimerState()
-
-
- def event(self, ev, x, y, st):
-
- state = self.state
-
- if not state.started:
- state.started = True
- state.next_event = st + self.delay
-
- if state.next_event is None:
- return
-
- if st < state.next_event:
- renpy.game.interface.timeout(state.next_event - st)
- return
-
- if not self.repeat:
- state.next_event = None
- else:
- state.next_event = state.next_event + self.delay
- if state.next_event < st:
- state.next_event = st + self.delay
-
- renpy.game.interface.timeout(state.next_event - st)
-
- return run(self.function, *self.args, **self.kwargs)
-
-
-class MouseArea(renpy.display.core.Displayable):
-
- def __init__(self, hovered=None, unhovered=None, replaces=None, **properties):
- super(MouseArea, self).__init__(**properties)
-
- self.hovered = hovered
- self.unhovered = unhovered
-
- # Are we hovered right now?
- self.is_hovered = False
-
- if replaces is not None:
- self.is_hovered = replaces.is_hovered
-
- # Taken from the render.
- self.width = 0
- self.height = 0
-
-
- def render(self, width, height, st, at):
- self.width = width
- self.height = height
-
- return Render(width, height)
-
- def event(self, ev, x, y, st):
-
- if 0 <= x < self.width and 0 <= y < self.height:
- is_hovered = True
- else:
- is_hovered = False
-
- if is_hovered and not self.is_hovered:
- self.is_hovered = True
-
- return run(self.hovered)
-
- elif not is_hovered and self.is_hovered:
- self.is_hovered = False
-
- run_unhovered(self.hovered)
- run(self.unhovered)
-
-
diff --git a/unrpyc/renpy/display/core.py b/unrpyc/renpy/display/core.py
deleted file mode 100644
index 4731df0..0000000
--- a/unrpyc/renpy/display/core.py
+++ /dev/null
@@ -1,2463 +0,0 @@
-# Copyright 2004-2013 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.
-
-# This file contains code for initializing and managing the display
-# window.
-
-import renpy.display
-import renpy.audio
-import renpy.text
-
-import pygame #@UnusedImport
-
-import sys
-import os
-import time
-import io
-import threading
-
-try:
- import android #@UnresolvedImport @UnusedImport
- import android.sound #@UnresolvedImport
-except:
- android = None
-
-# Is the cpu idle enough to do other things?
-cpu_idle = threading.Event()
-cpu_idle.clear()
-
-# Need to be +4, so we don't interfere with FFMPEG's events.
-TIMEEVENT = pygame.USEREVENT + 4
-PERIODIC = pygame.USEREVENT + 5
-JOYEVENT = pygame.USEREVENT + 6
-REDRAW = pygame.USEREVENT + 7
-
-# All events except for TIMEEVENT and REDRAW
-ALL_EVENTS = [ i for i in range(0, REDRAW + 1) if i != TIMEEVENT and i != REDRAW ]
-
-# The number of msec between periodic events.
-PERIODIC_INTERVAL = 50
-
-# Time management.
-time_base = None
-
-def init_time():
- global time_base
- time_base = time.time() - pygame.time.get_ticks() / 1000.0
-
-def get_time():
- return time_base + pygame.time.get_ticks() / 1000.0
-
-
-def displayable_by_tag(layer, tag):
- """
- Get the displayable on the given layer with the given tag.
- """
-
- return renpy.game.context().scene_lists.get_displayable_by_tag(layer, tag)
-
-class IgnoreEvent(Exception):
- """
- Exception that is raised when we want to ignore an event, but
- also don't want to return anything.
- """
-
- pass
-
-class EndInteraction(Exception):
- """
- Exception that can be raised (for example, during the render method of
- a displayable) to end the current interaction immediately.
- """
-
- def __init__(self, value):
- self.value = value
-
-class absolute(float):
- """
- This represents an absolute float coordinate.
- """
- __slots__ = [ ]
-
-
-class Displayable(renpy.object.Object):
- """
- The base class for every object in Ren'Py that can be
- displayed to the screen.
-
- Drawables will be serialized to a savegame file. Therefore, they
- shouldn't store non-serializable things (like pygame surfaces) in
- their fields.
- """
-
- # Some invariants about method call order:
- #
- # per_interact is called before render.
- # render is called before event.
- #
- # get_placement can be called at any time, so can't
- # assume anything.
-
- activated = False
- focusable = False
- full_focus_name = None
- role = ''
-
- # The event we'll pass on to our parent transform.
- transform_event = None
-
- # Can we change our look in response to transform_events?
- transform_event_responder = False
-
- def __init__(self, focus=None, default=False, style='default', **properties): # W0231
- self.style = renpy.style.Style(style, properties, heavy=True)
- self.focus_name = focus
- self.default = default
-
- def find_focusable(self, callback, focus_name):
-
- focus_name = self.focus_name or focus_name
-
- if self.focusable:
- callback(self, focus_name)
-
- for i in self.visit():
- if i is None:
- continue
-
- i.find_focusable(callback, focus_name)
-
-
- def focus(self, default=False):
- """
- Called to indicate that this widget has the focus.
- """
-
- if not self.activated:
- self.set_style_prefix(self.role + "hover_", True)
-
- if not default and not self.activated:
- if self.style.sound:
- renpy.audio.music.play(self.style.sound, channel="sound")
-
- def unfocus(self, default=False):
- """
- Called to indicate that this widget has become unfocused.
- """
-
- if not self.activated:
- self.set_style_prefix(self.role + "idle_", True)
-
- def is_focused(self):
-
- if renpy.display.focus.grab and renpy.display.focus.grab is not self:
- return
-
- return renpy.game.context().scene_lists.focused is self
-
- def set_style_prefix(self, prefix, root):
- """
- Called to set the style prefix of this widget and its child
- widgets, if any.
-
- `root` - True if this is the root of a style tree, False if this
- has been passed on to a child.
- """
-
- if prefix == self.style.prefix:
- return
-
- self.style.set_prefix(prefix)
- renpy.display.render.redraw(self, 0)
-
- def parameterize(self, name, parameters):
- """
- Called to parameterize this. By default, we don't take any
- parameters.
- """
-
- if parameters:
- raise Exception("Image '%s' can't take parameters '%s'. (Perhaps you got the name wrong?)" %
- (' '.join(name), ' '.join(parameters)))
-
- return self
-
- def render(self, width, height, st, at):
- """
- Called to display this displayable. This is called with width
- and height parameters, which give the largest width and height
- that this drawable can be drawn to without overflowing some
- bounding box. It's also given two times. It returns a Surface
- that is the current image of this drawable.
-
- @param st: The time since this widget was first shown, in seconds.
- @param at: The time since a similarly named widget was first shown,
- in seconds.
- """
-
- assert False, "Draw not implemented."
-
- def event(self, ev, x, y, st):
- """
- Called to report than an event has occured. Ev is the raw
- pygame event object representing that event. If the event
- involves the mouse, x and y are the translation of the event
- into the coordinates of this displayable. st is the time this
- widget has been shown for.
-
- @returns A value that should be returned from Interact, or None if
- no value is appropriate.
- """
-
- return None
-
- def get_placement(self):
- """
- Returns a style object containing placement information for
- this Displayable. Children are expected to overload this
- to return something more sensible.
- """
-
- return self.style.get_placement()
-
- def visit_all(self, callback):
- """
- Calls the callback on this displayable and all children of this
- displayable.
- """
-
- for d in self.visit():
- if not d:
- continue
- d.visit_all(callback)
-
- callback(self)
-
- def visit(self):
- """
- Called to ask the displayable to return a list of its children
- (including children taken from styles). For convenience, this
- list may also include None values.
- """
-
- return [ ]
-
- def per_interact(self):
- """
- Called once per widget per interaction.
- """
-
- return None
-
- def predict_one(self):
- """
- Called to ask this displayable to call the callback with all
- the images it may want to load.
- """
-
- return
-
- def predict_one_action(self):
- """
- Called to ask this displayable to cause image prediction
- to occur for images that may be loaded by its actions.
- """
-
- return
-
- def place(self, dest, x, y, width, height, surf, main=True):
- """
- This draws this Displayable onto a destination surface, using
- the placement style information returned by this object's
- get_placement() method.
-
- @param dest: The surface that this displayable will be drawn
- on.
-
- @param x: The minimum x coordinate on this surface that this
- Displayable will be drawn to.
-
- @param y: The minimum y coordinate on this surface that this
- displayable will be drawn to.
-
- @param width: The width of the area allocated to this
- Displayable.
-
- @param height: The height of the area allocated to this
- Displayable.
-
- @param surf: The surface returned by a previous call to
- self.render().
- """
-
- xpos, ypos, xanchor, yanchor, xoffset, yoffset, subpixel = self.get_placement()
-
- if xpos is None:
- xpos = 0
- if ypos is None:
- ypos = 0
- if xanchor is None:
- xanchor = 0
- if yanchor is None:
- yanchor = 0
- if xoffset is None:
- xoffset = 0
- if yoffset is None:
- yoffset = 0
-
- # We need to use type, since isinstance(absolute(0), float).
- if xpos.__class__ is float:
- xpos *= width
-
- if xanchor.__class__ is float:
- xanchor *= surf.width
-
- xpos += x + xoffset - xanchor
-
- # y
-
- if ypos.__class__ is float:
- ypos *= height
-
- if yanchor.__class__ is float:
- yanchor *= surf.height
-
- ypos += y + yoffset - yanchor
-
- if dest is not None:
- if subpixel:
- dest.subpixel_blit(surf, (xpos, ypos), main, main, None)
- else:
- dest.blit(surf, (xpos, ypos), main, main, None)
-
- return xpos, ypos
-
- def set_transform_event(self, event):
- """
- Sets the transform event of this displayable to event.
- """
-
- if event == self.transform_event:
- return
-
- self.transform_event = event
- if self.transform_event_responder:
- renpy.display.render.redraw(self, 0)
-
- def _hide(self, st, at, kind):
- """
- Returns None if this displayable is ready to be hidden, or
- a replacement displayable if it doesn't want to be hidden
- quite yet. Kind is either "hide" or "replaced".
- """
-
- return None
-
- def _show(self):
- """
- Called when the displayable is added to a scene list.
- """
-
- def _get_parameterized(self):
- """
- If this is a ImageReference to a parameterized image, return
- the get_parameterized() of the parameterized image. Otherwise,
- return this displayable.
- """
-
- return self
-
- def _change_transform_child(self, child):
- """
- If this is a transform, makes a copy of the transform and sets
- the child of the innermost transform to this. Otherwise,
- simply returns child.
- """
-
- return child
-
-
-
-class SceneListEntry(renpy.object.Object):
- """
- Represents a scene list entry. Since this was replacing a tuple,
- it should be treated as immutable after its initial creation.
- """
-
- def __init__(self, tag, zorder, show_time, animation_time, displayable, name):
- self.tag = tag
- self.zorder = zorder
- self.show_time = show_time
- self.animation_time = animation_time
- self.displayable = displayable
- self.name = name
-
- def __iter__(self):
- return iter((self.tag, self.zorder, self.show_time, self.animation_time, self.displayable))
-
- def __getitem__(self, index):
- return (self.tag, self.zorder, self.show_time, self.animation_time, self.displayable)[index]
-
- def __repr__(self):
- return "<SLE: %r %r %r>" % (self.tag, self.name, self.displayable)
-
- def copy(self):
- return SceneListEntry(
- self.tag,
- self.zorder,
- self.show_time,
- self.animation_time,
- self.displayable,
- self.name)
-
- def update_time(self, time):
-
- rv = self
-
- if self.show_time is None or self.animation_time is None:
- rv = self.copy()
- rv.show_time = rv.show_time or time
- rv.animation_time = rv.animation_time or time
-
- return rv
-
-
-class SceneLists(renpy.object.Object):
- """
- This stores the current scene lists that are being used to display
- things to the user.
- """
-
- __version__ = 6
-
- def after_setstate(self):
- for i in renpy.config.layers + renpy.config.top_layers:
- if i not in self.layers:
- self.layers[i] = [ ]
- self.at_list[i] = { }
- self.layer_at_list[i] = (None, [ ])
-
- def after_upgrade(self, version):
-
- if version < 1:
-
- self.at_list = { }
- self.layer_at_list = { }
-
- for i in renpy.config.layers + renpy.config.top_layers:
- self.at_list[i] = { }
- self.layer_at_list[i] = (None, [ ])
-
- if version < 3:
- self.shown_window = False
-
- if version < 4:
- for k in self.layers:
- self.layers[k] = [ SceneListEntry(*(i + (None,)) ) for i in self.layers[k] ]
-
- self.additional_transient = [ ]
-
- if version < 5:
- self.drag_group = None
-
- if version < 6:
- self.shown = self.image_predict_info
-
- def __init__(self, oldsl, shown):
-
- super(SceneLists, self).__init__()
-
- # Has a window been shown as part of these scene lists?
- self.shown_window = False
-
- # A map from layer name -> list(SceneListEntry)
- self.layers = { }
-
- # A map from layer name -> tag -> at_list associated with that tag.
- self.at_list = { }
-
- # A map from layer to (star time, at_list), where the at list has
- # been applied to the layer as a whole.
- self.layer_at_list = { }
-
- # The current shown images,
- self.shown = shown
-
- # A list of (layer, tag) pairs that are considered to be
- # transient.
- self.additional_transient = [ ]
-
- # Either None, or a DragGroup that's used as the default for
- # drags with names.
- self.drag_group = None
-
- if oldsl:
-
- for i in renpy.config.layers + renpy.config.top_layers:
-
- try:
- self.layers[i] = oldsl.layers[i][:]
- except KeyError:
- self.layers[i] = [ ]
-
- if i in oldsl.at_list:
- self.at_list[i] = oldsl.at_list[i].copy()
- self.layer_at_list[i] = oldsl.layer_at_list[i]
- else:
- self.at_list[i] = { }
- self.layer_at_list[i] = (None, [ ])
-
- for i in renpy.config.overlay_layers:
- self.clear(i)
-
- self.replace_transient()
-
- self.focused = None
-
- self.drag_group = oldsl.drag_group
-
- else:
- for i in renpy.config.layers + renpy.config.top_layers:
- self.layers[i] = [ ]
- self.at_list[i] = { }
- self.layer_at_list[i] = (None, [ ])
-
- self.music = None
- self.focused = None
-
- def replace_transient(self):
- """
- Replaces the contents of the transient display list with
- a copy of the master display list. This is used after a
- scene is displayed to get rid of transitions and interface
- elements.
- """
-
- for i in renpy.config.transient_layers:
- self.clear(i, True)
-
- for layer, tag in self.additional_transient:
- self.remove(layer, tag)
-
- self.additional_transient = [ ]
-
- def transient_is_empty(self):
- """
- This returns True if all transient layers are empty. This is
- used by the rollback code, as we can't start a new rollback
- if there is something in a transient layer (as things in the
- transient layer may contain objects that cannot be pickled,
- like lambdas.)
- """
-
- for i in renpy.config.transient_layers:
- if self.layers[i]:
- return False
-
- return True
-
- def transform_state(self, old_thing, new_thing):
- """
- If the old thing is a transform, then move the state of that transform
- to the new thing.
- """
-
- if old_thing is None:
- return new_thing
-
- # Don't bother wrapping screens, as they can't be transformed.
- if isinstance(new_thing, renpy.display.screen.ScreenDisplayable):
- return new_thing
-
- old_transform = old_thing._get_parameterized()
- if not isinstance(old_transform, renpy.display.motion.Transform):
- return new_thing
-
- new_transform = new_thing._get_parameterized()
- if not isinstance(new_transform, renpy.display.motion.Transform):
- new_thing = new_transform = renpy.display.motion.Transform(child=new_thing)
-
- new_transform.take_state(old_transform)
- return new_thing
-
-
- def find_index(self, layer, tag, zorder, behind):
- """
- This finds the spot in the named layer where we should insert the
- displayable. It returns two things: an index at which the new thing
- should be added, and an index at which the old thing should be hidden.
- (Note that the indexes are relative to the current state of the list,
- which may change on an add or remove.)
- """
-
- add_index = None
- remove_index = None
-
-
- for i, sle in enumerate(self.layers[layer]):
-
- if add_index is None:
-
- if sle.zorder == zorder:
- if sle.tag and (sle.tag == tag or sle.tag in behind):
- add_index = i
-
- elif sle.zorder > zorder:
- add_index = i
-
-
- if remove_index is None:
- if (sle.tag and sle.tag == tag) or sle.displayable == tag:
- remove_index = i
-
-
- if add_index is None:
- add_index = len(self.layers[layer])
-
- return add_index, remove_index
-
-
- def add(self,
- layer,
- thing,
- key=None,
- zorder=0,
- behind=[ ],
- at_list=[ ],
- name=None,
- atl=None,
- default_transform=None,
- transient=False):
- """
- Adds something to this scene list. Some of these names are quite a bit
- out of date.
-
- `thing` - The displayable to add.
-
- `key` - A string giving the tag associated with this thing.
-
- `zorder` - Where to place this thing in the zorder, an integer
- A greater value means closer to the user.
-
- `behind` - A list of tags to place the thing behind.
-
- `at_list` - The at_list associated with this
- displayable. Counterintunitively, this is not actually
- applied, but merely stored for future use.
-
- `name` - The full name of the image being displayed. This is used for
- image lookup.
-
- `atl` - If not None, an atl block applied to the thing. (This actually is
- applied here.)
-
- `default_transform` - The default transform that is used to initialized
- the values in the other transforms.
- """
-
- if not isinstance(thing, Displayable):
- raise Exception("Attempting to show something that isn't a displayable:" + repr(thing))
-
- if layer not in self.layers:
- raise Exception("Trying to add something to non-existent layer '%s'." % layer)
-
- if key:
- self.remove_hide_replaced(layer, key)
- self.at_list[layer][key] = at_list
-
- if key and name:
- self.shown.predict_show(layer, name)
-
- if transient:
- self.additional_transient.append((layer, key))
-
- l = self.layers[layer]
-
- if atl:
- thing = renpy.display.motion.ATLTransform(atl, child=thing)
-
- add_index, remove_index = self.find_index(layer, key, zorder, behind)
-
- at = None
- st = None
-
- if remove_index is not None:
- sle = l[remove_index]
- at = sle.animation_time
- old = sle.displayable
-
- if (not atl and
- not at_list and
- renpy.config.keep_running_transform and
- isinstance(old, renpy.display.motion.Transform)):
-
- thing = sle.displayable._change_transform_child(thing)
- else:
- thing = self.transform_state(l[remove_index].displayable, thing)
-
- thing.set_transform_event("replace")
- thing._show()
-
- else:
-
- if not isinstance(thing, renpy.display.motion.Transform):
- thing = self.transform_state(default_transform, thing)
-
- thing.set_transform_event("show")
- thing._show()
-
- sle = SceneListEntry(key, zorder, st, at, thing, name)
- l.insert(add_index, sle)
-
- if remove_index is not None:
- if add_index <= remove_index:
- remove_index += 1
-
- self.hide_or_replace(layer, remove_index, "replaced")
-
- def hide_or_replace(self, layer, index, prefix):
- """
- Hides or replaces the scene list entry at the given
- index. `prefix` is a prefix that is used if the entry
- decides it doesn't want to be hidden quite yet.
- """
-
- if index is None:
- return
-
- l = self.layers[layer]
- oldsle = l[index]
-
- now = get_time()
-
- st = oldsle.show_time or now
- at = oldsle.animation_time or now
-
- if oldsle.tag:
-
- d = oldsle.displayable._hide(now - st, now - at, prefix)
-
- # _hide can mutate the layers, so we need to recompute
- # index.
- index = l.index(oldsle)
-
- if d is not None:
-
- sle = SceneListEntry(
- prefix + "$" + oldsle.tag,
- oldsle.zorder,
- st,
- at,
- d,
- None)
-
- l[index] = sle
-
- return
-
- l.pop(index)
-
- def get_all_displayables(self):
- """
- Gets all displayables reachable from this scene list.
- """
-
- rv = [ ]
- for l in self.layers.values():
- for sle in l:
- rv.append(sle.displayable)
-
- return rv
-
- def remove_above(self, layer, thing):
- """
- Removes everything on the layer that is closer to the user
- than thing, which may be either a tag or a displayable. Thing must
- be displayed, or everything will be removed.
- """
-
- for i in reversed(range(len(self.layers[layer]))):
-
- sle = self.layers[layer][i]
-
- if thing:
- if sle.tag == thing or sle.displayable == thing:
- break
-
- if sle.tag and "$" in sle.tag:
- continue
-
- self.hide_or_replace(layer, i, "hide")
-
- def remove(self, layer, thing):
- """
- Thing is either a key or a displayable. This iterates through the
- named layer, searching for entries matching the thing.
- When they are found, they are removed from the displaylist.
-
- It's not an error to remove something that isn't in the layer in
- the first place.
- """
-
- if layer not in self.layers:
- raise Exception("Trying to remove something from non-existent layer '%s'." % layer)
-
- _add_index, remove_index = self.find_index(layer, thing, 0, [ ])
-
- if remove_index is not None:
- tag = self.layers[layer][remove_index].tag
-
- if tag:
- self.shown.predict_hide(layer, (tag,))
- self.at_list[layer].pop(tag, None)
-
- self.hide_or_replace(layer, remove_index, "hide")
-
- def clear(self, layer, hide=False):
- """
- Clears the named layer, making it empty.
-
- If hide is True, then objects are hidden. Otherwise, they are
- totally wiped out.
- """
-
- if not hide:
- self.layers[layer] = [ ]
-
- else:
-
- # Have to iterate in reverse order, since otherwise
- # the indexes might change.
- for i in reversed(range(len(self.layers[layer]))):
- self.hide_or_replace(layer, i, hide)
-
- self.at_list[layer].clear()
- self.shown.predict_scene(layer)
- self.layer_at_list[layer] = (None, [ ])
-
- def set_layer_at_list(self, layer, at_list):
- self.layer_at_list[layer] = (None, list(at_list))
-
- def set_times(self, time):
- """
- This finds entries with a time of None, and replaces that
- time with the given time.
- """
-
- for l, (t, list) in list(self.layer_at_list.items()): #@ReservedAssignment
- self.layer_at_list[l] = (t or time, list)
-
- for l, ll in self.layers.items():
- self.layers[l] = [ i.update_time(time) for i in ll ]
-
- def showing(self, layer, name):
- """
- Returns true if something with the prefix of the given name
- is found in the scene list.
- """
-
- return self.shown.showing(layer, name)
-
- def make_layer(self, layer, properties):
- """
- Creates a Fixed with the given layer name and scene_list.
- """
-
- rv = renpy.display.layout.MultiBox(layout='fixed', focus=layer, **properties)
- rv.append_scene_list(self.layers[layer])
-
- time, at_list = self.layer_at_list[layer]
-
- if at_list:
- for a in at_list:
-
- if isinstance(a, renpy.display.motion.Transform):
- rv = a(child=rv)
- else:
- rv = a(rv)
-
- f = renpy.display.layout.MultiBox(layout='fixed')
- f.add(rv, time, time)
- rv = f
-
- rv.layer_name = layer
- return rv
-
- def remove_hide_replaced(self, layer, tag):
- """
- Removes things that are hiding or replaced, that have the given
- tag.
- """
-
- hide_tag = "hide$" + tag
- replaced_tag = "replaced$" + tag
-
- l = self.layers[layer]
- self.layers[layer] = [ i for i in l if i.tag != hide_tag and i.tag != replaced_tag ]
-
- def remove_hidden(self):
- """
- Goes through all of the layers, and removes things that are
- hidden and are no longer being kept alive by their hide
- methods.
- """
-
- now = get_time()
-
- for l in self.layers:
- newl = [ ]
-
- for sle in self.layers[l]:
-
- if sle.tag:
-
- if sle.tag.startswith("hide$"):
- d = sle.displayable._hide(now - sle.show_time, now - sle.animation_time, "hide")
- if not d:
- continue
-
- elif sle.tag.startswith("replaced$"):
- d = sle.displayable._hide(now - sle.show_time, now - sle.animation_time, "replaced")
- if not d:
- continue
-
- newl.append(sle)
-
- self.layers[l] = newl
-
- def get_displayable_by_tag(self, layer, tag):
- """
- Returns the displayable on the layer with the given tag, or None
- if no such displayable exists. Note that this will usually return
- a Transform.
- """
-
- if layer not in self.layers:
- raise Exception("Unknown layer %r." % layer)
-
- for sle in self.layers[layer]:
- if sle.tag == tag:
- return sle.displayable
-
- return None
-
- def get_displayable_by_name(self, layer, name):
- """
- Returns the displayable on the layer with the given tag, or None
- if no such displayable exists. Note that this will usually return
- a Transform.
- """
-
- if layer not in self.layers:
- raise Exception("Unknown layer %r." % layer)
-
- for sle in self.layers[layer]:
-
- if sle.name == name:
- return sle.displayable
-
- return None
-
-
-def scene_lists(index=-1):
- """
- Returns either the current scenelists object, or the one for the
- context at the given index.
- """
-
- return renpy.game.context(index).scene_lists
-
-
-class Interface(object):
- """
- This represents the user interface that interacts with the user.
- It manages the Display objects that display things to the user, and
- also handles accepting and responding to user input.
-
- @ivar display: The display that we used to display the screen.
-
- @ivar profile_time: The time of the last profiling.
-
- @ivar screenshot: A screenshot, or None if no screenshot has been
- taken.
-
- @ivar old_scene: The last thing that was displayed to the screen.
-
- @ivar transition: A map from layer name to the transition that will
- be applied the next time interact restarts.
-
- @ivar transition_time: A map from layer name to the time the transition
- involving that layer started.
-
- @ivar transition_from: A map from layer name to the scene that we're
- transitioning from on that layer.
-
- @ivar suppress_transition: If True, then the next transition will not
- happen.
-
- @ivar force_redraw: If True, a redraw is forced.
-
- @ivar restart_interaction: If True, the current interaction will
- be restarted.
-
- @ivar pushed_event: If not None, an event that was pushed back
- onto the stack.
-
- @ivar mouse: The name of the mouse cursor to use during the current
- interaction.
-
- @ivar ticks: The number of 20hz ticks.
-
- @ivar frame_time: The time at which we began drawing this frame.
-
- @ivar interact_time: The time of the start of the first frame of the current interact_core.
-
- @ivar time_event: A singleton ignored event.
-
- @ivar event_time: The time of the current event.
-
- @ivar timeout_time: The time at which the timeout will occur.
- """
-
- def __init__(self):
- self.screenshot = None
- self.old_scene = { }
- self.transition = { }
- self.ongoing_transition = { }
- self.transition_time = { }
- self.transition_from = { }
- self.suppress_transition = False
- self.quick_quit = False
- self.force_redraw = False
- self.restart_interaction = False
- self.pushed_event = None
- self.ticks = 0
- self.mouse = 'default'
- self.timeout_time = None
- self.last_event = None
- self.current_context = None
- self.roll_forward = None
-
- # Things to be preloaded.
- self.preloads = [ ]
-
- # The time at which this draw occurs.
- self.frame_time = 0
-
- # The time when this interaction occured.
- self.interact_time = None
-
- # The time we last tried to quit.
- self.quit_time = 0
-
- self.time_event = pygame.event.Event(TIMEEVENT)
- self.redraw_event = pygame.event.Event(REDRAW)
-
- # Are we focused?
- self.focused = True
-
- # Properties for each layer.
- self.layer_properties = { }
-
- # Have we shown the window this interaction?
- self.shown_window = False
-
- # Are we in fullscren mode?
- self.fullscreen = False
-
- for layer in renpy.config.layers + renpy.config.top_layers:
- if layer in renpy.config.layer_clipping:
- x, y, w, h = renpy.config.layer_clipping[layer]
- self.layer_properties[layer] = dict(
- xpos = x,
- xanchor = 0,
- ypos = y,
- yanchor = 0,
- xmaximum = w,
- ymaximum = h,
- xminimum = w,
- yminimum = h,
- clipping = True,
- )
-
- else:
- self.layer_properties[layer] = dict()
-
-
- # A stack giving the values of self.transition and self.transition_time
- # for contexts outside the current one. This is used to restore those
- # in the case where nothing has changed in the new context.
- self.transition_info_stack = [ ]
-
- # The time when the event was dispatched.
- self.event_time = 0
-
- # The time we saw the last mouse event.
- self.mouse_event_time = None
-
- # Should we show the mouse?
- self.show_mouse = True
-
- # Should we reset the display?
- self.display_reset = False
-
- # The last size we were resized to. This lets us debounce the
- # VIDEORESIZE event.
- self.last_resize = None
-
- # Ensure that we kill off the presplash.
- renpy.display.presplash.end()
-
- # Initialize pygame.
- if pygame.version.vernum < (1, 8, 1):
- raise Exception("Ren'Py requires pygame 1.8.1 to run.")
-
- try:
- import pygame.macosx as macosx
- macosx.init() #@UndefinedVariable
- except:
- pass
-
- try:
- macosx.Video_AutoInit() #@UndefinedVariable
- except:
- pass
-
- pygame.font.init()
- renpy.audio.audio.init()
- renpy.display.joystick.init()
- pygame.display.init()
-
- # Init timing.
- init_time()
- self.profile_time = get_time()
- self.mouse_event_time = get_time()
-
- # The current window caption.
- self.window_caption = None
-
- renpy.game.interface = self
- renpy.display.interface = self
-
- # Are we in safe mode, from holding down shift at start?
- self.safe_mode = False
- if renpy.first_utter_start and (pygame.key.get_mods() & pygame.KMOD_SHIFT):
- self.safe_mode = True
-
- # Setup the video mode.
- self.set_mode()
-
- # Load the image fonts.
- renpy.text.font.load_image_fonts()
-
- # Setup the android keymap.
- if android is not None:
- android.map_key(android.KEYCODE_BACK, pygame.K_PAGEUP)
- android.map_key(android.KEYCODE_MENU, pygame.K_ESCAPE)
-
- # Double check, since at least on Linux, we can't set safe_mode until
- # the window maps.
- if renpy.first_utter_start and (pygame.key.get_mods() & pygame.KMOD_SHIFT):
- self.safe_mode = True
-
- # Setup periodic event.
- pygame.time.set_timer(PERIODIC, PERIODIC_INTERVAL)
-
- # Don't grab the screen.
- pygame.event.set_grab(False)
-
- # Do we need a background screenshot?
- self.bgscreenshot_needed = False
-
- # Event used to signal background screenshot taken.
- self.bgscreenshot_event = threading.Event()
-
- # The background screenshot surface.
- self.bgscreenshot_surface = None
-
-
- def post_init(self):
- # Setup.
-
- # Needed for Unity.
- wmclass = renpy.config.save_directory or os.path.basename(sys.argv[0])
- os.environ['SDL_VIDEO_X11_WMCLASS'] = wmclass
-
- self.set_window_caption(force=True)
- self.set_icon()
-
- if renpy.config.key_repeat is not None:
- delay, repeat_delay = renpy.config.key_repeat
- pygame.key.set_repeat(int(1000 * delay), int(1000 * repeat_delay))
-
- def set_icon(self):
- """
- This is called to set up the window icon.
- """
-
- # Window icon.
- icon = renpy.config.window_icon
-
- if renpy.windows and renpy.config.windows_icon:
- icon = renpy.config.windows_icon
-
- if icon:
-
- im = renpy.display.scale.image_load_unscaled(
- renpy.loader.load(icon),
- icon,
- convert=False,
- )
-
- # Convert the aspect ratio to be square.
- iw, ih = im.get_size()
- imax = max(iw, ih)
- square_im = renpy.display.pgrender.surface_unscaled((imax, imax), True)
- square_im.blit(im, ( (imax-iw)/2, (imax-ih)/2 ))
- im = square_im
-
- if renpy.windows and im.get_size() != (32, 32):
- im = renpy.display.scale.real_smoothscale(im, (32, 32))
-
- pygame.display.set_icon(im)
-
-
- def set_window_caption(self, force=False):
- caption = renpy.config.window_title + renpy.store._window_subtitle
- if not force and caption == self.window_caption:
- return
-
- self.window_caption = caption
- pygame.display.set_caption(caption.encode("utf-8"))
-
- def iconify(self):
- pygame.display.iconify()
-
- def get_draw_constructors(self):
- """
- Figures out the list of draw constructors to try.
- """
-
- renderer = renpy.game.preferences.renderer
- renderer = os.environ.get("RENPY_RENDERER", renderer)
-
- if self.safe_mode:
- renderer = "sw"
-
- renpy.config.renderer = renderer
-
- if renderer == "auto":
- if renpy.windows:
- renderers = [ "gl", "angle", "sw" ]
- else:
- renderers = [ "gl", "sw" ]
- else:
- renderers = [ renderer, "sw" ]
-
- draw_objects = { }
-
- def make_draw(name, mod, cls, *args):
- if name not in renderers:
- return False
-
- try:
- __import__(mod)
- module = sys.modules[mod]
- draw_class = getattr(module, cls)
- draw_objects[name] = draw_class(*args)
- return True
-
- except:
- renpy.display.log.write("Couldn't import {0} renderer:".format(name))
- renpy.display.log.exception()
-
- return False
-
- if renpy.windows:
- has_angle = make_draw("angle", "renpy.angle.gldraw", "GLDraw")
- else:
- has_angle = False
-
- make_draw("gl", "renpy.gl.gldraw", "GLDraw", not has_angle)
- make_draw("sw", "renpy.display.swdraw", "SWDraw")
-
- rv = [ ]
-
- def append_draw(name):
- if name in draw_objects:
- rv.append(draw_objects[name])
- else:
- renpy.display.log.write("Unknown renderer: {0}".format(name))
-
- for i in renderers:
- append_draw(i)
-
- return rv
-
-
- def kill_textures(self):
- renpy.display.render.free_memory()
- renpy.text.text.layout_cache_clear()
-
- def kill_textures_and_surfaces(self):
- """
- Kill all textures and surfaces that are loaded.
- """
-
- self.kill_textures()
-
- renpy.display.im.cache.clear()
- renpy.display.module.bo_cache = None
-
- def set_mode(self, physical_size=None):
- """
- This sets the video mode. It also picks the draw object.
- """
-
- # Ensure that we kill off the movie when changing screen res.
- if renpy.display.draw and renpy.display.draw.info["renderer"] == "sw":
- renpy.display.video.movie_stop(clear=False)
-
- if self.display_reset:
- renpy.display.draw.deinit()
-
- if renpy.display.draw.info["renderer"] == "angle":
- renpy.display.draw.quit()
-
- renpy.display.render.free_memory()
- renpy.display.im.cache.clear()
- renpy.text.text.layout_cache_clear()
-
- renpy.display.module.bo_cache = None
-
- self.kill_textures_and_surfaces()
-
- self.display_reset = False
-
- virtual_size = (renpy.config.screen_width, renpy.config.screen_height)
-
- if physical_size is None:
- if renpy.android or renpy.game.preferences.physical_size is None: #@UndefinedVariable
- physical_size = (renpy.config.screen_width, renpy.config.screen_height)
- else:
- physical_size = renpy.game.preferences.physical_size
-
- # Setup screen.
- fullscreen = renpy.game.preferences.fullscreen
-
- # If we're in fullscreen mode, and changing to another mode, go to
- # windowed mode first.
- s = pygame.display.get_surface()
- if s and (s.get_flags() & pygame.FULLSCREEN):
- fullscreen = False
-
- old_fullscreen = self.fullscreen
- self.fullscreen = fullscreen
-
- if os.environ.get('RENPY_DISABLE_FULLSCREEN', False):
- fullscreen = False
- self.fullscreen = renpy.game.preferences.fullscreen
-
- if renpy.display.draw:
- draws = [ renpy.display.draw ]
- else:
- draws = self.get_draw_constructors()
-
- for draw in draws:
- if draw.set_mode(virtual_size, physical_size, fullscreen):
- renpy.display.draw = draw
- break
- else:
- pygame.display.quit()
- else:
- # Ensure we don't get stuck in fullscreen.
- renpy.game.preferences.fullscreen = False
- raise Exception("Could not set video mode.")
-
- # Save the video size.
- if renpy.config.save_physical_size and not fullscreen and not old_fullscreen:
- renpy.game.preferences.physical_size = renpy.display.draw.get_physical_size()
-
- if android:
- android.init()
-
- # We need to redraw the (now blank) screen.
- self.force_redraw = True
-
- # Assume we have focus until told otherwise.
- self.focused = True
-
- # Assume we're not minimized.
- self.minimized = False
-
- # Force an interaction restart.
- self.restart_interaction = True
-
-
- def draw_screen(self, root_widget, fullscreen_video, draw):
-
- surftree = renpy.display.render.render_screen(
- root_widget,
- renpy.config.screen_width,
- renpy.config.screen_height,
- )
-
- if draw:
- renpy.display.draw.draw_screen(surftree, fullscreen_video)
-
- renpy.display.render.mark_sweep()
- renpy.display.focus.take_focuses()
-
- self.surftree = surftree
- self.fullscreen_video = fullscreen_video
-
-
- def take_screenshot(self, scale, background=False):
- """
- This takes a screenshot of the current screen, and stores it so
- that it can gotten using get_screenshot()
-
- `background`
- If true, we're in a background thread. So queue the request
- until it can be handled by the main thread.
- """
-
- if background:
- self.bgscreenshot_event.clear()
- self.bgscreenshot_needed = True
- self.bgscreenshot_event.wait()
-
- window = self.bgscreenshot_surface
- self.bgscreenshot_surface = None
-
- else:
-
- window = renpy.display.draw.screenshot(self.surftree, self.fullscreen_video)
-
- surf = renpy.display.pgrender.copy_surface(window, True)
- surf = renpy.display.scale.smoothscale(surf, scale)
- surf = surf.convert()
-
- sio = io.StringIO()
- renpy.display.module.save_png(surf, sio, 0)
- self.screenshot = sio.getvalue()
- sio.close()
-
-
- def save_screenshot(self, filename):
- """
- Saves a full-size screenshot in the given filename.
- """
-
- window = renpy.display.draw.screenshot(self.surftree, self.fullscreen_video)
-
- if renpy.config.screenshot_crop:
- window = window.subsurface(renpy.config.screenshot_crop)
-
- try:
- renpy.display.scale.image_save_unscaled(window, filename)
- except:
- if renpy.config.debug:
- raise
- pass
-
-
- def get_screenshot(self):
- """
- Gets the current screenshot, as a string. Returns None if there isn't
- a current screenshot.
- """
-
- rv = self.screenshot
-
- if not rv:
- self.take_screenshot((renpy.config.thumbnail_width, renpy.config.thumbnail_height))
- rv = self.screenshot
- self.lose_screenshot()
-
- return rv
-
-
- def lose_screenshot(self):
- """
- This deallocates the saved screenshot.
- """
-
- self.screenshot = None
-
-
- def show_window(self):
-
- if not renpy.store._window:
- return
-
- if renpy.game.context().scene_lists.shown_window:
- return
-
- if renpy.config.empty_window:
- renpy.config.empty_window()
-
- def do_with(self, trans, paired, clear=False):
-
- if renpy.config.with_callback:
- trans = renpy.config.with_callback(trans, paired)
-
- if (not trans) or self.suppress_transition:
- self.with_none()
- return False
- else:
- self.set_transition(trans)
- return self.interact(trans_pause=True,
- suppress_overlay=not renpy.config.overlay_during_with,
- mouse='with',
- clear=clear)
-
- def with_none(self):
- """
- Implements the with None command, which sets the scene we will
- be transitioning from.
- """
-
- renpy.exports.say_attributes = None
-
- # Show the window, if that's necessary.
- self.show_window()
-
- # Compute the overlay.
- self.compute_overlay()
-
- scene_lists = renpy.game.context().scene_lists
-
- # Compute the scene.
- self.old_scene = self.compute_scene(scene_lists)
-
- # Get rid of transient things.
-
- for i in renpy.config.overlay_layers:
- scene_lists.clear(i)
-
- scene_lists.replace_transient()
- scene_lists.shown_window = False
-
-
- def set_transition(self, transition, layer=None, force=False):
- """
- Sets the transition that will be performed as part of the next
- interaction.
- """
-
- if self.suppress_transition and not force:
- return
-
- if transition is None:
- self.transition.pop(layer, None)
- else:
- self.transition[layer] = transition
-
-
- def event_peek(self):
- """
- This peeks the next event. It returns None if no event exists.
- """
-
- if self.pushed_event:
- return self.pushed_event
-
- ev = pygame.event.poll()
-
- if ev.type == pygame.NOEVENT:
- # Seems to prevent the CPU from speeding up.
- renpy.display.draw.event_peek_sleep()
- return None
-
- self.pushed_event = ev
-
- return ev
-
- def event_poll(self):
- """
- Called to busy-wait for an event while we're waiting to
- redraw a frame.
- """
-
- if self.pushed_event:
- rv = self.pushed_event
- self.pushed_event = None
- else:
- rv = pygame.event.poll()
-
- self.last_event = rv
-
- return rv
-
-
- def event_wait(self):
- """
- This is in its own function so that we can track in the
- profiler how much time is spent in interact.
- """
-
- if self.pushed_event:
- rv = self.pushed_event
- self.pushed_event = None
- self.last_event = rv
- return rv
-
- # Handle a request for a background screenshot.
- if self.bgscreenshot_needed:
- self.bgscreenshot_needed = False
- self.bgscreenshot_surface = renpy.display.draw.screenshot(self.surftree, self.fullscreen_video)
- self.bgscreenshot_event.set()
-
- try:
- cpu_idle.set()
- ev = pygame.event.wait()
- finally:
- cpu_idle.clear()
-
- self.last_event = ev
-
- return ev
-
-
- def compute_overlay(self):
-
- if renpy.store.suppress_overlay:
- return
-
- # Figure out what the overlay layer should look like.
- renpy.ui.layer("overlay")
-
- for i in renpy.config.overlay_functions:
- i()
-
- if renpy.game.context().scene_lists.shown_window:
- for i in renpy.config.window_overlay_functions:
- i()
-
- renpy.ui.close()
-
-
- def compute_scene(self, scene_lists):
- """
- This converts scene lists into a dictionary mapping layer
- name to a Fixed containing that layer.
- """
-
- rv = { }
-
- for layer in renpy.config.layers + renpy.config.top_layers:
- rv[layer] = scene_lists.make_layer(layer, self.layer_properties[layer])
-
- root = renpy.display.layout.MultiBox(layout='fixed')
- root.layers = { }
-
- for layer in renpy.config.layers:
- root.layers[layer] = rv[layer]
- root.add(rv[layer])
- rv[None] = root
-
- return rv
-
-
- def quit_event(self):
- """
- This is called to handle the user invoking a quit.
- """
-
- if self.quit_time > (time.time() - .75):
- raise renpy.game.QuitException()
-
- if renpy.config.quit_action is not None:
- self.quit_time = time.time()
-
- # Make the screen more suitable for interactions.
- renpy.exports.movie_stop(only_fullscreen=True)
- renpy.store.mouse_visible = True
-
- renpy.display.behavior.run(renpy.config.quit_action)
- else:
- raise renpy.game.QuitException()
-
-
- def get_mouse_info(self):
- # Figure out if the mouse visibility algorithm is hiding the mouse.
- if self.mouse_event_time + renpy.config.mouse_hide_time < renpy.display.core.get_time():
- visible = False
- else:
- visible = renpy.store.mouse_visible and (not renpy.game.less_mouse)
-
- visible = visible and self.show_mouse
-
- # If not visible, hide the mouse.
- if not visible:
- return False, 0, 0, None
-
- # Deal with a hardware mouse, the easy way.
- if not renpy.config.mouse:
- return True, 0, 0, None
-
- # Deal with the mouse going offscreen.
- if not self.focused:
- return False, 0, 0, None
-
- mouse_kind = renpy.display.focus.get_mouse() or self.mouse
-
- # Figure out the mouse animation.
- if mouse_kind in renpy.config.mouse:
- anim = renpy.config.mouse[mouse_kind]
- else:
- anim = renpy.config.mouse[getattr(renpy.store, 'default_mouse', 'default')]
-
- img, x, y = anim[self.ticks % len(anim)]
- tex = renpy.display.im.load_image(img)
-
- return False, x, y, tex
-
- def drawn_since(self, seconds_ago):
- """
- Returns true if the screen has been drawn in the last `seconds_ago`,
- and false otherwise.
- """
-
- return (get_time() - self.frame_time) <= seconds_ago
-
- def android_check_suspend(self):
-
- if android.check_pause():
-
- android.sound.pause_all()
-
- pygame.time.set_timer(PERIODIC, 0)
- pygame.time.set_timer(REDRAW, 0)
- pygame.time.set_timer(TIMEEVENT, 0)
-
- # The game has to be saved.
- renpy.loadsave.save("_reload-1")
-
- android.wait_for_resume()
-
- # Since we came back to life, we can get rid of the
- # auto-reload.
- renpy.loadsave.unlink_save("_reload-1")
-
- pygame.time.set_timer(PERIODIC, PERIODIC_INTERVAL)
-
- android.sound.unpause_all()
-
- def iconified(self):
- """
- Called when we become an icon.
- """
-
- if self.minimized:
- return
-
- self.minimized = True
-
- renpy.display.log.write("The window was minimized.")
-
-
- def restored(self):
- """
- Called when we are restored from being an icon.
- """
-
- # This is necessary on Windows/DirectX/Angle, as otherwise we get
- # a blank screen.
-
- if not self.minimized:
- return
-
- self.minimized = False
-
- renpy.display.log.write("The window was restored.")
-
- if renpy.windows:
- self.display_reset = True
- self.set_mode(self.last_resize)
-
- def enter_context(self):
- """
- Called when we enter a new context.
- """
-
- # Stop ongoing transitions.
- self.ongoing_transition.clear()
- self.transition_from.clear()
- self.transition_time.clear()
-
- def post_time_event(self):
- """
- Posts a time_event object to the queue.
- """
-
- try:
- pygame.event.post(self.time_event)
- except:
- pass
-
- def interact(self, clear=True, suppress_window=False, **kwargs):
- """
- This handles an interaction, restarting it if necessary. All of the
- keyword arguments are passed off to interact_core.
- """
-
- # Cancel magic error reporting.
- renpy.bootstrap.report_error = None
-
- context = renpy.game.context()
-
- if context.interacting:
- raise Exception("Cannot start an interaction in the middle of an interaction, without creating a new context.")
-
- context.interacting = True
-
-
- # Show a missing window.
- if not suppress_window:
- self.show_window()
-
- # These things can be done once per interaction.
-
- preloads = self.preloads
- self.preloads = [ ]
-
- try:
- renpy.game.after_rollback = False
-
- for i in renpy.config.start_interact_callbacks:
- i()
-
- repeat = True
-
- while repeat:
- repeat, rv = self.interact_core(preloads=preloads, **kwargs)
-
- return rv
-
- finally:
-
- context.interacting = False
-
- # Clean out transient stuff at the end of an interaction.
- if clear:
- scene_lists = renpy.game.context().scene_lists
- scene_lists.replace_transient()
-
- self.ongoing_transition = { }
- self.transition_time = { }
- self.transition_from = { }
-
- self.restart_interaction = True
-
- renpy.game.context().scene_lists.shown_window = False
-
- def interact_core(self,
- show_mouse=True,
- trans_pause=False,
- suppress_overlay=False,
- suppress_underlay=False,
- mouse='default',
- preloads=[],
- roll_forward=None,
- ):
-
- """
- This handles one cycle of displaying an image to the user,
- and then responding to user input.
-
- @param show_mouse: Should the mouse be shown during this
- interaction? Only advisory, and usually doesn't work.
-
- @param trans_pause: If given, we must have a transition. Should we
- add a pause behavior during the transition?
-
- @param suppress_overlay: This suppresses the display of the overlay.
- @param suppress_underlay: This suppresses the display of the underlay.
- """
-
- self.roll_forward = roll_forward
- self.show_mouse = show_mouse
-
- suppress_transition = renpy.config.skipping or renpy.game.less_updates
-
- # The global one.
- self.suppress_transition = False
-
- # Figure out transitions.
- for k in self.transition:
- if k not in self.old_scene:
- continue
-
- self.ongoing_transition[k] = self.transition[k]
- self.transition_from[k] = self.old_scene[k]
- self.transition_time[k] = None
-
- self.transition.clear()
-
- if suppress_transition:
- self.ongoing_transition.clear()
- self.transition_from.clear()
- self.transition_time.clear()
-
- ## Safety condition, prevents deadlocks.
- if trans_pause:
- if not self.ongoing_transition:
- return False, None
- if None not in self.ongoing_transition:
- return False, None
- if suppress_transition:
- return False, None
- if not self.old_scene:
- return False, None
-
- # Check to see if the language has changed.
- renpy.translation.check_language()
-
- # We just restarted.
- self.restart_interaction = False
-
- # Setup the mouse.
- self.mouse = mouse
-
- # The start and end times of this interaction.
- start_time = get_time()
- end_time = start_time
-
- # frames = 0
-
- for i in renpy.config.interact_callbacks:
- i()
-
- # Set the window caption.
- self.set_window_caption()
-
- # Tick time forward.
- renpy.display.im.cache.tick()
- renpy.text.text.layout_cache_tick()
- renpy.display.predict.reset()
-
- # Cleare the size groups.
- renpy.display.layout.size_groups.clear()
-
- # Clear some events.
- pygame.event.clear((pygame.MOUSEMOTION,
- PERIODIC,
- TIMEEVENT,
- REDRAW))
-
- # Add a single TIMEEVENT to the queue.
- self.post_time_event()
-
- # Figure out the scene list we want to show.
- scene_lists = renpy.game.context().scene_lists
-
- # Remove the now-hidden things.
- scene_lists.remove_hidden()
-
- # Compute the overlay.
- if not suppress_overlay:
- self.compute_overlay()
-
- # The root widget of everything that is displayed on the screen.
- root_widget = renpy.display.layout.MultiBox(layout='fixed')
- root_widget.layers = { }
-
- # A list of widgets that are roots of trees of widgets that are
- # considered for focusing.
- focus_roots = [ ]
-
- # Add the underlay to the root widget.
- if not suppress_underlay:
- for i in renpy.config.underlay:
- root_widget.add(i)
- focus_roots.append(i)
-
- if roll_forward is not None:
- rfw = renpy.display.behavior.RollForward(roll_forward)
- root_widget.add(rfw)
- focus_roots.append(rfw)
-
- # Figure out the scene. (All of the layers, and the root.)
- scene = self.compute_scene(scene_lists)
-
- # If necessary, load all images here.
- for w in scene.values():
- try:
- renpy.display.predict.displayable(w)
- except:
- pass
-
- # The root widget of all of the layers.
- layers_root = renpy.display.layout.MultiBox(layout='fixed')
- layers_root.layers = { }
-
- def add_layer(where, layer):
-
- scene_layer = scene[layer]
- focus_roots.append(scene_layer)
-
- if (self.ongoing_transition.get(layer, None) and
- not suppress_transition):
-
- trans = self.ongoing_transition[layer](
- old_widget=self.transition_from[layer],
- new_widget=scene_layer)
-
- if not isinstance(trans, Displayable):
- raise Exception("Expected transition to be a displayable, not a %r" % trans)
-
- transition_time = self.transition_time.get(layer, None)
-
- where.add(trans, transition_time, transition_time)
- where.layers[layer] = trans
-
- else:
- where.layers[layer] = scene_layer
- where.add(scene_layer)
-
- # Add layers (perhaps with transitions) to the layers root.
- for layer in renpy.config.layers:
- add_layer(layers_root, layer)
-
- # Add layers_root to root_widget, perhaps through a transition.
- if (self.ongoing_transition.get(None, None) and
- not suppress_transition):
-
- old_root = renpy.display.layout.MultiBox(layout='fixed')
- old_root.layers = { }
-
- for layer in renpy.config.layers:
- d = self.transition_from[None].layers[layer]
- old_root.layers[layer] = d
- old_root.add(d)
-
- trans = self.ongoing_transition[None](
- old_widget=old_root,
- new_widget=layers_root)
-
- if not isinstance(trans, Displayable):
- raise Exception("Expected transition to be a displayable, not a %r" % trans)
-
- trans._show()
-
- transition_time = self.transition_time.get(None, None)
- root_widget.add(trans, transition_time, transition_time)
-
- if trans_pause:
- sb = renpy.display.behavior.SayBehavior()
- root_widget.add(sb)
- focus_roots.append(sb)
-
- pb = renpy.display.behavior.PauseBehavior(trans.delay)
- root_widget.add(pb, transition_time, transition_time)
- focus_roots.append(pb)
-
- else:
- root_widget.add(layers_root)
-
- # Add top_layers to the root_widget.
- for layer in renpy.config.top_layers:
- add_layer(root_widget, layer)
-
- prediction_coroutine = renpy.display.predict.prediction_coroutine(root_widget)
- prediction_coroutine.send(None)
-
- # Clean out the registered adjustments.
- renpy.display.behavior.adj_registered.clear()
-
- # Clean up some movie-related things.
- renpy.display.video.early_interact()
-
- # Call per-interaction code for all widgets.
- root_widget.visit_all(lambda i : i.per_interact())
-
- # Now, update various things regarding scenes and transitions,
- # so we are ready for a new interaction or a restart.
- self.old_scene = scene
-
- # Okay, from here on we now have a single root widget (root_widget),
- # which we will try to show to the user.
-
- # Figure out what should be focused.
- renpy.display.focus.before_interact(focus_roots)
-
- # Redraw the screen.
- renpy.display.render.process_redraws()
- needs_redraw = True
-
- # First pass through the while loop?
- first_pass = True
-
- # We don't yet know when the interaction began.
- self.interact_time = None
-
- # We only want to do autosave once.
- did_autosave = False
-
- old_timeout_time = None
- old_redraw_time = None
-
- rv = None
-
- # Start sound.
- renpy.audio.audio.interact()
-
- # How long until we redraw.
- redraw_in = 3600
-
- # Have we drawn a frame yet?
- video_frame_drawn = False
-
- # This try block is used to force cleanup even on termination
- # caused by an exception propagating through this function.
- try:
-
- while rv is None:
-
- # Check for a change in fullscreen preference.
- if self.fullscreen != renpy.game.preferences.fullscreen or self.display_reset:
- self.set_mode()
- needs_redraw = True
-
- # Check for suspend.
- if android:
- self.android_check_suspend()
-
- # Redraw the screen.
- if (self.force_redraw or
- ((first_pass or not pygame.event.peek(ALL_EVENTS)) and
- renpy.display.draw.should_redraw(needs_redraw, first_pass))):
-
- self.force_redraw = False
-
- # If we have a movie, start showing it.
- fullscreen_video = renpy.display.video.interact()
-
- # Clean out the redraws, if we have to.
- # renpy.display.render.kill_redraws()
-
- # Draw the screen.
- self.frame_time = get_time()
-
- if not self.interact_time:
- self.interact_time = self.frame_time
-
- self.draw_screen(root_widget, fullscreen_video, (not fullscreen_video) or video_frame_drawn)
-
- if first_pass:
- scene_lists.set_times(self.interact_time)
- for k, v in self.transition_time.items():
- if v is None:
- self.transition_time[k] = self.interact_time
-
- renpy.config.frames += 1
-
- # If profiling is enabled, report the profile time.
- if renpy.config.profile :
- new_time = get_time()
-
- if new_time - self.profile_time > .015:
- print("Profile: Redraw took %f seconds." % (new_time - self.frame_time))
- print("Profile: %f seconds to complete event." % (new_time - self.profile_time))
-
- if first_pass and self.last_event:
- x, y = renpy.display.draw.get_mouse_pos()
- renpy.display.focus.mouse_handler(self.last_event, x, y, default=False)
-
- needs_redraw = False
- first_pass = False
-
- pygame.time.set_timer(REDRAW, 0)
- pygame.event.clear([REDRAW])
- old_redraw_time = None
-
- # Draw the mouse, if it needs drawing.
- renpy.display.draw.update_mouse()
-
- # See if we want to restart the interaction entirely.
- if self.restart_interaction:
- return True, None
-
- # Determine if we need a redraw. (We want to run these
- # functions, so we put them first to prevent short-circuiting.)
-
- if renpy.display.video.frequent():
- needs_redraw = True
- video_frame_drawn = True
-
- needs_redraw = renpy.display.video.frequent() or needs_redraw
- needs_redraw = renpy.display.render.process_redraws() or needs_redraw
-
- # How many seconds until we timeout.
- timeout_in = 3600
-
- # Handle the redraw timer.
- redraw_time = renpy.display.render.redraw_time()
-
- if (redraw_time is not None) and not needs_redraw:
- if redraw_time != old_redraw_time:
- time_left = redraw_time - get_time()
- time_left = min(time_left, 3600)
- redraw_in = time_left
-
- if time_left <= 0:
- try:
- pygame.event.post(self.redraw_event)
- except:
- pass
- pygame.time.set_timer(REDRAW, 0)
- else:
- pygame.time.set_timer(REDRAW, max(int(time_left * 1000), 1))
-
- old_redraw_time = redraw_time
- else:
- redraw_in = 3600
- pygame.time.set_timer(REDRAW, 0)
-
- # Handle the timeout timer.
- if not self.timeout_time:
- pygame.time.set_timer(TIMEEVENT, 0)
- else:
- time_left = self.timeout_time - get_time()
- time_left = min(time_left, 3600)
- timeout_in = time_left
-
- if time_left <= 0:
- self.timeout_time = None
- pygame.time.set_timer(TIMEEVENT, 0)
- self.post_time_event()
- elif self.timeout_time != old_timeout_time:
- # Always set to at least 1ms.
- pygame.time.set_timer(TIMEEVENT, int(time_left * 1000 + 1))
- old_timeout_time = self.timeout_time
-
- # Predict images, if we haven't done so already.
- while prediction_coroutine is not None:
-
- # Can we do expensive prediction?
- expensive_predict = not (needs_redraw or self.event_peek() or renpy.audio.music.is_playing("movie"))
-
- result = prediction_coroutine.send(expensive_predict)
-
- if not result:
- prediction_coroutine = None
- break
-
- if not expensive_predict:
- break
-
- # If we need to redraw again, do it if we don't have an
- # event going on.
- if needs_redraw and not self.event_peek():
- if renpy.config.profile:
- self.profile_time = get_time()
- continue
-
- # Handle autosaving, as necessary.
- if not did_autosave and not needs_redraw and not self.event_peek() and redraw_in > .25 and timeout_in > .25:
- renpy.loadsave.autosave()
- did_autosave = True
-
- if needs_redraw or renpy.display.video.playing():
- ev = self.event_poll()
- else:
- ev = self.event_wait()
-
- if ev.type == pygame.NOEVENT:
- continue
-
- if renpy.config.profile:
- self.profile_time = get_time()
-
- # Try to merge an TIMEEVENT with other timeevents.
- if ev.type == TIMEEVENT:
- old_timeout_time = None
- pygame.event.clear([TIMEEVENT])
-
- # On Android, where we have multiple mouse buttons, we can
- # merge a mouse down and mouse up event with its successor. This
- # prevents us from getting overwhelmed with too many events on
- # a multitouch screen.
- if android and (ev.type == pygame.MOUSEBUTTONDOWN or ev.type == pygame.MOUSEBUTTONUP):
- pygame.event.clear(ev.type)
-
- # Handle redraw timeouts.
- if ev.type == REDRAW:
- pygame.event.clear([REDRAW])
- old_redraw_time = None
- continue
-
- # Handle periodic events. This includes updating the mouse timers (and through the loop,
- # the mouse itself), and the audio system periodic calls.
- if ev.type == PERIODIC:
- events = 1 + len(pygame.event.get([PERIODIC]))
- self.ticks += events
-
- if renpy.config.periodic_callback:
- renpy.config.periodic_callback()
-
- renpy.audio.audio.periodic()
- continue
-
- # This can set the event to None, to ignore it.
- ev = renpy.display.joystick.event(ev)
- if not ev:
- continue
-
- # Handle skipping.
- renpy.display.behavior.skipping(ev)
-
- # Handle quit specially for now.
- if ev.type == pygame.QUIT:
- self.quit_event()
- continue
-
- # Handle videoresize.
- if ev.type == pygame.VIDEORESIZE:
- evs = pygame.event.get([pygame.VIDEORESIZE])
- if len(evs):
- ev = evs[-1]
-
- if self.last_resize != ev.size:
- self.last_resize = ev.size
- self.set_mode((ev.w, ev.h))
-
- continue
-
- if ev.type == pygame.MOUSEMOTION or \
- ev.type == pygame.MOUSEBUTTONDOWN or \
- ev.type == pygame.MOUSEBUTTONUP:
-
- self.mouse_event_time = renpy.display.core.get_time()
-
- # Merge mousemotion events.
- if ev.type == pygame.MOUSEMOTION:
- evs = pygame.event.get([pygame.MOUSEMOTION])
- if len(evs):
- ev = evs[-1]
-
- if renpy.windows:
- self.focused = True
-
- # Handle focus notifications.
- if ev.type == pygame.ACTIVEEVENT:
- if ev.state & 1:
- self.focused = ev.gain
-
- if ev.state & 4:
- if ev.gain:
- self.restored()
- else:
- self.iconified()
-
- pygame.key.set_mods(0)
-
- # This returns the event location. It also updates the
- # mouse state as necessary.
- x, y = renpy.display.draw.mouse_event(ev)
-
- if not self.focused:
- x = -1
- y = -1
-
- self.event_time = end_time = get_time()
-
- try:
-
- # Handle the event normally.
- rv = renpy.display.focus.mouse_handler(ev, x, y)
-
-
- if rv is None:
- rv = root_widget.event(ev, x, y, 0)
-
- if rv is None:
- rv = renpy.display.focus.key_handler(ev)
-
- if rv is not None:
- break
-
- # Handle displayable inspector.
- if renpy.config.inspector and renpy.display.behavior.inspector(ev):
- l = self.surftree.main_displayables_at_point(x, y, renpy.config.transient_layers + renpy.config.context_clear_layers + renpy.config.overlay_layers)
- renpy.game.invoke_in_new_context(renpy.config.inspector, l)
-
- except IgnoreEvent:
- # An ignored event can change the timeout. So we want to
- # process an TIMEEVENT to ensure that the timeout is
- # set correctly.
- self.post_time_event()
-
-
- # Check again after handling the event.
- needs_redraw |= renpy.display.render.process_redraws()
-
- if self.restart_interaction:
- return True, None
-
- # If we were trans-paused and rv is true, suppress
- # transitions up to the next interaction.
- if trans_pause and rv:
- self.suppress_transition = True
-
- # But wait, there's more! The finally block runs some cleanup
- # after this.
- return False, rv
-
- except EndInteraction as e:
- return False, e.value
-
- finally:
-
- renpy.exports.say_attributes = None
-
- # Clean out the overlay layers.
- for i in renpy.config.overlay_layers:
- scene_lists.clear(i)
-
- # Stop ongoing preloading.
- renpy.display.im.cache.end_tick()
-
- # We no longer disable periodic between interactions.
- # pygame.time.set_timer(PERIODIC, 0)
-
- pygame.time.set_timer(TIMEEVENT, 0)
- pygame.time.set_timer(REDRAW, 0)
-
- renpy.game.context().runtime += end_time - start_time
-
- # Restart the old interaction, which also causes a
- # redraw if needed.
- self.restart_interaction = True
-
- # print "It took", frames, "frames."
-
- def timeout(self, offset):
- if offset < 0:
- return
-
- if self.timeout_time:
- self.timeout_time = min(self.event_time + offset, self.timeout_time)
- else:
- self.timeout_time = self.event_time + offset
-
diff --git a/unrpyc/renpy/display/dragdrop.py b/unrpyc/renpy/display/dragdrop.py
deleted file mode 100644
index 0b99e63..0000000
--- a/unrpyc/renpy/display/dragdrop.py
+++ /dev/null
@@ -1,731 +0,0 @@
-# Copyright 2004-2013 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.
-
-# TODO: Use overlap (rather than simple pointer location) to determine
-# drag and drop.
-
-import renpy.display
-from renpy.display.render import render, Render, redraw
-from renpy.display.core import absolute
-from renpy.display.behavior import map_event, run
-
-import pygame
-
-def default_drag_group():
- """
- Gets the default drag group. If it doesn't exist yet, creates it.
- """
-
- sls = renpy.game.context().scene_lists
-
- rv = sls.drag_group
-
- if rv is None:
- rv = DragGroup()
- sls.drag_group = rv
-
- return rv
-
-def default_drag_joined(drag):
- return [ (drag, 0, 0) ]
-
-class Drag(renpy.display.core.Displayable, renpy.python.RevertableObject):
- """
- :doc: drag_drop class
- :args: (d=None, drag_name=None, draggable=True, droppable=True, drag_raise=True, dragged=None, dropped=None, drag_handle=(0.0, 0.0, 1.0, 1.0), drag_joined=..., clicked=None, hovered=None, unhovered=None, **properties)
-
- A displayable that represents an object that can be dragged around
- its enclosing area. A Drag can also represent an area that
- other Drags can be dropped on.
-
- A Drag can be moved around inside is parent. Generally, its parent
- should be either a :func:`Fixed` or :class:`DragGroup`.
-
- A Drag has one child. The child's state reflects the status
- of the drag and drop operation:
-
- * ``selected_hover`` - when it is being dragged.
- * ``selected_idle`` - when it can be dropped on.
- * ``hover`` - when the draggable will be dragged when the mouse is
- clicked.
- * ``idle`` - otherwise.
-
- The drag handle is a rectangle inside the child. The mouse must be over
- a non-transparent pixel inside the drag handle for dragging or clicking
- to occur.
-
- A newly-created draggable is added to the default DragGroup. A draggable
- can only be in a single DragGroup - if it's added to a second group,
- it's removed from the first.
-
- When a Drag is first rendered, if it's position cannot be determined
- from the DragGroup it is in, the position of its upper-left corner
- is computed using the standard layout algorithm. Once that position
-
-
- `d`
- If present, the child of this Drag. Drags use the child style
- in preference to this, if it's not None.
-
- `drag_name`
- If not None, the name of this draggable. This is available
- as the `name` property of draggable objects. If a Drag
- with the same name is or was in the DragGroup, the starting
- position of this Drag is taken from that Draggable.
-
- `draggable`
- If true, the Drag can be dragged around the screen with
- the mouse.
-
- `droppable`
- If true, other Drags can be dropped on this Drag.
-
- `drag_raise`
- If true, this Drag is raised to the top when it is dragged. If
- it is joined to other Drags, all joined drags are raised.
-
- `dragged`
- A callback (or list of callbacks) that is called when the Drag
- has been dragged. It is called with two arguments. The first is
- a list of Drags that are being dragged. The second is either
- a Drag that is being dropped onto, or None of a drop did not
- occur. If the callback returns a value other than None, that
- value is returned as the result of the interaction.
-
- `dropped`
- A callback (or list of callbacks) that is called when this Drag
- is dropped onto. It is called with two arguments. The first
- is the Drag being dropped onto. The second is a list of Drags that
- are being dragged. If the callback returns a value other than None,
- that value is returned as the result of the interaction.
-
- When a dragged and dropped callback are triggered for the same
- event, the dropped callback is only called if dragged returns
- None.
-
- `clicked`
- A callback this is called, with no arguments, when the Drag is
- clicked without being moved. A droppable can also be focused
- and clicked. If the callback returns a value othe than None,
- that value is returned as the result of the interaction.
-
- `drag_handle`
- A (x, y, width, height) tuple, giving the position of the drag
- handle within the child. In this tuple, integers are considered
- to be a literal number of pixels, while floats are relative to
- the size of the child.
-
- `drag_joined`
- This is called with the current Drag as an argument. It's
- expected to return a list of [ (drag, x, y) ] tuples, giving
- the draggables to drag as a unit. `x` and `y` are the offsets
- of the drags relative to each other, they are not relative
- to the corner of this drag.
-
- Except for `d`, all of the parameters are available as fields (with
- the same name) on the Drag object. In addition, after the drag has
- been rendered, the following fields become available:
-
- `x`, `y`
- The position of the Drag relative to its parent, in pixels.
-
- `w`, `h`
- The width and height of the Drag's child, in pixels.
- """
-
- def __init__(self,
- d=None,
- drag_name=None,
- draggable=True,
- droppable=True,
- drag_raise=True,
- dragged=None,
- dropped=None,
- drag_handle=(0.0, 0.0, 1.0, 1.0),
- drag_joined=default_drag_joined,
- clicked=None,
- hovered=None,
- unhovered=None,
- replaces=None,
- **properties):
-
- super(Drag, self).__init__(self, **properties)
-
- self.drag_name = drag_name
- self.draggable = draggable
- self.droppable = droppable
- self.drag_raise = drag_raise
- self.dragged = dragged
- self.dropped = dropped
- self.drag_handle = drag_handle
- self.drag_joined = drag_joined
- self.clicked = clicked
- self.hovered = hovered
- self.unhovered = unhovered
-
- self.child = None
-
- # Add us to a drag group on creation.
- if drag_name:
- self.drag_group = default_drag_group()
-
- # The current x and y coordinates of this displayable.
- self.x = None
- self.y = None
-
- # The width and height of the child.
- self.w = None
- self.h = None
-
- # The width and height of our parent.
- self.parent_width = None
- self.parent_height = None
-
- # The target x and y coordinates of this displayable. (The
- # coordinates that we're snapping to.)
- self.target_x = None
- self.target_y = None
-
- # The offset from the location of the mouse to the "grab point",
- # which is where the things that are being moved are offset from.
- self.grab_x = None
- self.grab_y = None
-
- # x and y from the last time we rendered.
- self.last_x = None
- self.last_y = None
-
- # The abs_x and abs_y from when we started the grab.
- self.start_x = 0
- self.start_y = 0
-
- # The last time we were shown, using the animation timebases.
- self.at = 0
-
- # The (animation timebase) time at which we should reach
- # the target coordinates.
- self.target_at = 0
-
- # The displayable we were last dropping on.
- self.last_drop = None
-
- # Did we move over the course of this drag?
- self.drag_moved = False
-
- if replaces is not None:
- self.x = replaces.x
- self.y = replaces.y
- self.at = replaces.at
- self.target_x = replaces.target_x
- self.target_y = replaces.target_y
- self.target_at = replaces.target_at
-
- if d is not None:
- self.add(d)
-
-
- def snap(self, x, y, delay=0):
- """
- :doc: drag_drop method
-
- Changes the position of the drag. If the drag is not showing,
- then the position change is instantaneous. Otherwise, the
- position change takes `delay` seconds, and is animated as a
- linear move.
- """
-
- self.target_x = x
- self.target_y = y
-
- if self.x is not None:
- self.target_at = self.at + delay
- else:
- self.target_at = self.at
- self.x = x
- self.y = y
-
- redraw(self, 0)
-
- def set_style_prefix(self, prefix, root):
- super(Drag, self).set_style_prefix(prefix, root)
-
- if self.child is not None:
- self.child.set_style_prefix(prefix, False)
-
- def add(self, d):
- if self.child is not None:
- raise Exception("Drag expects either zero or one children.")
-
- self.child = renpy.easy.displayable(d)
-
- def set_child(self, d):
- """
- :doc: drag_drop method
-
- Changes the child of this drag to `d`.
- """
-
- d.per_interact()
- self.child = renpy.easy.displayable(d)
-
- def top(self):
- """
- :doc: drag_drop method
-
- Raises this displayable to the top of its drag_group.
- """
-
- if self.drag_group is not None:
- self.drag_group.raise_children([ self ])
-
- def visit(self):
- return [ self.child ]
-
- def focus(self, default=False):
- super(Drag, self).focus(default)
-
- rv = None
-
- if not default:
- rv = run(self.hovered)
-
- return rv
-
- def unfocus(self, default=False):
- super(Drag, self).unfocus(default)
-
- if not default:
- run(self.unhovered)
-
- def render(self, width, height, st, at):
-
- child = self.style.child
- if child is None:
- child = self.child
-
- self.parent_width = width
- self.parent_height = height
-
- cr = render(child, width, height, st, at)
- cw, ch = cr.get_size()
-
- rv = Render(cw, ch)
- rv.blit(cr, (0, 0))
-
- self.w = cw
- self.h = ch
-
- # If we don't have a position, then look for it in a drag group.
- if (self.x is None) and (self.drag_group is not None) and (self.drag_name is not None):
- if self.drag_name in self.drag_group.positions:
- self.x, self.y = self.drag_group.positions[self.drag_name]
-
- # If we don't have a position, run the placement code and use
- # that to compute our placement.
- if self.x is None:
- self.x, self.y = self.place(None, 0, 0, width, height, rv)
- self.x = int(self.x)
- self.y = int(self.y)
-
- if self.target_x is None:
- self.target_x = self.x
- self.target_y = self.y
- self.target_at = at
-
- # Determine if we need to do the snap animation.
- if at >= self.target_at:
- self.x = self.target_x
- self.y = self.target_y
- else:
- done = (at - self.at) / (self.target_at - self.at)
- self.x = absolute(self.x + done * (self.target_x - self.x))
- self.y = absolute(self.y + done * (self.target_y - self.y))
- redraw(self, 0)
-
- if self.draggable or self.clicked is not None:
-
- fx, fy, fw, fh = self.drag_handle
-
- if isinstance(fx, float):
- fx = int(fx * cw)
-
- if isinstance(fy, float):
- fy = int(fy * ch)
-
- if isinstance(fw, float):
- fw = int(fw * cw)
-
- if isinstance(fh, float):
- fh = int(fh * ch)
-
- rv.add_focus(self, None, fx, fy, fw, fh, fx, fy, cr.subsurface((fx, fy, fw, fh)))
-
- self.last_x = self.x
- self.last_y = self.y
- self.at = at
-
- return rv
-
- def event(self, ev, x, y, st):
-
- if not self.is_focused():
- return self.child.event(ev, x, y, st)
-
- # if not self.draggable:
- # return self.child.event(ev, x, y, st)
-
- # Mouse, in parent-relative coordinates.
- par_x = self.last_x + x
- par_y = self.last_y + y
-
- grabbed = (renpy.display.focus.get_grab() is self)
-
- if grabbed:
- joined_offsets = self.drag_joined(self)
- joined = [ i[0] for i in joined_offsets ]
-
- elif self.draggable and map_event(ev, "drag_activate"):
-
- joined_offsets = self.drag_joined(self)
- joined = [ i[0] for i in joined_offsets ]
-
- if not joined:
- raise renpy.display.core.IgnoreEvent()
-
- renpy.display.focus.set_grab(self)
-
- self.grab_x = x
- self.grab_y = y
-
- # If we're not the only thing we're joined with, we
- # might need to adjust our grab point.
- for i, xo, yo in joined_offsets:
- if i is self:
- self.grab_x += xo
- self.grab_y += yo
- break
-
- self.drag_moved = False
- self.start_x = par_x
- self.start_y = par_y
-
- grabbed = True
-
- # Handle clicking on droppables.
- if not grabbed:
- if self.clicked is not None and map_event(ev, "drag_deactivate"):
- rv = run(self.clicked)
- if rv is not None:
- return rv
-
- raise renpy.display.core.IgnoreEvent()
-
- return self.child.event(ev, x, y, st)
-
- # Handle moves by moving things relative to the grab point.
- if ev.type in (pygame.MOUSEMOTION, pygame.MOUSEBUTTONUP, pygame.MOUSEBUTTONDOWN):
-
- if not self.drag_moved and (self.start_x != par_x or self.start_y != par_y):
- self.drag_moved = True
-
- # We may not be in the drag_joined group.
- self.set_style_prefix("idle_", True)
-
- # Set the style.
- for i in joined:
- i.set_style_prefix("selected_hover_", True)
-
- # Raise the joined items.
- if self.drag_raise and self.drag_group is not None:
- self.drag_group.raise_children(joined)
-
- if self.drag_moved:
- for i, xo, yo in joined_offsets:
-
- new_x = par_x - self.grab_x + xo
- new_y = par_y - self.grab_y + yo
- new_x = max(new_x, 0)
- new_x = min(new_x, i.parent_width - i.w)
- new_y = max(new_y, 0)
- new_y = min(new_y, i.parent_height - i.h)
-
- if i.drag_group is not None and i.drag_name is not None:
- i.drag_group.positions[i.drag_name] = (new_x, new_y)
-
- i.x = new_x
- i.y = new_y
- i.target_x = new_x
- i.target_y = new_y
- i.target_at = self.at
- redraw(i, 0)
-
- if (self.drag_group is not None) and self.drag_moved:
- drop = self.drag_group.get_best_drop(joined)
- else:
- drop = None
-
- if drop is not self.last_drop:
-
- if self.last_drop is not None:
- self.last_drop.set_style_prefix("idle_", True)
-
- if drop is not None:
- drop.set_style_prefix("selected_idle_", True)
-
- self.last_drop = drop
-
- if map_event(ev, 'drag_deactivate'):
- renpy.display.focus.set_grab(None)
-
- if drop is not None:
- drop.set_style_prefix("idle_", True)
-
- for i in joined:
- i.set_style_prefix("idle_", True)
-
- self.set_style_prefix("hover_", True)
-
- self.grab_x = None
- self.grab_y = None
- self.last_drop = None
-
- if self.drag_moved:
-
- # Call the drag callback.
- drag = joined[0]
- if drag.dragged is not None:
- rv = run(drag.dragged, joined, drop)
- if rv is not None:
- return rv
-
- # Call the drop callback.
- if drop is not None and drop.dropped is not None:
- rv = run(drop.dropped, drop, joined)
- if rv is not None:
- return rv
-
- else:
-
- # Call the clicked callback.
- if self.clicked:
- rv = run(self.clicked)
- if rv is not None:
- return rv
-
- raise renpy.display.core.IgnoreEvent()
-
-
- def get_placement(self):
-
- if self.x is not None:
- return self.x, self.y, 0, 0, 0, 0, True
- else:
- return super(Drag, self).get_placement()
-
- def per_interact(self):
- self.set_style_prefix("idle_", True)
- super(Drag, self).per_interact()
-
-
-class DragGroup(renpy.display.layout.MultiBox):
- """
- :doc: drag_drop class
-
- Represents a group of Drags. A Drag is limited to the boundary of
- its DragGroup. Dropping only works between Drags that are in the
- same DragGroup. Drags may only be raised when they are inside a
- DragGroup.
-
- A DragGroup is laid out like a :func:`Fixed`.
-
- All positional parameters to the DragGroup constructor should be
- Drags, that are added to the DragGroup.
- """
-
- _list_type = renpy.python.RevertableList
-
- def __init__(self, *children, **properties):
- properties.setdefault("style", "fixed")
- properties.setdefault("layout", "fixed")
-
- replaces = properties.pop("replaces", None)
-
- super(DragGroup, self).__init__(**properties)
-
- if replaces is not None:
- self.positions = renpy.python.RevertableDict(replaces.positions)
- self.sensitive = replaces.sensitive
- else:
- self.positions = renpy.python.RevertableDict()
- self.sensitive = True
-
- for i in children:
- self.add(i)
-
-
- def add(self, child):
- """
- :doc: drag_drop method
-
- Adds `child`, which must be a Drag, to this DragGroup.
- """
-
- if not isinstance(child, Drag):
- raise Exception("Only drags can be added to a drag group.")
-
- child.drag_group = self
- super(DragGroup, self).add(child)
-
- def remove(self, child):
- """
- :doc: drag_drop method
-
- Removes `child` from this DragGroup.
- """
-
-
- if not isinstance(child, Drag):
- raise Exception("Only drags can be removed from a drag group.")
-
- child.x = None
- super(DragGroup, self).remove(child)
-
-
- def event(self, ev, x, y, st):
-
- if not self.sensitive:
- return None
-
- return super(DragGroup, self).event(ev, x, y, st)
-
- def raise_children(self, l):
- """
- Raises the children in `l` to the top of this drag_group, using the
- order given in l for those children.
- """
-
- s = set(l)
-
- offset_map = { }
-
- children = [ ]
- offsets = [ ]
-
- for i, c in enumerate(self.children):
- if i < len(self.offsets):
- o = self.offsets[i]
- else:
- o = (0, 0)
-
- if c not in s:
- children.append(c)
- offsets.append(o)
- else:
- offset_map[c] = o
-
- for c in l:
- if c in offset_map:
- children.append(c)
- offsets.append(offset_map[c])
-
- self.children = self._list_type(children)
- self.offsets = self._list_type(offsets)
-
-
- def get_best_drop(self, joined):
- """
- Returns the droppable that the members of joined overlap the most.
- """
-
- max_overlap = 0
- rv = 0
-
- joined_set = set(joined)
-
- for d in joined:
-
- r1 = (d.x, d.y, d.w, d.h)
-
- for c in self.children:
- if c in joined_set:
- continue
-
- if not c.droppable:
- continue
-
- r2 = (c.x, c.y, c.w, c.h)
-
- overlap = rect_overlap_area(r1, r2)
-
- if overlap >= max_overlap:
- rv = c
- max_overlap = overlap
-
- if max_overlap <= 0:
- return None
- else:
- return rv
-
- def get_children(self):
- """
- Returns a list of Drags that are the children of
- this DragGroup.
- """
-
- return renpy.python.RevertableList(self.children)
-
- def get_child_by_name(self, name):
- """
- :doc: drag_drop method
-
- Returns the first child of this DragGroup that has a drag_name
- of name.
- """
-
- for i in self.children:
- if i.drag_name == name:
- return i
-
- return None
-
-
-def rect_overlap_area(r1, r2):
- """
- Returns the number of pixels by which rectangles r1 and r2 overlap.
- """
-
- x1, y1, w1, h1 = r1
- x2, y2, w2, h2 = r2
-
- maxleft = max(x1, x2)
- minright = min(x1 + w1, x2 + w2)
- maxtop = max(y1, y2)
- minbottom = min(y1 + h1, y2 + h2)
-
- if minright < maxleft:
- return 0
-
- if minbottom < maxtop:
- return 0
-
- return (minright - maxleft) * (minbottom - maxtop)
-
-
diff --git a/unrpyc/renpy/display/error.py b/unrpyc/renpy/display/error.py
deleted file mode 100644
index a8f7e1b..0000000
--- a/unrpyc/renpy/display/error.py
+++ /dev/null
@@ -1,159 +0,0 @@
-# Copyright 2004-2013 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.
-
-# This file contains code to handle GUI-based error reporting.
-
-import renpy.display
-import os
-
-error_handled = False
-
-##############################################################################
-# Initialized approach.
-
-def call_exception_screen(screen_name, **kwargs):
- try:
-
- old_quit = renpy.config.quit_action
- renpy.config.quit_action = renpy.exports.quit
-
- for i in renpy.config.layers:
- renpy.game.context().scene_lists.clear(i)
-
- renpy.exports.show_screen(screen_name, _transient=True, **kwargs)
- return renpy.ui.interact(mouse="screen", type="screen", suppress_overlay=True, suppress_underlay=True)
-
- finally:
- renpy.config.quit_action = old_quit
-
-def rollback_action():
- renpy.exports.rollback(force=True)
-
-def init_display():
- """
- The minimum amount of code required to init the display.
- """
-
- if not renpy.game.interface:
- renpy.display.core.Interface()
- renpy.loader.index_archives()
- renpy.display.im.cache.init()
- renpy.style.styles_built = True # The styles we use were built in renpy.main.
-
- renpy.ui.reset()
-
-def error_dump():
- """
- Handles dumps in the case where an error occurs.
- """
-
- renpy.dump.dump(True)
-
-def report_exception(short, full, traceback_fn):
- """
- Reports an exception to the user. Returns True if the exception should
- be raised by the normal reporting mechanisms. Otherwise, should raise
- the appropriate exception to cause a reload or quit or rollback.
- """
-
- global error_handled
- error_handled = True
-
- error_dump()
-
- if renpy.game.args.command != "run": #@UndefinedVariable
- return True
-
- if "RENPY_SIMPLE_EXCEPTIONS" in os.environ:
- return True
-
- if not renpy.exports.has_screen("_exception"):
- return True
-
- try:
- init_display()
- except:
- return True
-
- if renpy.display.draw is None:
- return True
-
- ignore_action = None
- rollback_action = None
- reload_action = None
-
- try:
- if not renpy.game.context().init_phase:
-
- if renpy.config.rollback_enabled:
- rollback_action = renpy.display.error.rollback_action
-
- reload_action = renpy.exports.curried_call_in_new_context("_save_reload_game")
-
- if renpy.game.context(-1).next_node is not None:
- ignore_action = renpy.ui.returns(False)
- except:
- pass
-
- renpy.game.invoke_in_new_context(
- call_exception_screen,
- "_exception",
- short=short, full=full,
- rollback_action=rollback_action,
- reload_action=reload_action,
- ignore_action=ignore_action,
- traceback_fn=traceback_fn,
- )
-
-
-def report_parse_errors(errors, error_fn):
- """
- Reports an exception to the user. Returns True if the exception should
- be raised by the normal reporting mechanisms. Otherwise, should raise
- the appropriate exception.
- """
-
- global error_handled
- error_handled = True
-
- error_dump()
-
- if renpy.game.args.command != "run": #@UndefinedVariable
- return True
-
- if "RENPY_SIMPLE_EXCEPTIONS" in os.environ:
- return True
-
- if not renpy.exports.has_screen("_parse_errors"):
- return True
-
- init_display()
-
- reload_action = renpy.exports.utter_restart
-
- renpy.game.invoke_in_new_context(
- call_exception_screen,
- "_parse_errors",
- reload_action=reload_action,
- errors=errors,
- error_fn = error_fn,
- )
-
diff --git a/unrpyc/renpy/display/focus.py b/unrpyc/renpy/display/focus.py
deleted file mode 100644
index 521fe22..0000000
--- a/unrpyc/renpy/display/focus.py
+++ /dev/null
@@ -1,449 +0,0 @@
-# Copyright 2004-2013 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.
-
-# This file contains code to manage focus on the display.
-
-import pygame
-import renpy.display
-
-class Focus(object):
-
- def __init__(self, widget, arg, x, y, w, h):
-
- self.widget = widget
- self.arg = arg
- self.x = x
- self.y = y
- self.w = w
- self.h = h
-
- def copy(self):
- return Focus(
- self.widget,
- self.arg,
- self.x,
- self.y,
- self.w,
- self.h)
-
- def __repr__(self):
- return "<Focus: %r %r (%r, %r, %r, %r)>" % (
- self.widget,
- self.arg,
- self.x,
- self.y,
- self.w,
- self.h)
-
-
-# The current focus argument.
-argument = None
-
-# The widget currently grabbing the input, if any.
-grab = None
-
-# The default focus for the current screen.
-default_focus = None
-
-# Sets the currently focused widget.
-def set_focused(widget, arg):
- global argument
- argument = arg
- renpy.game.context().scene_lists.focused = widget
-
-# Gets the currently focused widget.
-def get_focused():
- return renpy.game.context().scene_lists.focused
-
-# Get the mouse cursor for the focused widget.
-def get_mouse():
- focused = get_focused()
- if focused is None:
- return None
- else:
- return focused.style.mouse
-
-def set_grab(widget):
- global grab
- grab = widget
-
-def get_grab():
- return grab
-
-# The current list of focuses that we know about.
-focus_list = [ ]
-
-# This takes in a focus list from the rendering system.
-def take_focuses():
- global focus_list
- focus_list = [ ]
-
- renpy.display.render.take_focuses(focus_list)
-
- global default_focus
- default_focus = None
-
- for f in focus_list:
- if f.x is None:
- default_focus = f
-
-def focus_coordinates():
- """
- :doc: other
-
- This attempts to find the coordinates of the currently-focused
- displayable. If it can, it will return them as a (x, y, w, h)
- tuple. If not, it will return a (None, None, None, None) tuple.
- """
-
- current = get_focused()
-
- for i in focus_list:
- if i.widget == current and i.arg == argument:
- return i.x, i.y, i.w, i.h
-
- return None, None, None, None
-
-
-# This is called before each interaction. It's purpose is to choose
-# the widget that is focused, and to mark it as focused and all of
-# the other widgets as unfocused.
-
-# The new grab widget. (The one that replaced the old grab widget at the start
-# of the interaction.)
-new_grab = None
-
-def before_interact(roots):
-
- global new_grab
- global grab
-
- # a list of focusable, name tuples.
- fwn = [ ]
-
- def callback(f, n):
- fwn.append((f, n))
-
- for root in roots:
- root.find_focusable(callback, None)
-
- # Assign a full name to each focusable.
-
- namecount = { }
-
- for f, n in fwn:
- serial = namecount.get(n, 0)
- namecount[n] = serial + 1
-
- f.full_focus_name = n, serial
-
- # If there's something with the same full name as the current widget,
- # it becomes the new current widget.
-
- current = get_focused()
-
- if current is not None:
- current_name = current.full_focus_name
-
- for f, n in fwn:
- if f.full_focus_name == current_name:
- current = f
- set_focused(f, None)
- break
- else:
- current = None
-
- # Otherwise, focus the default widget, or nothing.
- if current is None:
-
- for f, n in fwn:
- if f.default:
- current = f
- set_focused(f, None)
- break
- else:
- set_focused(None, None)
-
- # Finally, mark the current widget as the focused widget, and
- # all other widgets as unfocused.
- for f, n in fwn:
- if f is not current:
- f.unfocus(default=True)
-
- if current:
- current.focus(default=True)
-
- grab = new_grab
- new_grab = None
-
-# This changes the focus to be the widget contained inside the new
-# focus object.
-def change_focus(newfocus, default=False):
- rv = None
-
- if grab:
- return
-
- if newfocus is None:
- widget = None
- else:
- widget = newfocus.widget
-
- current = get_focused()
-
- # Nothing to do.
- if current is widget and (newfocus is None or newfocus.arg == argument):
- return rv
-
- if current is not None:
- current.unfocus(default=default)
-
- current = widget
-
- if newfocus is not None:
- arg = newfocus.arg
- else:
- arg = None
-
- set_focused(current, arg)
-
- if widget is not None:
- rv = widget.focus(default=default)
-
- return rv
-
-# This handles mouse events, to see if they change the focus.
-def mouse_handler(ev, x, y, default=False):
-
- if ev.type not in (pygame.MOUSEMOTION, pygame.MOUSEBUTTONUP, pygame.MOUSEBUTTONDOWN):
- return
-
- new_focus = renpy.display.render.focus_at_point(x, y)
-
- if new_focus is None:
- new_focus = default_focus
-
- return change_focus(new_focus, default=default)
-
-
-# This focuses an extreme widget, which is one of the widgets that's
-# at an edge. To do this, we multiply the x, y, width, and height by
-# the supplied multiplers, add them all up, and take the focus with
-# the largest value.
-def focus_extreme(xmul, ymul, wmul, hmul):
-
- max_focus = None
- max_score = -(65536**2)
-
- for f in focus_list:
-
- if not f.x:
- continue
-
- score = (f.x * xmul +
- f.y * ymul +
- f.w * wmul +
- f.h * hmul)
-
- if score > max_score:
- max_score = score
- max_focus = f
-
- if max_focus:
- return change_focus(max_focus)
-
-
-# This calculates the distance between two points, applying
-# the given fudge factors. The distance is left squared.
-def points_dist(x0, y0, x1, y1, xfudge, yfudge):
- return (( x0 - x1 ) * xfudge ) ** 2 + \
- (( y0 - y1 ) * yfudge ) ** 2
-
-
-# This computes the distance between two horizontal lines. (So the
-# distance is either vertical, or has a vertical component to it.)
-#
-# The distance is left squared.
-def horiz_line_dist(ax0, ay0, ax1, ay1, bx0, by0, bx1, by1):
-
- # The lines overlap in x.
- if bx0 <= ax0 <= ax1 <= bx1 or \
- ax0 <= bx0 <= bx1 <= ax1 or \
- ax0 <= bx0 <= ax1 <= bx1 or \
- bx0 <= ax0 <= bx1 <= ax1:
- return (ay0 - by0) ** 2
-
- # The right end of a is to the left of the left end of b.
- if ax0 <= ax1 <= bx0 <= bx1:
- return points_dist(ax1, ay1, bx0, by0, renpy.config.focus_crossrange_penalty, 1.0)
-
- if bx0 <= bx1 <= ax0 <= ax1:
- return points_dist(ax0, ay0, bx1, by1, renpy.config.focus_crossrange_penalty, 1.0)
-
- assert False
-
-# This computes the distance between two vertical lines. (So the
-# distance is either hortizontal, or has a horizontal component to it.)
-#
-# The distance is left squared.
-def verti_line_dist(ax0, ay0, ax1, ay1, bx0, by0, bx1, by1):
-
- # The lines overlap in x.
- if by0 <= ay0 <= ay1 <= by1 or \
- ay0 <= by0 <= by1 <= ay1 or \
- ay0 <= by0 <= ay1 <= by1 or \
- by0 <= ay0 <= by1 <= ay1:
- return (ax0 - bx0) ** 2
-
- # The right end of a is to the left of the left end of b.
- if ay0 <= ay1 <= by0 <= by1:
- return points_dist(ax1, ay1, bx0, by0, 1.0, renpy.config.focus_crossrange_penalty)
-
- if by0 <= by1 <= ay0 <= ay1:
- return points_dist(ax0, ay0, bx1, by1, 1.0, renpy.config.focus_crossrange_penalty)
-
- assert False
-
-
-
-# This focuses the widget that is nearest to the current widget. To
-# determine nearest, we compute points on the widgets using the
-# {from,to}_{x,y}off values. We pick the nearest, applying a fudge
-# multiplier to the distances in each direction, that satisfies
-# the condition (which is given a Focus object to evaluate).
-#
-# If no focus can be found matching the above, we look for one
-# with an x of None, and make that the focus. Otherwise, we do
-# nothing.
-#
-# If no widget is focused, we pick one and focus it.
-#
-# If the current widget has an x of None, we pass things off to
-# focus_extreme to deal with.
-def focus_nearest(from_x0, from_y0, from_x1, from_y1,
- to_x0, to_y0, to_x1, to_y1,
- line_dist,
- condition,
- xmul, ymul, wmul, hmul):
-
- if not focus_list:
- return
-
- # No widget focused.
- current = get_focused()
-
- if not current:
- change_focus(focus_list[0])
- return
-
- # Find the current focus.
- for f in focus_list:
- if f.widget is current and f.arg == argument:
- from_focus = f
- break
- else:
- # If we can't pick something.
- change_focus(focus_list[0])
- return
-
- # If placeless, focus_extreme.
- if from_focus.x is None:
- focus_extreme(xmul, ymul, wmul, hmul)
- return
-
- fx0 = from_focus.x + from_focus.w * from_x0
- fy0 = from_focus.y + from_focus.h * from_y0
- fx1 = from_focus.x + from_focus.w * from_x1
- fy1 = from_focus.y + from_focus.h * from_y1
-
- placeless = None
- new_focus = None
-
- # a really big number.
- new_focus_dist = (65536.0 * renpy.config.focus_crossrange_penalty) ** 2
-
- for f in focus_list:
-
- if f is from_focus:
- continue
-
- if f.x is None:
- placeless = f
- continue
-
- if not condition(from_focus, f):
- continue
-
- tx0 = f.x + f.w * to_x0
- ty0 = f.y + f.h * to_y0
- tx1 = f.x + f.w * to_x1
- ty1 = f.y + f.h * to_y1
-
- dist = line_dist(fx0, fy0, fx1, fy1,
- tx0, ty0, tx1, ty1)
-
- if dist < new_focus_dist:
- new_focus = f
- new_focus_dist = dist
-
- # If we couldn't find anything, try the placeless focus.
- new_focus = new_focus or placeless
-
- # If we have something, switch to it.
- if new_focus:
- return change_focus(new_focus)
-
- # And, we're done.
-
-
-
-def key_handler(ev):
-
- if renpy.display.behavior.map_event(ev, 'focus_right'):
- return focus_nearest(0.9, 0.1, 0.9, 0.9,
- 0.1, 0.1, 0.1, 0.9,
- verti_line_dist,
- lambda old, new : old.x + old.w <= new.x,
- -1, 0, 0, 0)
-
- if renpy.display.behavior.map_event(ev, 'focus_left'):
- return focus_nearest(0.1, 0.1, 0.1, 0.9,
- 0.9, 0.1, 0.9, 0.9,
- verti_line_dist,
- lambda old, new : new.x + new.w <= old.x,
- 1, 0, 1, 0)
-
- if renpy.display.behavior.map_event(ev, 'focus_up'):
- return focus_nearest(0.1, 0.1, 0.9, 0.1,
- 0.1, 0.9, 0.9, 0.9,
- horiz_line_dist,
- lambda old, new : new.y + new.h <= old.y,
- 0, 1, 0, 1)
-
- if renpy.display.behavior.map_event(ev, 'focus_down'):
- return focus_nearest(0.1, 0.9, 0.9, 0.9,
- 0.1, 0.1, 0.9, 0.1,
- horiz_line_dist,
- lambda old, new : old.y + old.h <= new.y,
- 0, -1, 0, 0)
-
-
-
diff --git a/unrpyc/renpy/display/im.py b/unrpyc/renpy/display/im.py
deleted file mode 100644
index d90fb51..0000000
--- a/unrpyc/renpy/display/im.py
+++ /dev/null
@@ -1,1562 +0,0 @@
-# Copyright 2004-2013 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.
-
-# This file contains the new image code, which includes provisions for
-# size-based caching and constructing images from operations (like
-# cropping and scaling).
-
-import renpy.display
-
-import math
-import zipfile
-import io
-import threading
-import time
-
-
-# This is an entry in the image cache.
-class CacheEntry(object):
-
- def __init__(self, what, surf):
-
- # The object that is being cached (which needs to be
- # hashable and comparable).
- self.what = what
-
- # The pygame surface corresponding to the cached object.
- self.surf = surf
-
- # The size of this image.
- w, h = surf.get_size()
- self.size = w * h
-
- # The time when this cache entry was last used.
- self.time = 0
-
-# This is the singleton image cache.
-class Cache(object):
-
- def __init__(self):
-
- # The current arbitrary time. (Increments by one for each
- # interaction.)
- self.time = 0
-
- # A map from Image object to CacheEntry.
- self.cache = { }
-
- # A list of Image objects that we want to preload.
- self.preloads = [ ]
-
- # False if this is not the first preload in this tick.
- self.first_preload_in_tick = True
-
- # The total size of the current generation of images.
- self.size_of_current_generation = 0
-
- # The total size of everything in the cache.
- self.total_cache_size = 0
-
- # A lock that must be held when updating the cache.
- self.lock = threading.Condition()
-
- # A lock that mist be held to notify the preload thread.
- self.preload_lock = threading.Condition()
-
- # Is the preload_thread alive?
- self.keep_preloading = True
-
- # A map from image object to surface, only for objects that have
- # been pinned into memory.
- self.pin_cache = { }
-
- # Images that we tried, and failed, to preload.
- self.preload_blacklist = set()
-
- # The size of the cache, in pixels.
- self.cache_limit = 0
-
- # The preload thread.
- self.preload_thread = threading.Thread(target=self.preload_thread_main, name="preloader")
- self.preload_thread.setDaemon(True)
- self.preload_thread.start()
-
- # Have we been added this tick?
- self.added = set()
-
- # A list of (time, filename, preload) tuples. This is updated when
- # config.developer is True and an image is loaded. Preload is a
- # flag that is true if the image was loaded from the preload
- # thread. The log is limited to 100 entries, and the newest entry
- # is first.
- #
- # This is only updated when config.developer is True.
- self.load_log = [ ]
-
-
- def init(self):
- """
- Updates the cache object to make use of settings that might be provided
- by the game-maker.
- """
-
- self.cache_limit = renpy.config.image_cache_size * renpy.config.screen_width * renpy.config.screen_height
-
- def quit(self): #@ReservedAssignment
- if not self.preload_thread.isAlive():
- return
-
- with self.preload_lock:
- self.keep_preloading = False
- self.preload_lock.notify()
-
- self.preload_thread.join()
-
-
- # Clears out the cache.
- def clear(self):
-
- self.lock.acquire()
-
- self.preloads = [ ]
- self.pin_cache = { }
- self.cache = { }
- self.first_preload_in_tick = True
- self.size_of_current_generation = 0
- self.total_cache_size = 0
-
- self.added.clear()
-
- self.lock.release()
-
- # Increments time, and clears the list of images to be
- # preloaded.
- def tick(self):
-
- with self.lock:
- self.time += 1
- self.preloads = [ ]
- self.first_preload_in_tick = True
- self.size_of_current_generation = 0
- self.added.clear()
-
- if renpy.config.debug_image_cache:
- renpy.display.ic_log.write("----")
- filename, line = renpy.exports.get_filename_line()
- renpy.display.ic_log.write("%s %d", filename, line)
-
- # The preload thread can deal with this update, so we don't need
- # to lock things.
- def end_tick(self):
- self.preloads = [ ]
-
-
- # This returns the pygame surface corresponding to the provided
- # image. It also takes care of updating the age of images in the
- # cache to be current, and maintaining the size of the current
- # generation of images.
- def get(self, image, predict=False):
-
- if not isinstance(image, ImageBase):
- raise Exception("Expected an image of some sort, but got" + str(image) + ".")
-
- if not image.cache:
- surf = image.load()
- renpy.display.render.mutated_surface(surf)
- return surf
-
- # First try to grab the image out of the cache without locking it.
- ce = self.cache.get(image, None)
-
- # Otherwise, we load the image ourselves.
- if ce is None:
-
- try:
- if image in self.pin_cache:
- surf = self.pin_cache[image]
- else:
- surf = image.load()
-
- except:
- raise
-
- with self.lock:
-
- ce = CacheEntry(image, surf)
-
- if image not in self.cache:
- self.total_cache_size += ce.size
-
- self.cache[image] = ce
-
- # Indicate that this surface had changed.
- renpy.display.render.mutated_surface(ce.surf)
-
- if renpy.config.debug_image_cache:
- if predict:
- renpy.display.ic_log.write("Added %r (%.02f%%)", ce.what, 100.0 * self.total_cache_size / self.cache_limit)
- else:
- renpy.display.ic_log.write("Total Miss %r", ce.what)
-
- renpy.display.draw.load_texture(ce.surf)
-
-
- # Move it into the current generation. This isn't protected by
- # a lock, so in certain circumstances we could have an
- # inaccurate size - but that will be cured at the end of the
- # current generation.
-
- if ce.time != self.time:
- ce.time = self.time
- self.size_of_current_generation += ce.size
-
- # Done... return the surface.
- return ce.surf
-
-
- # This kills off a given cache entry.
- def kill(self, ce):
-
- # Should never happen... but...
- if ce.time == self.time:
- self.size_of_current_generation -= ce.size
-
- self.total_cache_size -= ce.size
- del self.cache[ce.what]
-
- if renpy.config.debug_image_cache:
- renpy.display.ic_log.write("Removed %r", ce.what)
-
- def cleanout(self):
- """
- Cleans out the cache, if it's gotten too large. Returns True
- if the cache is smaller than the size limit, or False if it's
- bigger and we don't want to continue preloading.
- """
-
- # If we're within the limit, return.
- if self.total_cache_size <= self.cache_limit:
- return True
-
- # If we're outside the cache limit, we need to go and start
- # killing off some of the entries until we're back inside it.
-
- for ce in sorted(iter(self.cache.values()), key=lambda a : a.time):
-
- if ce.time == self.time:
- # If we're bigger than the limit, and there's nothing
- # to remove, we should stop the preloading right away.
- return False
-
- # Otherwise, kill off the given cache entry.
- self.kill(ce)
-
- # If we're in the limit, we're done.
- if self.total_cache_size <= self.cache_limit:
- break
-
- return True
-
-
- # Called to report that a given image would like to be preloaded.
- def preload_image(self, im):
-
- if not isinstance(im, ImageBase):
- return
-
- with self.lock:
-
- if im in self.added:
- return
-
- self.added.add(im)
-
- if im in self.cache:
- self.get(im)
- in_cache = True
- else:
- self.preloads.append(im)
- in_cache = False
-
- if not in_cache:
-
- with self.preload_lock:
- self.preload_lock.notify()
-
- if in_cache and renpy.config.debug_image_cache:
- renpy.display.ic_log.write("Kept %r", im)
-
-
- def start_prediction(self):
- """
- Called at the start of prediction, to ensure the thread runs
- at least once to clean out the cache.
- """
-
- with self.preload_lock:
- self.preload_lock.notify()
-
- def preload_thread_main(self):
-
- while self.keep_preloading:
-
- self.preload_lock.acquire()
- self.preload_lock.wait()
- self.preload_lock.release()
-
- while self.preloads and self.keep_preloading:
-
- start = time.time()
-
- # If the size of the current generation is bigger than the
- # total cache size, stop preloading.
- with self.lock:
-
- # If the cache is overfull, clean it out.
- if not self.cleanout():
-
- if renpy.config.debug_image_cache:
- for i in self.preloads:
- renpy.display.ic_log.write("Overfull %r", i)
-
- self.preloads = [ ]
-
- break
-
- try:
- image = self.preloads.pop(0)
-
- if image not in self.preload_blacklist:
- try:
- self.get(image, True)
- except:
- self.preload_blacklist.add(image)
- except:
- pass
-
- with self.lock:
- self.cleanout()
-
- # If we have time, preload pinned images.
- if self.keep_preloading and not renpy.game.less_memory:
-
- workset = set(renpy.store._cache_pin_set)
-
- # Remove things that are not in the workset from the pin cache,
- # and remove things that are in the workset from pin cache.
- for i in list(self.pin_cache.keys()):
-
- if i in workset:
- workset.remove(i)
- else:
- surf = self.pin_cache[i]
-
- del self.pin_cache[i]
-
-
- # For each image in the worklist...
- for image in workset:
-
- if image in self.preload_blacklist:
- continue
-
- # If we have normal preloads, break out.
- if self.preloads:
- break
-
- try:
- surf = image.load()
- self.pin_cache[image] = surf
- renpy.display.draw.load_texture(surf)
- except:
- self.preload_blacklist.add(image)
-
- def add_load_log(self, filename):
-
- if not renpy.config.developer:
- return
-
- preload = (threading.current_thread() is self.preload_thread)
-
- self.load_log.insert(0, (time.time(), filename, preload))
-
- while len(self.load_log) > 100:
- self.load_log.pop()
-
-
-
-# The cache object.
-cache = Cache()
-
-def free_memory():
- """
- Frees some memory.
- """
-
- renpy.display.draw.free_memory()
- cache.clear()
-
-
-class ImageBase(renpy.display.core.Displayable):
- """
- This is the base class for all of the various kinds of images that
- we can possibly have.
- """
-
- __version__ = 1
-
- def after_upgrade(self, version):
- if version < 1:
- self.cache = True
-
- def __init__(self, *args, **properties):
-
- self.rle = properties.pop('rle', None)
- self.cache = properties.pop('cache', True)
-
- properties.setdefault('style', 'image')
-
- super(ImageBase, self).__init__(**properties)
- self.identity = (type(self).__name__, ) + args
-
-
- def __hash__(self):
- return hash(self.identity)
-
- def __eq__(self, other):
-
- if not isinstance(other, ImageBase):
- return False
-
- return self.identity == other.identity
-
- def __repr__(self):
- return "<" + " ".join([repr(i) for i in self.identity]) + ">"
-
- def load(self):
- """
- This function is called by the image cache code to cause this
- image to be loaded. It's expected that children of this class
- would override this.
- """
-
- assert False
-
- def render(self, w, h, st, at):
-
- im = cache.get(self)
- texture = renpy.display.draw.load_texture(im)
-
- w, h = im.get_size()
- rv = renpy.display.render.Render(w, h)
- rv.blit(texture, (0, 0))
- return rv
-
- def predict_one(self):
- renpy.display.predict.image(self)
-
- def predict_files(self):
- """
- Returns a list of files that will be accessed when this image
- operation is performed.
- """
-
- return [ ]
-
-class Image(ImageBase):
- """
- This image manipulator loads an image from a file.
- """
-
- def __init__(self, filename, **properties):
- """
- @param filename: The filename that the image will be loaded from.
- """
-
- super(Image, self).__init__(filename, **properties)
- self.filename = filename
-
- def get_mtime(self):
- return renpy.loader.get_mtime(self.filename)
-
- def load(self, unscaled=False):
-
- cache.add_load_log(self.filename)
-
- try:
-
- if unscaled:
- surf = renpy.display.pgrender.load_image_unscaled(renpy.loader.load(self.filename), self.filename)
- else:
- surf = renpy.display.pgrender.load_image(renpy.loader.load(self.filename), self.filename)
-
- return surf
-
- except Exception as e:
-
- if renpy.config.missing_image_callback:
- im = renpy.config.missing_image_callback(self.filename)
- if im is None:
- raise e
-
- return im.load()
-
- raise
-
- def predict_files(self):
-
- if renpy.loader.loadable(self.filename):
- return [ self.filename ]
- else:
- if renpy.config.missing_image_callback:
- im = renpy.config.missing_image_callback(self.filename)
- if im is not None:
- return im.predict_files()
-
- return [ self.filename ]
-
-class ZipFileImage(ImageBase):
-
- def __init__(self, zipfilename, filename, mtime=0, **properties):
- super(ZipFileImage, self).__init__(zipfilename, filename, mtime, **properties)
-
- self.zipfilename = zipfilename
- self.filename = filename
-
- def load(self):
- try:
- zf = zipfile.ZipFile(self.zipfilename, 'r')
- data = zf.read(self.filename)
- sio = io.StringIO(data)
- rv = renpy.display.pgrender.load_image(sio, self.filename)
- zf.close()
- return rv
- except:
- return renpy.display.pgrender.surface((2, 2), True)
-
-
-
- def predict_files(self):
- return [ ]
-
-
-
-class Composite(ImageBase):
- """
- :doc: im_im
-
- This image manipulator composites multiple images together to
- form a single image.
-
- The `size` should be a (width, height) tuple giving the size
- of the composed image.
-
- The remaining positional arguments are interpreted as groups of
- two. The first argument in a group should be an (x, y) tuple,
- while the second should be an image manipulator. The image
- produced by the image manipulator is composited at the location
- given by the tuple.
-
- ::
-
- image girl clothed happy = im.Composite(
- (300, 600),
- (0, 0), "girl_body.png",
- (0, 0), "girl_clothes.png",
- (100, 100), "girl_happy.png"
- )
-
- """
-
- def __init__(self, size, *args, **properties):
-
- super(Composite, self).__init__(size, *args, **properties)
-
- if len(args) % 2 != 0:
- raise Exception("Composite requires an odd number of arguments.")
-
- self.size = size
- self.positions = args[0::2]
- self.images = [ image(i) for i in args[1::2] ]
-
- def get_mtime(self):
- return min(i.get_mtime() for i in self.images)
-
- def load(self):
-
- if self.size:
- size = self.size
- else:
- size = cache.get(self.images[0]).get_size()
-
- rv = renpy.display.pgrender.surface(size, True)
-
- for pos, im in zip(self.positions, self.images):
- rv.blit(cache.get(im), pos)
-
- return rv
-
- def predict_files(self):
-
- rv = [ ]
-
- for i in self.images:
- rv.extend(i.predict_files())
-
- return rv
-
-class Scale(ImageBase):
- """
- :doc: im_im
-
- An image manipulator that scales `im` (an image manipulator) to
- `width` and `height`.
-
- If `bilinear` is true, then bilinear interpolation is used for
- the scaling. Otherwise, nearest neighbor interpolation is used.
-
- ::
-
- image logo scale = im.Scale("logo.png", 100, 150)
- """
-
- def __init__(self, im, width, height, bilinear=True, **properties):
-
- im = image(im)
- super(Scale, self).__init__(im, width, height, bilinear, **properties)
-
- self.image = im
- self.width = int(width)
- self.height = int(height)
- self.bilinear = bilinear
-
- def get_mtime(self):
- return self.image.get_mtime()
-
- def load(self):
-
- child = cache.get(self.image)
-
- if self.bilinear:
- try:
- renpy.display.render.blit_lock.acquire()
- rv = renpy.display.scale.smoothscale(child, (self.width, self.height))
- finally:
- renpy.display.render.blit_lock.release()
- else:
- try:
- renpy.display.render.blit_lock.acquire()
- rv = renpy.display.pgrender.transform_scale(child, (self.width, self.height))
- finally:
- renpy.display.render.blit_lock.release()
-
- return rv
-
- def predict_files(self):
- return self.image.predict_files()
-
-class FactorScale(ImageBase):
- """
- :doc: im_im
-
- An image manipulator that scales `im` (a second image manipulator)
- to `width` times its original `width`, and `height` times its
- original height. If `height` is ommitted, it defaults to `width`.
-
- If `bilinear` is true, then bilinear interpolation is used for
- the scaling. Otherwise, nearest neighbor interpolation is used.
-
- ::
-
- image logo doubled = im.FactorScale("logo.png", 1.5)
- """
-
-
- def __init__(self, im, width, height=None, bilinear=True, **properties):
-
- if height is None:
- height = width
-
- im = image(im)
- super(FactorScale, self).__init__(im, width, height, bilinear, **properties)
-
- self.image = im
- self.width = width
- self.height = height
- self.bilinear = bilinear
-
- def get_mtime(self):
- return self.image.get_mtime()
-
- def load(self):
-
- surf = cache.get(self.image)
- width, height = surf.get_size()
-
- width = int(width * self.width)
- height = int(height * self.height)
-
- if self.bilinear:
- try:
- renpy.display.render.blit_lock.acquire()
- rv = renpy.display.scale.smoothscale(surf, (width, height))
- finally:
- renpy.display.render.blit_lock.release()
-
- else:
- try:
- renpy.display.render.blit_lock.acquire()
- rv = renpy.display.pgrender.transform_scale(surf, (width, height))
- finally:
- renpy.display.render.blit_lock.release()
-
- return rv
-
- def predict_files(self):
- return self.image.predict_files()
-
-
-class Flip(ImageBase):
- """
- :doc: im_im
-
- An image manipulator that flips `im` (an image manipulator)
- vertically or horizontally. `vertical` and `horizontal` control
- the directions in which the image is flipped.
-
- ::
-
- image eileen flip = im.Flip("eileen_happy.png", vertical=True)
- """
-
- def __init__(self, im, horizontal=False, vertical=False, **properties):
-
- if not (horizontal or vertical):
- raise Exception("im.Flip must be called with a true value for horizontal or vertical.")
-
- im = image(im)
- super(Flip, self).__init__(im, horizontal, vertical, **properties)
-
- self.image = im
- self.horizontal = horizontal
- self.vertical = vertical
-
-
- def get_mtime(self):
- return self.image.get_mtime()
-
- def load(self):
-
- child = cache.get(self.image)
-
- try:
- renpy.display.render.blit_lock.acquire()
- rv = renpy.display.pgrender.flip(child, self.horizontal, self.vertical)
- finally:
- renpy.display.render.blit_lock.release()
-
- return rv
-
-
- def predict_files(self):
- return self.image.predict_files()
-
-
-
-class Rotozoom(ImageBase):
- """
- This is an image manipulator that is a smooth rotation and zoom of another image manipulator.
- """
-
- def __init__(self, im, angle, zoom, **properties):
- """
- @param im: The image to be rotozoomed.
-
- @param angle: The number of degrees counterclockwise the image is
- to be rotated.
-
- @param zoom: The zoom factor. Numbers that are greater than 1.0
- lead to the image becoming larger.
- """
-
- im = image(im)
- super(Rotozoom, self).__init__(im, angle, zoom, **properties)
-
- self.image = im
- self.angle = angle
- self.zoom = zoom
-
- def get_mtime(self):
- return self.image.get_mtime()
-
- def load(self):
-
- child = cache.get(self.image)
-
- try:
- renpy.display.render.blit_lock.acquire()
- rv = renpy.display.pgrender.rotozoom(child, self.angle, self.zoom)
- finally:
- renpy.display.render.blit_lock.release()
-
- return rv
-
- def predict_files(self):
- return self.image.predict_files()
-
-
-
-class Crop(ImageBase):
- """
- :doc: im_im
- :args: (im, rect)
-
- An image manipulator that crops `rect`, a (x, y, width, height) tuple,
- out of `im`, an image manipulator.
-
- ::
-
- image logo crop = im.Crop("logo.png", (0, 0, 100, 307))
- """
-
- def __init__(self, im, x, y=None, w=None, h=None, **properties):
-
- im = image(im)
-
- if y is None:
- (x, y, w, h) = x
-
- super(Crop, self).__init__(im, x, y, w, h, **properties)
-
- self.image = im
- self.x = x
- self.y = y
- self.w = w
- self.h = h
-
- def get_mtime(self):
- return self.image.get_mtime()
-
- def load(self):
- return cache.get(self.image).subsurface((self.x, self.y,
- self.w, self.h))
-
- def predict_files(self):
- return self.image.predict_files()
-
-
-ramp_cache = { }
-
-
-def ramp(start, end):
- """
- Returns a 256 character linear ramp, where the first character has
- the value start and the last character has the value end. Such a
- ramp can be used as a map argument of im.Map.
- """
-
- rv = ramp_cache.get((start, end), None)
- if rv is None:
-
- chars = [ ]
-
- for i in range(0, 256):
- i = i / 255.0
- chars.append(chr(int( end * i + start * (1.0 - i) ) ) )
-
- rv = "".join(chars)
- ramp_cache[start, end] = rv
-
- return rv
-
-identity = ramp(0, 255)
-
-class Map(ImageBase):
- """
- This adjusts the colors of the image that is its child. It takes
- as arguments 4 256 character strings. If a pixel channel has a
- value of 192, then the value of the 192nd character in the string
- is used for the mapped pixel component.
- """
-
- def __init__(self, im, rmap=identity, gmap=identity, bmap=identity,
- amap=identity, force_alpha=False, **properties):
-
- im = image(im)
-
- super(Map, self).__init__(im, rmap, gmap, bmap, amap, force_alpha, **properties)
-
- self.image = im
- self.rmap = rmap
- self.gmap = gmap
- self.bmap = bmap
- self.amap = amap
-
- self.force_alpha = force_alpha
-
- def get_mtime(self):
- return self.image.get_mtime()
-
- def load(self):
-
- surf = cache.get(self.image)
-
- rv = renpy.display.pgrender.surface(surf.get_size(), True)
-
- renpy.display.module.map(surf, rv,
- self.rmap, self.gmap, self.bmap, self.amap)
-
- return rv
-
- def predict_files(self):
- return self.image.predict_files()
-
-class Twocolor(ImageBase):
- """
- This takes as arguments two colors, white and black. The image is
- mapped such that pixels in white have the white color, pixels in
- black have the black color, and shades of gray are linearly
- interpolated inbetween. The alpha channel is mapped linearly
- between 0 and the alpha found in the white color, the black
- color's alpha is ignored.
- """
-
- def __init__(self, im, white, black, force_alpha=False, **properties):
-
- white = renpy.easy.color(white)
- black = renpy.easy.color(black)
-
- im = image(im)
-
- super(Twocolor, self).__init__(im, white, black, force_alpha, **properties)
-
- self.image = im
- self.white = white
- self.black = black
-
- self.force_alpha = force_alpha
-
- def get_mtime(self):
- return self.image.get_mtime()
-
- def load(self):
-
- surf = cache.get(self.image)
-
- rv = renpy.display.pgrender.surface(surf.get_size(), True)
-
- renpy.display.module.twomap(surf, rv,
- self.white, self.black)
-
- return rv
-
- def predict_files(self):
- return self.image.predict_files()
-
-
-class Recolor(ImageBase):
- """
- This adjusts the colors of the image that is its child. It takes as an
- argument 4 numbers between 0 and 255, and maps each channel of the image
- linearly between 0 and the supplied color.
- """
-
- def __init__(self, im, rmul=255, gmul=255, bmul=255,
- amul=255, force_alpha=False, **properties):
-
- im = image(im)
-
- super(Recolor, self).__init__(im, rmul, gmul, bmul, amul, force_alpha, **properties)
-
- self.image = im
- self.rmul = rmul + 1
- self.gmul = gmul + 1
- self.bmul = bmul + 1
- self.amul = amul + 1
-
- self.force_alpha = force_alpha
-
- def get_mtime(self):
- return self.image.get_mtime()
-
- def load(self):
-
- surf = cache.get(self.image)
-
- rv = renpy.display.pgrender.surface(surf.get_size(), True)
-
- renpy.display.module.linmap(surf, rv,
- self.rmul, self.gmul, self.bmul, self.amul)
-
- return rv
-
- def predict_files(self):
- return self.image.predict_files()
-
-class MatrixColor(ImageBase):
- """
- :doc: im_matrixcolor
-
- An image operator that uses `matrix` to linearly transform the
- image manipulator `im`.
-
- `Matrix` should be a list, tuple, or :func:`im.matrix` that is 20
- or 25 elements long. If the object has 25 elements, then elements
- past the 20th are ignored.
-
- When the four components of the source color are R, G, B, and A,
- which range from 0.0 to 1.0; the four components of the transformed
- color are R', G', B', and A', with the same range; and the elements
- of the matrix are named::
-
- [ a, b, c, d, e,
- f, g, h, i, j,
- k, l, m, n, o,
- p, q, r, s, t ]
-
- the transformed colors can be computed with the formula::
-
- R' = (a * R) + (b * G) + (c * B) + (d * A) + e
- G' = (f * R) + (g * G) + (h * B) + (i * A) + j
- B' = (k * R) + (l * G) + (m * B) + (n * A) + o
- A' = (p * R) + (q * G) + (r * B) + (s * A) + t
-
- The components of the transformed color are clamped to the
- range [0.0, 1.0].
- """
-
- def __init__(self, im, matrix, **properties):
-
- im = image(im)
-
- if len(matrix) != 20 and len(matrix) != 25:
- raise Exception("ColorMatrix expects a 20 or 25 element matrix, got %d elements." % len(matrix))
-
- matrix = tuple(matrix)
- super(MatrixColor, self).__init__(im, matrix, **properties)
-
- self.image = im
- self.matrix = matrix
-
- def get_mtime(self):
- return self.image.get_mtime()
-
- def load(self):
-
- surf = cache.get(self.image)
-
- rv = renpy.display.pgrender.surface(surf.get_size(), True)
-
- renpy.display.module.colormatrix(surf, rv, self.matrix)
-
- return rv
-
- def predict_files(self):
- return self.image.predict_files()
-
-class matrix(tuple):
- """
- :doc: im_matrixcolor
-
- Constructs an im.matrix object from `matrix`. im.matrix objects
- support The operations supported are matrix multiplication, scalar
- multiplication, element-wise addition, and element-wise
- subtraction. These operations are invoked using the standard
- mathematical operators (\\*, \\*, +, and -, respectively). If two
- im.matrix objects are multiplied, matrix multiplication is
- performed, otherwise scalar multiplication is used.
-
- `matrix` is a 20 or 25 element list or tuple. If it is 20 elements
- long, it is padded with (0, 0, 0, 0, 1) to make a 5x5 matrix,
- suitable for multiplication.
- """
-
- def __new__(cls, *args):
-
- if len(args) == 1:
- args = tuple(args[0])
-
- if len(args) == 20:
- args = args + (0, 0, 0, 0, 1)
-
- if len(args) != 25:
- raise Exception("Matrix expects to be given 20 or 25 entries, not %d." % len(args))
-
- return tuple.__new__(cls, args)
-
- def mul(self, a, b):
-
- if not isinstance(a, matrix):
- a = matrix(a)
-
- if not isinstance(b, matrix):
- b = matrix(b)
-
- result = [ 0 ] * 25
- for y in range(0, 5):
- for x in range(0, 5):
- for i in range(0, 5):
- result[x + y * 5] += a[x + i * 5] * b[i + y * 5]
-
- return matrix(result)
-
- def scalar_mul(self, other):
- other = float(other)
- return matrix([ i * other for i in self ])
-
- def vector_mul(self, o):
-
- return (o[0]*self[0] + o[1]*self[1] + o[2]*self[2] + o[3]*self[3] + self[4],
- o[0]*self[5] + o[1]*self[6] + o[2]*self[7] + o[3]*self[8] + self[9],
- o[0]*self[10] + o[1]*self[11] + o[2]*self[12] + o[3]*self[13] + self[14],
- o[0]*self[15] + o[1]*self[16] + o[2]*self[17] + o[3]*self[18] + self[19],
- 1)
-
-
- def __add__(self, other):
- if isinstance(other, (int, float)):
- other = float(other)
- return matrix([ i + other for i in self ])
-
- other = matrix(other)
- return matrix([ i + j for i, j in zip(self, other)])
-
- __radd__ = __add__
-
- def __sub__(self, other):
- return self + other * -1
-
- def __rsub__(self, other):
- return self * -1 + other
-
- def __mul__(self, other):
- if isinstance(other, (int, float)):
- return self.scalar_mul(other)
-
- return self.mul(self, other)
-
- def __rmul__(self, other):
- if isinstance(other, (int, float)):
- return self.scalar_mul(other)
-
- return self.mul(other, self)
-
- def __repr__(self):
- return """\
-im.matrix(%f, %f, %f, %f, %f.
- %f, %f, %f, %f, %f,
- %f, %f, %f, %f, %f,
- %f, %f, %f, %f, %f,
- %f, %f, %f, %f, %f)""" % self
-
-
- @staticmethod
- def identity():
- """
- :doc: im_matrixcolor
- :name: im.matrix.identity
-
- Returns an identity matrix, one that does not change color or
- alpha.
- """
-
- return matrix(1, 0, 0, 0, 0,
- 0, 1, 0, 0, 0,
- 0, 0, 1, 0, 0,
- 0, 0, 0, 1, 0)
- @staticmethod
- def saturation(level, desat=(0.2126, 0.7152, 0.0722)):
- """
- :doc: im_matrixcolor
- :name: im.matrix.saturation
-
- Returns an im.matrix that alters the saturation of an
- image. The alpha channel is untouched.
-
- `level`
- The amount of saturation in the resulting image. 1.0 is
- the unaltered image, while 0.0 is grayscale.
-
- `desat`
- This is a 3-element tuple that controls how much of the
- red, green, and blue channels will be placed into all
- three channels of a fully desaturated image. The default
- is based on the constants used for the luminance channel
- of an NTSC television signal. Since the human eye is
- mostly sensitive to green, more of the green channel is
- kept then the other two channels.
- """
-
- r, g, b = desat
-
- def I(a, b):
- return a + (b - a) * level
-
- return matrix(I(r, 1), I(g, 0), I(b, 0), 0, 0,
- I(r, 0), I(g, 1), I(b, 0), 0, 0,
- I(r, 0), I(g, 0), I(b, 1), 0, 0,
- 0, 0, 0, 1, 0)
-
- @staticmethod
- def desaturate():
- """
- :doc: im_matrixcolor
- :name: im.matrix.desaturate
-
- Returns an im.matrix that desaturates the image (makes it
- grayscale). This is equivalent to calling
- im.matrix.saturation(0).
- """
-
- return matrix.saturation(0.0)
-
- @staticmethod
- def tint(r, g, b):
- """
- :doc: im_matrixcolor
- :name: im.matrix.tint
-
- Returns an im.matrix that tints an image, without changing
- the alpha channel. `r`, `g`, and `b` should be numbers between
- 0 and 1, and control what fraction of the given channel is
- placed into the final image. (For example, if `r` is .5, and
- the value of the red channel is 100, the transformed color
- will have a red value of 50.)
- """
-
- return matrix(r, 0, 0, 0, 0,
- 0, g, 0, 0, 0,
- 0, 0, b, 0, 0,
- 0, 0, 0, 1, 0)
-
- @staticmethod
- def invert():
- """
- :doc: im_matrixcolor
- :name: im.matrix.invert
-
- Returns an im.matrix that inverts the red, green, and blue
- channels of the image without changing the alpha channel.
- """
-
- return matrix(-1, 0, 0, 0, 1,
- 0, -1, 0, 0, 1,
- 0, 0, -1, 0, 1,
- 0, 0, 0, 1, 0)
-
- @staticmethod
- def brightness(b):
- """
- :doc: im_matrixcolor
- :name: im.matrix.brightness
-
- Returns an im.matrix that alters the brightness of an image.
-
- `b`
- The amount of change in image brightness. This should be
- a number between -1 and 1, with -1 the darkest possible
- image and 1 the brightest.
- """
-
- return matrix(1, 0, 0, 0, b,
- 0, 1, 0, 0, b,
- 0, 0, 1, 0, b,
- 0, 0, 0, 1, 0)
-
- @staticmethod
- def opacity(o):
- """
- :doc: im_matrixcolor
- :name: im.matrix.opacity
-
- Returns an im.matrix that alters the opacity of an image. An
- `o` of 0.0 is fully transparent, while 1.0 is fully opaque.
- """
-
- return matrix(1, 0, 0, 0, 0,
- 0, 1, 0, 0, 0,
- 0, 0, 1, 0, 0,
- 0, 0, 0, o, 0)
-
- @staticmethod
- def contrast(c):
- """
- :doc: im_matrixcolor
- :name: im.matrix.contrast
-
- Returns an im.matrix that alters the contrast of an image. `c` should
- be greater than 0.0, with values between 0.0 and 1.0 decreasing contrast, and
- values greater than 1.0 increasing contrast.
- """
-
- return matrix.brightness(-.5) * matrix.tint(c, c, c) * matrix.brightness(.5)
-
- # from http://www.gskinner.com/blog/archives/2005/09/flash_8_source.html
- @staticmethod
- def hue(h):
- """
- :doc: im_matrixcolor
- :name: im.matrix.hue
-
- Returns an im.matrix that rotates the hue by `h` degrees, while
- preserving luminosity.
- """
-
- h = h * math.pi / 180
- cosVal = math.cos(h)
- sinVal = math.sin(h)
- lumR = 0.213
- lumG = 0.715
- lumB = 0.072
- return matrix(
- lumR+cosVal*(1-lumR)+sinVal*(-lumR),lumG+cosVal*(-lumG)+sinVal*(-lumG),lumB+cosVal*(-lumB)+sinVal*(1-lumB),0,0,
- lumR+cosVal*(-lumR)+sinVal*(0.143),lumG+cosVal*(1-lumG)+sinVal*(0.140),lumB+cosVal*(-lumB)+sinVal*(-0.283),0,0,
- lumR+cosVal*(-lumR)+sinVal*(-(1-lumR)),lumG+cosVal*(-lumG)+sinVal*(lumG),lumB+cosVal*(1-lumB)+sinVal*(lumB),0,0,
- 0,0,0,1,0,
- 0,0,0,0,1
- )
-
- @staticmethod
- def colorize(black_color, white_color):
- """
- :doc: im_matrixcolor
- :name: im.matrix.colorize
-
- Returns an im.matrix that colorizes a black and white image.
- `black_color` and `white_color` are Ren'Py style colors, so
- they may be specfied as strings or tuples of (0-255) color
- values. ::
-
- # This makes black colors red, and white colors blue.
- image logo colored = im.MatrixColor(
- "bwlogo.png",
- im.matrix.colorize("#f00", "#00f"))
-
- """
-
- (r0, g0, b0, _a0) = renpy.easy.color(black_color)
- (r1, g1, b1, _a1) = renpy.easy.color(white_color)
-
- r0 /= 255.0
- g0 /= 255.0
- b0 /= 255.0
- r1 /= 255.0
- g1 /= 255.0
- b1 /= 255.0
-
- return matrix((r1-r0), 0, 0, 0, r0,
- 0, (g1-g0), 0, 0, g0,
- 0, 0, (b1-b0), 0, b0,
- 0, 0, 0, 1, 0)
-
-
-
-def Grayscale(im, desat=(0.2126, 0.7152, 0.0722), **properties):
- """
- :doc: im_im
- :args: (im, **properties)
-
- An image manipulator that creats a desaturated version of the image
- manipulator `im`.
- """
-
- return MatrixColor(im, matrix.saturation(0.0, desat), **properties)
-
-
-def Sepia(im, tint=(1.0, .94, .76), desat=(0.2126, 0.7152, 0.0722), **properties):
- """
- :doc: im_im
- :args: (im, **properties)
-
- An image manipulator that creates a sepia-toned version of the image
- manipulator `im`.
- """
-
- return MatrixColor(im, matrix.saturation(0.0, desat) * matrix.tint(tint[0], tint[1], tint[2]), **properties)
-
-
-def Color(im, color):
- """
- This recolors the supplied image, mapping colors such that black is
- black and white is the supplied color.
- """
-
- r, g, b, a = renpy.easy.color(color)
-
- return Recolor(im, r, g, b, a)
-
-
-def Alpha(image, alpha, **properties):
- """
- Returns an alpha-mapped version of the image. Alpha is the maximum
- alpha that this image can have, a number between 0.0 (fully
- transparent) and 1.0 (opaque).
-
- If an image already has an alpha channel, values in that alpha
- channel are reduced as appropriate.
- """
-
- return Recolor(image, 255, 255, 255, int(255 * alpha), force_alpha=True, **properties)
-
-class Tile(ImageBase):
- """
- :doc: im_im
-
- An image manipulator that tiles the image manipulator `im`, until
- it is `size`.
-
- `size`
- If not None, a (width, height) tuple. If None, this defaults to
- (:var:`config.screen_width`, :var:`config.screen_height`).
- """
-
- def __init__(self, im, size=None, **properties):
-
- im = image(im)
-
- super(Tile, self).__init__(im, size, **properties)
- self.image = im
- self.size = size
-
- def get_mtime(self):
- return self.image.get_mtime()
-
- def load(self):
-
- size = self.size
-
- if size is None:
- size = (renpy.config.screen_width, renpy.config.screen_height)
-
- surf = cache.get(self.image)
-
- rv = renpy.display.pgrender.surface(size, True)
-
- width, height = size
- sw, sh = surf.get_size()
-
- for y in range(0, height, sh):
- for x in range(0, width, sw):
- rv.blit(surf, (x, y))
-
- return rv
-
- def predict_files(self):
- return self.image.predict_files()
-
-class AlphaMask(ImageBase):
- """
- :doc: im_im
-
- An image manipulator that takes two image manipulators, `base` and
- `mask`, as arguments. It replaces the alpha channel of `base` with
- the red channel of `mask`.
-
- This is used to provide an image's alpha channel in a second
- image, like having one jpeg for color data, and a second one
- for alpha. In some cases, two jpegs can be smaller than a
- single png file.
- """
-
- def __init__(self, base, mask, **properties):
- super(AlphaMask, self).__init__(base, mask, **properties)
-
- self.base = image(base)
- self.mask = image(mask)
-
- def get_mtime(self):
- return max(self.base.get_mtime(), self.image.get_mtime())
-
- def load(self):
-
- basesurf = cache.get(self.base)
- masksurf = cache.get(self.mask)
-
- if basesurf.get_size() != masksurf.get_size():
- raise Exception("AlphaMask surfaces must be the same size.")
-
- # Used to copy the surface.
- rv = renpy.display.pgrender.copy_surface(basesurf)
- renpy.display.module.alpha_munge(masksurf, rv, identity)
-
- return rv
-
- def predict_files(self):
- return self.base.predict_files() + self.mask.predict_files()
-
-def image(arg, loose=False, **properties):
- """
- :doc: im_image
- :name: Image
- :args: (filename, **properties)
-
- Loads an image from a file. `filename` is a
- string giving the name of the file.
-
- `filename` should be a JPEG or PNG file with an appropriate
- extension.
- """
-
- """
- (Actually, the user documentation is a bit misleading, as
- this tries for compatibility with several older forms of
- image specification.)
-
- If the loose argument is False, then this will report an error if an
- arbitrary argument is given. If it's True, then the argument is passed
- through unchanged.
- """
-
- if isinstance(arg, ImageBase):
- return arg
-
- elif isinstance(arg, str):
- return Image(arg, **properties)
-
- elif isinstance(arg, renpy.display.image.ImageReference):
- arg.find_target()
- return image(arg.target, loose=loose, **properties)
-
- elif isinstance(arg, tuple):
- params = [ ]
-
- for i in arg:
- params.append((0, 0))
- params.append(i)
-
- return Composite(None, *params)
-
- elif loose:
- return arg
-
- if isinstance(arg, renpy.display.core.Displayable):
- raise Exception("Expected an image, but got a general displayable.")
- else:
- raise Exception("Could not construct image from argument.")
-
-
-def load_image(fn):
- """
- This loads an image from the given filename, using the cache.
- """
-
- surf = cache.get(image(fn))
- return renpy.display.draw.load_texture(surf)
diff --git a/unrpyc/renpy/display/image.py b/unrpyc/renpy/display/image.py
deleted file mode 100644
index 362f87a..0000000
--- a/unrpyc/renpy/display/image.py
+++ /dev/null
@@ -1,397 +0,0 @@
-# Copyright 2004-2013 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.
-
-# This file contains some miscellaneous displayables that involve images.
-# Most of the guts of this file have been moved into im.py, with only some
-# of the stuff thar uses images remaining.
-
-import renpy.display
-import renpy.text
-from renpy.display.render import render, Render
-
-import collections
-
-# A map from image name to the displayable object corresponding to that
-# image.
-images = { }
-
-# A map from image tag to lists of possible attributes for images with that
-# tag.
-image_attributes = collections.defaultdict(list)
-
-def register_image(name, d):
- """
- Registers the existence of an image with `name`, and that the image
- used displayable d.
- """
-
- tag = name[0]
- rest = name[1:]
-
- images[name] = d
- image_attributes[tag].append(rest)
-
-
-def wrap_render(child, w, h, st, at):
- rend = render(child, w, h, st, at)
- rv = Render(rend.width, rend.height)
- rv.blit(rend, (0, 0))
- return rv
-
-class ImageReference(renpy.display.core.Displayable):
- """
- ImageReference objects are used to reference images by their name,
- which is a tuple of strings corresponding to the name used to define
- the image in an image statment.
- """
-
- nosave = [ 'target' ]
- target = None
- param_target = None
-
- def __init__(self, name, **properties):
- """
- @param name: A tuple of strings, the name of the image. Or else
- a displayable, containing the image directly.
- """
-
- super(ImageReference, self).__init__(**properties)
-
- self.name = name
-
- def _get_parameterized(self):
- if self.param_target:
- return self.param_target._get_parameterized()
-
- return self
-
- def find_target(self):
-
- if self.param_target:
- self.target = self.param_target
- return None
-
- name = self.name
-
- if isinstance(name, renpy.display.core.Displayable):
- self.target = name
- return True
-
- if not isinstance(name, tuple):
- name = tuple(name.split())
-
- parameters = [ ]
-
- def error(msg):
- self.target = renpy.text.text.Text(msg, color=(255, 0, 0, 255), xanchor=0, xpos=0, yanchor=0, ypos=0)
-
- if renpy.config.debug:
- raise Exception(msg)
-
-
- # Scan through, searching for an image (defined with an
- # input statement) that is a prefix of the given name.
- while name:
- if name in images:
- target = images[name]
-
- try:
- self.target = target.parameterize(name, parameters)
- if self.target is not target:
- self.param_target = self.target
-
- except Exception as e:
- if renpy.config.debug:
- raise
-
- error(str(e))
-
- return True
-
- else:
- parameters.insert(0, name[-1])
- name = name[:-1]
-
- error("Image '%s' not found." % ' '.join(self.name))
- return False
-
- def parameterize(self, name, parameters):
- if not self.target:
- self.find_target()
-
- return self.target.parameterize(name, parameters)
-
- def _hide(self, st, at, kind):
- if not self.target:
- self.find_target()
-
- return self.target._hide(st, at, kind)
-
- def set_transform_event(self, event):
- if not self.target:
- self.find_target()
-
- return self.target.set_transform_event(event)
-
- def event(self, ev, x, y, st):
- if not self.target:
- self.find_target()
-
- return self.target.event(ev, x, y, st)
-
- def render(self, width, height, st, at):
- if not self.target:
- self.find_target()
-
- return wrap_render(self.target, width, height, st, at)
-
- def get_placement(self):
- if not self.target:
- self.find_target()
-
- if not renpy.config.imagereference_respects_position:
- return self.target.get_placement()
-
- xpos, ypos, xanchor, yanchor, xoffset, yoffset, subpixel = self.target.get_placement()
-
- if xpos is None:
- xpos = self.style.xpos
-
- if ypos is None:
- ypos = self.style.ypos
-
- if xanchor is None:
- xanchor = self.style.xanchor
-
- if yanchor is None:
- yanchor = self.style.yanchor
-
- return xpos, ypos, xanchor, yanchor, xoffset, yoffset, subpixel
-
- def visit(self):
- if not self.target:
- self.find_target()
-
- return [ self.target ]
-
-
-
-class ShownImageInfo(renpy.object.Object):
- """
- This class keeps track of which images are being shown right now,
- and what the attributes of those images are. (It's used for a similar
- purpose during prediction, regarding the state in the future.)
- """
-
- __version__ = 2
-
- def __init__(self, old=None):
- """
- Creates a new object. If `old` is given, copies the default state
- from old, otherwise initializes the object to a default state.
- """
-
- if old is None:
-
- # A map from (layer, tag) -> tuple of attributes
- # This doesn't necessarily correspond to something that is
- # currently showing, as we can remember the state of a tag
- # for use in SideImage.
- self.attributes = { }
-
- # A set of (layer, tag) pairs that are being shown on the
- # screen right now.
- self.shown = set()
-
- else:
- self.attributes = old.attributes.copy()
- self.shown = old.shown.copy()
-
-
- def after_upgrade(self, version):
- if version < 2:
-
- self.attributes = { }
- self.shown = set()
-
- for layer in self.images:
- for tag in self.images[layer]:
- self.attributes[layer, tag] = self.images[layer][tag][1:]
- self.shown.add((layer, tag))
-
- def get_attributes(self, layer, tag):
- """
- Get the attributes associated the image with tag on the given
- layer.
- """
-
- return self.attributes.get((layer, tag), ())
-
- def showing(self, layer, name):
- """
- Returns true if name is the prefix of an image that is showing
- on layer, or false otherwise.
- """
-
- tag = name[0]
- rest = name[1:]
-
- if (layer, tag) not in self.shown:
- return None
-
- shown = self.attributes[layer, tag]
-
- if len(shown) < len(rest):
- return False
-
- for a, b in zip(shown, rest):
- if a != b:
- return False
-
- return True
-
- def predict_scene(self, layer):
- """
- Predicts the scene statement being called on layer.
- """
-
- for l, t in list(self.attributes.keys()):
- if l == layer:
- del self.attributes[l, t]
-
- self.shown = set((l, t) for l, t in self.shown if l != layer)
-
- def predict_show(self, layer, name, show=True):
- """
- Predicts name being shown on layer.
-
- `show`
- If True, the image will be flagged as being shown to the user. If
- False, only the attributes will be updated.
- """
-
- tag = name[0]
- rest = name[1:]
-
- self.attributes[layer, tag] = rest
-
- if show:
- self.shown.add((layer, tag))
-
- def predict_hide(self, layer, name):
- tag = name[0]
-
- if (layer, tag) in self.attributes:
- del self.attributes[layer, tag]
-
- self.shown.discard((layer, tag))
-
-
- def apply_attributes(self, layer, tag, name):
- """
- Given a layer, tag, and an image name (with attributes),
- returns the canonical name of an image, if one exists. Raises
- an exception if it's ambiguious, and returns None if an image
- with that name couldn't be found.
- """
-
- # If the name matches one that exactly exists, return it.
- if name in images:
- return name
-
- nametag = name[0]
-
- # The set of attributes a matching image must have.
- required = set(name[1:])
-
- # The set of attributes a matching image may have.
- optional = set(self.attributes.get((layer, tag), [ ]))
-
- # Deal with banned attributes..
- for i in name[1:]:
- if i[0] == "-":
- optional.discard(i[1:])
- required.discard(i)
-
- return self.choose_image(nametag, required, optional, name)
-
- def choose_image(self, tag, required, optional, exception_name):
- """
- """
-
- # The longest length of an image that matches.
- max_len = 0
-
- # The list of matching images.
- matches = None
-
- for attrs in image_attributes[tag]:
-
- num_required = 0
-
- for i in attrs:
- if i in required:
- num_required += 1
- continue
-
- elif i not in optional:
- break
-
- else:
-
- # We don't have any not-found attributes. But we might not
- # have all of the attributes.
-
- if num_required != len(required):
- continue
-
- len_attrs = len(attrs)
-
- if len_attrs < max_len:
- continue
-
- if len_attrs > max_len:
- max_len = len_attrs
- matches = [ ]
-
- matches.append((tag, ) + attrs)
-
- if matches is None:
- return None
-
- if len(matches) == 1:
- return matches[0]
-
- if exception_name:
- raise Exception("Showing '" + " ".join(exception_name) + "' is ambiguous, possible images include: " + ", ".join(" ".join(i) for i in matches))
- else:
- return None
-
-renpy.display.core.ImagePredictInfo = ShownImageInfo
-
-
-# Functions that have moved from this module to other modules,
-# that live here for the purpose of backward-compatibility.
-Image = renpy.display.im.image
-Solid = renpy.display.imagelike.Solid
-Frame = renpy.display.imagelike.Frame
-ImageButton = renpy.display.behavior.ImageButton
-
diff --git a/unrpyc/renpy/display/imagelike.py b/unrpyc/renpy/display/imagelike.py
deleted file mode 100644
index aea1d15..0000000
--- a/unrpyc/renpy/display/imagelike.py
+++ /dev/null
@@ -1,382 +0,0 @@
-# Copyright 2004-2013 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 renpy.display
-from renpy.display.render import render, Render, Matrix2D
-
-# This file contains displayables that are image-like, because they take
-# up a rectangular area of the screen, and do not respond to input.
-
-class Solid(renpy.display.core.Displayable):
- """
- :doc: disp_imagelike
-
- A displayable that fills the area its assigned with `color`.
-
- ::
-
- image white = Solid("#fff")
-
- """
-
- def __init__(self, color, **properties):
-
- super(Solid, self).__init__(**properties)
-
- if color is not None:
- self.color = renpy.easy.color(color)
- else:
- self.color = None
-
- def visit(self):
- return [ ]
-
- def render(self, width, height, st, at):
-
- color = self.color or self.style.color
-
- rv = Render(width, height)
-
- if color is None or width <= 0 or height <= 0:
- return rv
-
- SIZE = 10
-
- if width < SIZE or height < SIZE:
- tex = renpy.display.draw.solid_texture(width, height, color)
- else:
- tex = renpy.display.draw.solid_texture(SIZE, SIZE, color)
- rv.forward = Matrix2D(1.0 * SIZE / width, 0, 0, 1.0 * SIZE / height)
- rv.reverse = Matrix2D(1.0 * width / SIZE, 0, 0, 1.0 * height / SIZE)
-
- rv.blit(tex, (0, 0))
-
- return rv
-
-class Frame(renpy.display.core.Displayable):
- """
- :doc: disp_imagelike
- :args: (image, xborder, yborder, tile=False, **properties)
-
- A displayable that resizes an image to fill the available area,
- while preserving the width and height of its borders. is often
- used as the background of a window or button.
-
- .. figure:: frame_example.png
-
- Using a frame to resize an image to double its size.
-
- `image`
- An image manipulator that will be resized by this frame.
-
- `left`
- The size of the border on the left side.
-
- `top`
- The size of the border on the top.
-
- `right`
- The size of the border on the right side. If None, defaults
- to `left`.
-
- `bottom`
- The side of the border on the bottom. If None, defaults to `top`.
-
- `tile`
- If true, tiling is used to resize sections of the image,
- rather than scaling.
-
- ::
-
- # Resize the background of the text window if it's too small.
- init python:
- style.window.background = Frame("frame.png", 10, 10)
- """
-
- __version__ = 1
-
- def after_upgrade(self, version):
- if version < 2:
- self.left = self.xborder
- self.right = self.xborder
- self.top = self.yborder
- self.bottom = self.yborder
-
- def __init__(self, image, left, top, right=None, bottom=None, bilinear=True, tile=False, **properties):
- super(Frame, self).__init__(**properties)
-
- self.image = renpy.easy.displayable(image)
- self.tile = tile
-
- if right is None:
- right = left
- if bottom is None:
- bottom = top
-
- self.left = left
- self.top = top
- self.right = right
- self.bottom = bottom
-
- def render(self, width, height, st, at):
-
- crend = render(self.image, width, height, st, at)
-
- sw, sh = crend.get_size()
- sw = int(sw)
- sh = int(sh)
-
- dw = int(width)
- dh = int(height)
-
- bw = self.left + self.right
- bh = self.top + self.bottom
-
- xborder = min(bw, sw - 2, dw)
- if xborder:
- left = self.left * xborder / bw
- right = self.right * xborder / bw
- else:
- left = 0
- right = 0
-
- yborder = min(bh, sh - 2, dh)
- if yborder:
- top = self.top * yborder / bh
- bottom = self.bottom * yborder / bh
- else:
- top = 0
- bottom = 0
-
- if renpy.display.draw.info["renderer"] == "sw":
- return self.sw_render(crend, dw, dh, left, top, right, bottom)
-
- def draw(x0, x1, y0, y1):
-
- # Compute the coordinates of the left, right, top, and
- # bottom sides of the region, for both the source and
- # destination surfaces.
-
- # left side.
- if x0 >= 0:
- dx0 = x0
- sx0 = x0
- else:
- dx0 = dw + x0
- sx0 = sw + x0
-
- # right side.
- if x1 > 0:
- dx1 = x1
- sx1 = x1
- else:
- dx1 = dw + x1
- sx1 = sw + x1
-
- # top side.
- if y0 >= 0:
- dy0 = y0
- sy0 = y0
- else:
- dy0 = dh + y0
- sy0 = sh + y0
-
- # bottom side
- if y1 > 0:
- dy1 = y1
- sy1 = y1
- else:
- dy1 = dh + y1
- sy1 = sh + y1
-
- # Quick exit.
- if sx0 == sx1 or sy0 == sy1:
- return
-
- # Compute sizes.
- csw = sx1 - sx0
- csh = sy1 - sy0
- cdw = dx1 - dx0
- cdh = dy1 - dy0
-
- if csw <= 0 or csh <= 0 or cdh <= 0 or cdw <= 0:
- return
-
- # Get a subsurface.
- cr = crend.subsurface((sx0, sy0, csw, csh))
-
- # Scale or tile if we have to.
- if csw != cdw or csh != cdh:
-
- if self.tile:
- newcr = Render(cdw, cdh)
- newcr.clipping = True
-
- for x in range(0, cdw, csw):
- for y in range(0, cdh, csh):
- newcr.blit(cr, (x, y))
-
- cr = newcr
-
- else:
-
- newcr = Render(cdw, cdh)
- newcr.forward = Matrix2D(1.0 * csw / cdw, 0, 0, 1.0 * csh / cdh)
- newcr.reverse = Matrix2D(1.0 * cdw / csw, 0, 0, 1.0 * cdh / csh)
- newcr.blit(cr, (0, 0))
-
- cr = newcr
-
- # Blit.
- rv.blit(cr, (dx0, dy0))
- return
-
- rv = Render(dw, dh)
-
- self.draw_pattern(draw, left, top, right, bottom)
-
- return rv
-
- def draw_pattern(self, draw, left, top, right, bottom):
- # Top row.
- if top:
-
- if left:
- draw(0, left, 0, top)
-
- draw(left, -right, 0, top)
-
- if right:
- draw(-right, 0, 0, top)
-
- # Middle row.
- if left:
- draw(0, left, top, -bottom)
-
- draw(left, -right, top, -bottom)
-
- if right:
- draw(-right, 0, top, -bottom)
-
- # Bottom row.
- if bottom:
- if left:
- draw(0, left, -bottom, 0)
-
- draw(left, -right, -bottom, 0)
-
- if right:
- draw(-right, 0, -bottom, 0)
-
-
-
- def sw_render(self, crend, dw, dh, left, top, right, bottom):
-
- source = crend.render_to_texture(True)
- sw, sh = source.get_size()
-
- dest = renpy.display.swdraw.surface(dw, dh, True)
- rv = dest
-
- def draw(x0, x1, y0, y1):
-
- # Compute the coordinates of the left, right, top, and
- # bottom sides of the region, for both the source and
- # destination surfaces.
-
- # left side.
- if x0 >= 0:
- dx0 = x0
- sx0 = x0
- else:
- dx0 = dw + x0
- sx0 = sw + x0
-
- # right side.
- if x1 > 0:
- dx1 = x1
- sx1 = x1
- else:
- dx1 = dw + x1
- sx1 = sw + x1
-
- # top side.
- if y0 >= 0:
- dy0 = y0
- sy0 = y0
- else:
- dy0 = dh + y0
- sy0 = sh + y0
-
- # bottom side
- if y1 > 0:
- dy1 = y1
- sy1 = y1
- else:
- dy1 = dh + y1
-
- sy1 = sh + y1
-
- # Quick exit.
- if sx0 == sx1 or sy0 == sy1 or dx1 <= dx0 or dy1 <= dy0:
- return
-
- # Compute sizes.
- srcsize = (sx1 - sx0, sy1 - sy0)
- dstsize = (int(dx1 - dx0), int(dy1 - dy0))
-
- # Get a subsurface.
- surf = source.subsurface((sx0, sy0, srcsize[0], srcsize[1]))
-
- # Scale or tile if we have to.
- if dstsize != srcsize:
- if self.tile:
- tilew, tileh = srcsize
- dstw, dsth = dstsize
-
- surf2 = renpy.display.pgrender.surface_unscaled(dstsize, surf)
-
- for y in range(0, dsth, tileh):
- for x in range(0, dstw, tilew):
- surf2.blit(surf, (x, y))
-
- surf = surf2
-
- else:
- surf2 = renpy.display.scale.real_transform_scale(surf, dstsize)
- surf = surf2
-
- # Blit.
- dest.blit(surf, (dx0, dy0))
-
- self.draw_pattern(draw, left, top, right, bottom)
-
- rrv = renpy.display.render.Render(dw, dh)
- rrv.blit(rv, (0, 0))
- rrv.depends_on(crend)
-
- # And, finish up.
- return rrv
-
- def visit(self):
- return [ self.image ]
-
-
diff --git a/unrpyc/renpy/display/imagemap.py b/unrpyc/renpy/display/imagemap.py
deleted file mode 100644
index 380bf80..0000000
--- a/unrpyc/renpy/display/imagemap.py
+++ /dev/null
@@ -1,233 +0,0 @@
-# Copyright 2004-2013 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.
-
-# This file handles imagemap caching.
-
-import pygame
-import renpy.display
-
-from renpy.display.render import render
-
-import hashlib
-import os
-
-# A list of cache images we've already written.
-cached = set()
-
-class ImageMapCrop(renpy.display.core.Displayable):
- """
- This handles the cropping of uncached imagemap components.
- """
-
- def __init__(self, child, rect):
- super(ImageMapCrop, self).__init__()
-
- self.child = child
- self.rect = rect
-
- def visit(self):
- return [ self.child ]
-
- def render(self, width, height, st, at):
- cr = render(self.child, width, height, st, at)
- return cr.subsurface(self.rect)
-
-
-class ImageCacheCrop(renpy.display.core.Displayable):
- """
- This handles the cropping of an imagemap component.
- """
-
- def __init__(self, cache, index):
- super(ImageCacheCrop, self).__init__()
-
- # The cache object we're associated with.
- self.cache = cache
-
- # The index of
- self.index = index
-
- def visit(self):
- return self.cache.visit(self.index)
-
- def render(self, width, height, st, at):
- return self.cache.render(self.index, width, height, st, at)
-
-class ImageMapCache(renpy.object.Object):
-
- def __init__(self, enable):
- self.md5 = hashlib.md5()
-
- # A list of (image, rect) tuples. The index in this list is used
- # as a unique identifier for an ImageCacheCrop object.
- self.imagerect = [ ]
-
- # A map from (image, rect) to ImageCacheCrop object.
- self.hotspots = { }
-
- # A list of (width, height, index) tuples.
- self.areas = [ ]
-
- # The image containing our children.
- self.cache = None
-
- # A list that, for each hotspot, gives the rectangle in the cache
- # image corresponding to that hotspot.
- self.cache_rect = None
-
- # The size of the cache.
- self.cache_width = None
- self.cache_height = None
-
- self.enable = enable
-
- def visit(self, index):
- if self.cache is not None:
- return [ self.cache ]
- else:
- return [ self.imagerect[index][0] ]
-
- def crop(self, d, rect):
- if not isinstance(d, renpy.display.im.ImageBase) or \
- not renpy.config.imagemap_cache or \
- not self.enable:
- return ImageMapCrop(d, rect)
-
- key = (d, rect)
- rv = self.hotspots.get(key, None)
- if rv is not None:
- return rv
-
- self.md5.update(repr(d.identity))
- self.md5.update(repr(d.identity))
-
- index = len(self.imagerect)
- rv = ImageCacheCrop(self, index)
-
- self.imagerect.append(key)
- self.hotspots[key] = rv
- self.areas.append((rect[2] + 2, rect[3] + 2, index))
-
- return rv
-
- def layout(self):
- self.areas.sort()
- self.areas.reverse()
- self.cache_rect = [ None ] * len(self.areas)
-
- # The width of the cache image.
- width = self.areas[0][0]
-
- x = 0
- y = 0
- line_height = 0
-
- for w, h, i in self.areas:
-
- if x + w > width:
- y += line_height
- line_height = 0
- x = 0
-
- self.cache_rect[i] = (x+1, y+1, w-2, h-2)
-
- x += w
- if line_height < h:
- line_height = h
-
- self.cache_width = width
- self.cache_height = y + line_height
-
- def write_cache(self, filename):
-
- if filename in cached:
- return
-
- cached.add(filename)
-
- # If all of our dependencies are of the same age or less,
- # we don't need to rebuild the cache.
-
- if renpy.loader.loadable(filename):
- d_set = set()
- mtime = 0
-
- for i, rect in self.imagerect:
- if i in d_set:
- continue
-
- d_set.add(i)
- mtime = max(i.get_mtime(), mtime)
-
- if renpy.loader.get_mtime(filename) >= mtime:
- return
-
- fn = os.path.join(renpy.config.gamedir, filename)
- dir = os.path.dirname(fn) #@ReservedAssignment
-
- if not os.path.exists(dir):
- os.makedirs(dir)
-
- cache = pygame.Surface((self.cache_width, self.cache_height), pygame.SRCALPHA, 32)
-
- for i, (d, rect) in enumerate(self.imagerect):
- x, y, _w, _h = self.cache_rect[i]
-
- surf = renpy.display.im.cache.get(d).subsurface(rect)
- cache.blit(surf, (x, y))
-
- pygame.image.save(cache, renpy.exports.fsencode(fn))
-
- def finish(self):
- if not self.areas:
- return
-
- filename = "im-%s.png" % (self.md5.hexdigest())
-
- if renpy.game.preferences.language:
- filename = renpy.game.preferences.language + "-" + filename
-
- filename = "cache/" + filename
-
- self.md5 = None
-
- self.layout()
-
- if renpy.config.developer:
- try:
- self.write_cache(filename)
- except:
- pass
-
- if renpy.loader.loadable(filename):
- self.cache = renpy.display.im.Image(filename)
-
-
- def render(self, index, width, height, st, at):
- if self.cache is None:
- d, rect = self.imagerect[index]
- return render(d, width, height, st, at).subsurface(rect)
-
- return render(self.cache, width, height, st, at).subsurface(self.cache_rect[index])
-
-
-
-
diff --git a/unrpyc/renpy/display/joystick.py b/unrpyc/renpy/display/joystick.py
deleted file mode 100644
index c606dd9..0000000
--- a/unrpyc/renpy/display/joystick.py
+++ /dev/null
@@ -1,126 +0,0 @@
-# Copyright 2004-2013 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.
-
-# This file is responsible for joystick support in Ren'Py.
-
-import os
-import pygame
-
-import renpy.display
-
-# Do we have a joystick enabled?
-enabled = False
-
-# The old states for each axis.
-old_axis_states = { }
-
-def init():
- """
- Initialize the joystick system.
- """
-
- global enabled
-
- if not renpy.config.joystick:
- return
-
- if 'RENPY_DISABLE_JOYSTICK' in os.environ:
- return
-
- try:
- pygame.joystick.init()
-
- for i in range(0, pygame.joystick.get_count()):
- pygame.joystick.Joystick(i).init()
- enabled = True
- except:
- if renpy.config.debug:
- raise
-
-def event(ev):
-
- if not enabled:
- return ev
-
- if ev.type == pygame.JOYAXISMOTION:
-
- if not renpy.display.interface.focused:
- return None
-
- if ev.value >= 0.5:
- state = "Positive"
- elif ev.value <= -0.5:
- state = "Negative"
- else:
- state = None
-
- oldstate = old_axis_states.get((ev.joy, ev.axis), None)
-
- if state == oldstate:
- return None
-
- if oldstate:
- release = "Axis %d.%d %s" % (ev.joy, ev.axis, oldstate)
- else:
- release = None
-
- old_axis_states[ev.joy, ev.axis] = state
-
- if state:
- press = "Axis %d.%d %s" % (ev.joy, ev.axis, state)
- else:
- press = None
-
- if not press and not release:
- return None
-
- return pygame.event.Event(renpy.display.core.JOYEVENT,
- press=press, release=release)
-
- if ev.type == pygame.JOYBUTTONDOWN:
-
- if not renpy.display.interface.focused:
- return None
-
- return pygame.event.Event(renpy.display.core.JOYEVENT,
- press="Button %d.%d" % (ev.joy, ev.button),
- release=None)
- if ev.type == pygame.JOYBUTTONUP:
-
- if not renpy.display.interface.focused:
- return None
-
- return pygame.event.Event(renpy.display.core.JOYEVENT,
- press=None,
- release="Button %d.%d" % (ev.joy, ev.button))
-
- return ev
-
-class JoyBehavior(renpy.display.layout.Null):
- """
- This is a behavior intended for joystick calibration. If a joystick
- event occurs, this returns it as a string.
- """
-
- def event(self, ev, x, y, st):
- if ev.type == renpy.display.core.JOYEVENT:
- return ev.press
-
diff --git a/unrpyc/renpy/display/layout.py b/unrpyc/renpy/display/layout.py
deleted file mode 100644
index e07e28d..0000000
--- a/unrpyc/renpy/display/layout.py
+++ /dev/null
@@ -1,1744 +0,0 @@
-# Copyright 2004-2013 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.
-
-# This file contains classes that handle layout of displayables on
-# the screen.
-
-from renpy.display.render import render, Render
-import renpy.display
-
-
-def scale(num, base):
- """
- If num is a float, multiplies it by base and returns that. Otherwise,
- returns num unchanged.
- """
-
- if isinstance(num, float):
- return num * base
- else:
- return num
-
-class Null(renpy.display.core.Displayable):
- """
- :doc: disp_imagelike
-
- A displayable that creates an empty box on the screen. The size
- of the box is controlled by `width` and `height`. This can be used
- when a displayable requires a child, but no child is suitable, or
- as a spacer inside a box.
-
- ::
-
- image logo spaced = HBox("logo.png", Null(width=100), "logo.png")
-
- """
-
- def __init__(self, width=0, height=0, **properties):
- super(Null, self).__init__(**properties)
- self.width = width
- self.height = height
-
- def render(self, width, height, st, at):
- rv = renpy.display.render.Render(self.width, self.height)
-
- if self.focusable:
- rv.add_focus(self, None, None, None, None, None)
-
- return rv
-
-
-class Container(renpy.display.core.Displayable):
- """
- This is the base class for containers that can have one or more
- children.
-
- @ivar children: A list giving the children that have been added to
- this container, in the order that they were added in.
-
- @ivar child: The last child added to this container. This is also
- used to access the sole child in containers that can only hold
- one child.
-
- @ivar offsets: A list giving offsets for each of our children.
- It's expected that render will set this up each time it is called.
-
- @ivar sizes: A list giving sizes for each of our children. It's
- also expected that render will set this each time it is called.
-
- """
-
- # We indirect all list creation through this, so that we can
- # use RevertableLists if we want.
- _list_type = list
-
- def __init__(self, *args, **properties):
-
- self.children = self._list_type()
- self.child = None
- self.offsets = self._list_type()
-
- for i in args:
- self.add(i)
-
- super(Container, self).__init__(**properties)
-
- def set_style_prefix(self, prefix, root):
- super(Container, self).set_style_prefix(prefix, root)
-
- for i in self.children:
- i.set_style_prefix(prefix, False)
-
- def add(self, d):
- """
- Adds a child to this container.
- """
-
- child = renpy.easy.displayable(d)
-
- self.children.append(child)
-
- self.child = child
- self.offsets = self._list_type()
-
- def remove(self, d):
- """
- Removes the first instance of child from this container. May
- not work with all containers.
- """
-
- for i, c in enumerate(self.children):
- if c is d:
- break
- else:
- return
-
- self.children.pop(i) # W0631
- self.offsets = self._list_type()
-
- if self.children:
- self.child = self.children[-1]
- else:
- self.child = None
-
-
- def update(self):
- """
- This should be called if a child is added to this
- displayable outside of the render function.
- """
-
- renpy.display.render.invalidate(self)
-
-
- def render(self, width, height, st, at):
-
- rv = Render(width, height)
- self.offsets = self._list_type()
-
- for c in self.children:
- cr = render(c, width, height, st, at)
- offset = c.place(rv, 0, 0, width, height, cr)
- self.offsets.append(offset)
-
- return rv
-
-
- def event(self, ev, x, y, st):
-
- children = self.children
- offsets = self.offsets
-
- for i in range(len(offsets) - 1, -1, -1):
-
- d = children[i]
- xo, yo = offsets[i]
-
- rv = d.event(ev, x - xo, y - yo, st)
- if rv is not None:
- return rv
-
- return None
-
- def visit(self):
- return self.children
-
- # These interact with the ui functions to allow use as a context
- # manager.
-
- def __enter__(self):
-
- renpy.ui.context_enter(self)
- return self
-
- def __exit__(self, exc_type, exc_val, exc_tb):
-
- renpy.ui.context_exit(self)
- return False
-
-
-
-
-def LiveComposite(size, *args, **properties):
- """
- :args: disp_imagelike
-
- This creates a new displayable of `size`, by compositing other
- displayables. `size` is a (width, height) tuple.
-
- The remaining positional arguments are used to place images inside
- the LiveComposite. The remaining positional arguments should come
- in groups of two, with the first member of each group an (x, y)
- tuple, and the second member of a group is a displayable that
- is composited at that position.
-
- Displayables are composited from back to front.
-
- ::
-
- image eileen composite = LiveComposite(
- (300, 600),
- (0, 0), "body.png",
- (0, 0), "clothes.png",
- (50, 50), "expression.png")
- """
-
- properties.setdefault('style', 'image_placement')
-
- width, height = size
-
- rv = Fixed(xmaximum=width, ymaximum=height, xminimum=width, yminimum=height, **properties)
-
- if len(args) % 2 != 0:
- raise Exception("LiveComposite requires an odd number of arguments.")
-
- for pos, widget in zip(args[0::2], args[1::2]):
- xpos, ypos = pos
- rv.add(Position(renpy.easy.displayable(widget),
- xpos=xpos, xanchor=0, ypos=ypos, yanchor=0))
-
- return rv
-
-class Position(Container):
- """
- Controls the placement of a displayable on the screen, using
- supplied position properties. This is the non-curried form of
- Position, which should be used when the user has directly created
- the displayable that will be shown on the screen.
- """
-
- def __init__(self, child, style='image_placement', **properties):
- """
- @param child: The child that is being laid out.
-
- @param style: The base style of this position.
-
- @param properties: Position properties that control where the
- child of this widget is placed.
- """
-
- super(Position, self).__init__(style=style, **properties)
- self.add(child)
-
- def render(self, width, height, st, at):
-
- surf = render(self.child, width, height, st, at)
-
- self.offsets = [ (0, 0) ]
-
- rv = renpy.display.render.Render(surf.width, surf.height)
- rv.blit(surf, (0, 0))
-
- return rv
-
- def get_placement(self):
-
- xpos, ypos, xanchor, yanchor, xoffset, yoffset, subpixel = self.child.get_placement()
-
- v = self.style.xpos
- if v is not None:
- xpos = v
-
- v = self.style.ypos
- if v is not None:
- ypos = v
-
- v = self.style.xanchor
- if v is not None:
- xanchor = v
-
- v = self.style.yanchor
- if v is not None:
- yanchor = v
-
- v = self.style.xoffset
- if v is not None:
- xoffset = v
-
- v = self.style.yoffset
- if v is not None:
- yoffset = v
-
- v = self.style.subpixel
- if v is not None:
- subpixel = v
-
- return xpos, ypos, xanchor, yanchor, xoffset, yoffset, subpixel
-
-
-class Grid(Container):
- """
- A grid is a widget that evenly allocates space to its children.
- The child widgets should not be greedy, but should instead be
- widgets that only use part of the space available to them.
- """
-
- def __init__(self, cols, rows, padding=None,
- transpose=False,
- style='grid', **properties):
- """
- @param cols: The number of columns in this widget.
-
- @params rows: The number of rows in this widget.
-
- @params transpose: True if the grid should be transposed.
- """
-
- if padding is not None:
- properties.setdefault('spacing', padding)
-
- super(Grid, self).__init__(style=style, **properties)
-
- cols = int(cols)
- rows = int(rows)
-
- self.cols = cols
- self.rows = rows
-
- self.transpose = transpose
-
- def render(self, width, height, st, at):
-
- # For convenience and speed.
- padding = self.style.spacing
- cols = self.cols
- rows = self.rows
-
- if len(self.children) != cols * rows:
- if len(self.children) < cols * rows:
- raise Exception("Grid not completely full.")
- else:
- raise Exception("Grid overfull.")
-
- # If necessary, transpose the grid (kinda hacky, but it works here.)
- if self.transpose:
- self.transpose = False
-
- old_children = self.children[:]
-
- for y in range(0, rows):
- for x in range(0, cols):
- self.children[x + y * cols] = old_children[ y + x * rows ]
-
-
- # Now, start the actual rendering.
-
- renwidth = width
- renheight = height
-
- if self.style.xfill:
- renwidth = (width - (cols - 1) * padding) / cols
- if self.style.yfill:
- renheight = (height - (rows - 1) * padding) / rows
-
- renders = [ render(i, renwidth, renheight, st, at) for i in self.children ]
- sizes = [ i.get_size() for i in renders ]
-
- cwidth = 0
- cheight = 0
-
- for w, h in sizes:
- cwidth = max(cwidth, w)
- cheight = max(cheight, h)
-
- if self.style.xfill:
- cwidth = renwidth
-
- if self.style.yfill:
- cheight = renheight
-
- width = cwidth * cols + padding * (cols - 1)
- height = cheight * rows + padding * (rows - 1)
-
- rv = renpy.display.render.Render(width, height)
-
- self.offsets = [ ]
-
- for y in range(0, rows):
- for x in range(0, cols):
-
- child = self.children[ x + y * cols ]
- surf = renders[x + y * cols]
-
- xpos = x * (cwidth + padding)
- ypos = y * (cheight + padding)
-
- offset = child.place(rv, xpos, ypos, cwidth, cheight, surf)
- self.offsets.append(offset)
-
- return rv
-
-class IgnoreLayers(Exception):
- """
- Raise this to have the event ignored by layers, but reach the
- underlay.
- """
-
- pass
-
-class MultiBox(Container):
-
- layer_name = None
- first = True
- order_reverse = False
-
- def __init__(self, spacing=None, layout=None, style='default', **properties):
-
- if spacing is not None:
- properties['spacing'] = spacing
-
- super(MultiBox, self).__init__(style=style, **properties)
-
- self.default_layout = layout
-
- # The start and animation times for children of this
- # box.
- self.start_times = [ ]
- self.anim_times = [ ]
-
- # A map from layer name to the widget corresponding to
- # that layer.
- self.layers = None
-
- # The scene list for this widget.
- self.scene_list = None
-
- def add(self, widget, start_time=None, anim_time=None): # W0221
- super(MultiBox, self).add(widget)
- self.start_times.append(start_time)
- self.anim_times.append(anim_time)
-
- def append_scene_list(self, l):
-
- for sle in l:
- self.add(sle.displayable, sle.show_time, sle.animation_time)
-
- if self.scene_list is None:
- self.scene_list = [ ]
-
- self.scene_list.extend(l)
-
- def render(self, width, height, st, at):
-
- # Do we need to adjust the child times due to our being a layer?
- if self.layer_name or (self.layers is not None):
- adjust_times = True
- else:
- adjust_times = False
-
- xminimum = self.style.xminimum
- if xminimum is not None:
- width = max(width, scale(xminimum, width))
-
- yminimum = self.style.yminimum
- if yminimum is not None:
- height = max(height, scale(yminimum, height))
-
- if self.first:
-
- self.first = False
-
- if adjust_times:
-
- it = renpy.game.interface.interact_time
-
- self.start_times = [ i or it for i in self.start_times ]
- self.anim_times = [ i or it for i in self.anim_times ]
-
- layout = self.style.box_layout
-
- if layout is None:
- layout = self.default_layout
-
- self.layout = layout # W0201
-
- else:
- layout = self.layout
-
-
- # Handle time adjustment, store the results in csts and cats.
- if adjust_times:
- t = renpy.game.interface.frame_time
-
- csts = [ t - start for start in self.start_times ]
- cats = [ t - anim for anim in self.anim_times ]
-
- else:
- csts = [ st ] * len(self.children)
- cats = [ at ] * len(self.children)
-
- offsets = [ ]
-
- if layout == "fixed":
-
- rv = None
-
- if self.style.order_reverse:
- iterator = list(zip(reversed(self.children), reversed(csts), reversed(cats)))
- else:
- iterator = list(zip(self.children, csts, cats))
-
- for child, cst, cat in iterator:
-
- surf = render(child, width, height, cst, cat)
-
- if rv is None:
-
- if self.style.fit_first:
- sw, sh = surf.get_size()
- width = min(width, sw)
- height = min(height, sh)
-
-
- rv = renpy.display.render.Render(width, height, layer_name=self.layer_name)
-
- if surf:
- offset = child.place(rv, 0, 0, width, height, surf)
- offsets.append(offset)
- else:
- offsets.append((0, 0))
-
- if rv is None:
- rv = renpy.display.render.Render(width, height, layer_name=self.layer_name)
-
- if self.style.order_reverse:
- offsets.reverse()
-
- self.offsets = offsets
-
- return rv
-
- # If we're here, we have a box, either horizontal or vertical. Which is good,
- # as we can share some code between boxes.
-
-
- spacing = self.style.spacing
- first_spacing = self.style.first_spacing
-
- if first_spacing is None:
- first_spacing = spacing
-
- spacings = [ first_spacing ] + [ spacing ] * (len(self.children) - 1)
-
- box_wrap = self.style.box_wrap
-
- xfill = self.style.xfill
- yfill = self.style.yfill
-
- # The shared height and width of the current line. The line_height must
- # be 0 for a vertical box, and the line_width must be 0 for a horizontal
- # box.
- line_width = 0
- line_height = 0
-
- # The children to layout.
- children = list(self.children)
- if self.style.box_reverse:
- children.reverse()
- spacings.reverse()
-
- # a list of (child, x, y, w, h, surf) tuples that are turned into
- # calls to child.place().
- placements = [ ]
-
- # The maximum x and y.
- maxx = 0
- maxy = 0
-
- def layout_line(line, xfill, yfill):
- """
- Lays out a single line.
-
- `line` a list of (child, x, y, surf) tuples.
- `xfill` the amount of space to add in the x direction.
- `yfill` the amount of space to add in the y direction.
- """
-
- xfill = max(0, xfill)
- yfill = max(0, yfill)
-
- if line:
- xperchild = xfill / len(line)
- yperchild = yfill / len(line)
- else:
- xperchild = 0
- yperchild = 0
-
- maxxout = maxx
- maxyout = maxy
-
- for i, (child, x, y, surf) in enumerate(line):
- sw, sh = surf.get_size()
- sw = max(line_width, sw)
- sh = max(line_height, sh)
-
- x += i * xperchild
- y += i * yperchild
-
- sw += xperchild
- sh += yperchild
-
- placements.append((child, x, y, sw, sh, surf))
-
- maxxout = max(maxxout, x + sw)
- maxyout = max(maxyout, y + sh)
-
- return maxxout, maxyout
-
- x = 0
- y = 0
-
- full_width = False
- full_height = False
-
- if layout == "horizontal":
-
- full_height = yfill
-
- line_height = 0
- line = [ ]
- remwidth = width
-
- for d, padding, cst, cat in zip(children, spacings, csts, cats):
-
- if box_wrap:
- rw = width
- else:
- rw = remwidth
-
- surf = render(d, rw, height - y, cst, cat)
- sw, sh = surf.get_size()
-
- if box_wrap and remwidth - sw - padding <= 0 and line:
- maxx, maxy = layout_line(line, remwidth if xfill else 0, 0)
-
- y += line_height
- x = 0
- line_height = 0
- remwidth = width
- line = [ ]
-
-
- line.append((d, x, y, surf))
- line_height = max(line_height, sh)
- x += sw + padding
- remwidth -= (sw + padding)
-
- maxx, maxy = layout_line(line, remwidth if xfill else 0, 0)
-
-
- elif layout == "vertical":
-
- full_width = xfill
-
- line_width = 0
- line = [ ]
- remheight = height
-
- for d, padding, cst, cat in zip(children, spacings, csts, cats):
-
- if box_wrap:
- rh = height
- else:
- rh = remheight
-
- surf = render(d, width - x, rh, cst, cat)
- sw, sh = surf.get_size()
-
- if box_wrap and remheight - sh - padding <= 0:
- maxx, maxy = layout_line(line, 0, remheight if yfill else 0)
-
- x += line_width
- y = 0
- line_width = 0
- remheight = height
- line = [ ]
-
- line.append((d, x, y, surf))
- line_width = max(line_width, sw)
- y += sh + padding
- remheight -= (sh + padding)
-
- maxx, maxy = layout_line(line, 0, remheight if yfill else 0)
-
- # Back to the common for vertical and horizontal.
-
- if not xfill:
- width = maxx
-
- if not yfill:
- height = maxy
-
- rv = renpy.display.render.Render(width, height)
-
- if self.style.box_reverse ^ self.style.order_reverse:
- placements.reverse()
-
- for child, x, y, w, h, surf in placements:
- if full_width:
- w = width
- if full_height:
- h = height
-
- offset = child.place(rv, x, y, w, h, surf)
- offsets.append(offset)
-
- if self.style.order_reverse:
- offsets.reverse()
-
- self.offsets = offsets
-
- return rv
-
-
- def event(self, ev, x, y, st):
-
-
- children_offsets = list(zip(self.children, self.offsets, self.start_times))
-
- if not self.style.order_reverse:
- children_offsets.reverse()
-
- try:
-
- for i, (xo, yo), t in children_offsets:
-
- if t is None:
- cst = st
- else:
- cst = renpy.game.interface.event_time - t
-
- rv = i.event(ev, x - xo, y - yo, cst)
- if rv is not None:
- return rv
-
- except IgnoreLayers:
- if self.layers:
- return None
- else:
- raise
-
- return None
-
-def Fixed(**properties):
- return MultiBox(layout='fixed', **properties)
-
-class SizeGroup(renpy.object.Object):
-
- def __init__(self):
-
- super(SizeGroup, self).__init__()
-
- self.members = [ ]
- self._width = None
- self.computing_width = False
-
- def width(self, width, height, st, at):
- if self._width is not None:
- return self._width
-
- if self.computing_width:
- return 0
-
- self.computing_width = True
-
- maxwidth = 0
-
- for i in self.members:
- rend = renpy.display.render.render(i, width, height, st, at)
- maxwidth = max(rend.width, maxwidth)
- renpy.display.render.invalidate(i)
-
- self._width = maxwidth
- self.computing_width = False
-
- return maxwidth
-
-
-size_groups = dict()
-
-class Window(Container):
- """
- A window that has padding and margins, and can place a background
- behind its child. `child` is the child added to this
- displayable. All other properties are as for the :ref:`Window`
- screen language statement.
- """
-
- def __init__(self, child, style='window', **properties):
-
- super(Window, self).__init__(style=style, **properties)
- if child is not None:
- self.add(child)
-
- def visit(self):
- return [ self.style.background ] + self.children
-
- def get_child(self):
- return self.style.child or self.child
-
- def per_interact(self):
- size_group = self.style.size_group
-
- if size_group:
- group = size_groups.get(size_group, None)
- if group is None:
- group = size_groups[size_group] = SizeGroup()
-
- group.members.append(self)
-
- def predict_one(self):
- # Child will be predicted by visiting.
-
- pd = renpy.display.predict.displayable
- style = self.style
-
- pd(style.insensitive_background)
- pd(style.idle_background)
- pd(style.hover_background)
- pd(style.selected_idle_background)
- pd(style.selected_hover_background)
-
- pd(style.insensitive_child)
- pd(style.idle_child)
- pd(style.hover_child)
- pd(style.selected_idle_child)
- pd(style.selected_hover_child)
-
- pd(style.insensitive_foreground)
- pd(style.idle_foreground)
- pd(style.hover_foreground)
- pd(style.selected_idle_foreground)
- pd(style.selected_hover_foreground)
-
- def render(self, width, height, st, at):
-
- # save some typing.
- style = self.style
-
- xminimum = scale(style.xminimum, width)
- yminimum = scale(style.yminimum, height)
-
- size_group = self.style.size_group
- if size_group and size_group in size_groups:
- xminimum = max(xminimum, size_groups[size_group].width(width, height, st, at))
-
- left_margin = scale(style.left_margin, width)
- left_padding = scale(style.left_padding, width)
-
- right_margin = scale(style.right_margin, width)
- right_padding = scale(style.right_padding, width)
-
- top_margin = scale(style.top_margin, height)
- top_padding = scale(style.top_padding, height)
-
- bottom_margin = scale(style.bottom_margin, height)
- bottom_padding = scale(style.bottom_padding, height)
-
- # c for combined.
- cxmargin = left_margin + right_margin
- cymargin = top_margin + bottom_margin
-
- cxpadding = left_padding + right_padding
- cypadding = top_padding + bottom_padding
-
- child = self.get_child()
-
- # Render the child.
- surf = render(child,
- width - cxmargin - cxpadding,
- height - cymargin - cypadding,
- st, at)
-
- sw, sh = surf.get_size()
-
- # If we don't fill, shrink our size to fit.
-
- if not style.xfill:
- width = max(cxmargin + cxpadding + sw, xminimum)
-
- if not style.yfill:
- height = max(cymargin + cypadding + sh, yminimum)
-
- rv = renpy.display.render.Render(width, height)
-
- # Draw the background. The background should render at exactly the
- # requested size. (That is, be a Frame or a Solid).
- if style.background:
- bw = width - cxmargin
- bh = height - cymargin
-
- back = render(style.background, bw, bh, st, at)
-
- style.background.place(rv, left_margin, top_margin, bw, bh, back, main=False)
-
- offsets = child.place(rv,
- left_margin + left_padding,
- top_margin + top_padding,
- width - cxmargin - cxpadding,
- height - cymargin - cypadding,
- surf)
-
- # Draw the foreground. The background should render at exactly the
- # requested size. (That is, be a Frame or a Solid).
- if style.foreground:
- bw = width - cxmargin
- bh = height - cymargin
-
- back = render(style.foreground, bw, bh, st, at)
-
- style.foreground.place(rv, left_margin, top_margin, bw, bh, back, main=False)
-
- self.offsets = [ offsets ]
-
- self.window_size = width, height # W0201
-
- return rv
-
-
-def dynamic_displayable_compat(st, at, expr):
- child = renpy.python.py_eval(expr)
- return child, None
-
-class DynamicDisplayable(renpy.display.core.Displayable):
- """
- :doc: disp_dynamic
-
- A displayable that can change its child based on a Python
- function, over the course of an interaction.
-
- `function`
- A function that is called with the arguments:
-
- * The amount of time the displayable has been shown for.
- * The amount of time any displayable with the same tag has been shown for.
- * Any positional or keyword arguments supplied to DynamicDisplayable.
-
- and should return a (d, redraw) tuple, where:
-
- * `d` is a displayable to show.
- * `redraw` is the amount of time to wait before calling the
- function again, or None to not call the function again
- before the start of the next interaction.
-
- `function` is called at the start of every interaction.
-
- As a special case, `function` may also be a python string that evaluates
- to a displayable. In that case, function is run once per interaction.
-
- ::
-
- # If tooltip is not empty, shows it in a text. Otherwise,
- # show Null. Checks every tenth of a second to see if the
- # tooltip has been updated.
- init python:
- def show_tooltip(st, at):
- if tooltip:
- return tooltip, .1
- else:
- return Null()
-
- image tooltipper = DynamicDisplayable(show_tooltip)
-
- """
-
- nosave = [ 'child' ]
-
- def after_setstate(self):
- self.child = None
-
- def __init__(self, function, *args, **kwargs):
-
- super(DynamicDisplayable, self).__init__()
- self.child = None
-
- if isinstance(function, str):
- args = ( function, )
- kwargs = { }
- function = dynamic_displayable_compat
-
- self.predict_function = kwargs.pop("_predict_function", None)
- self.function = function
- self.args = args
- self.kwargs = kwargs
- self.st = 0
- self.at = 0
-
- def visit(self):
- return [ ]
-
- def per_interact(self):
- child, _ = self.function(self.st, self.at, *self.args, **self.kwargs)
- child = renpy.easy.displayable(child)
- child.visit_all(lambda a : a.per_interact())
-
- if child is not self.child:
- renpy.display.render.redraw(self, 0)
- self.child = child
-
- def render(self, w, h, st, at):
-
- self.st = st
- self.at = at
-
- child, redraw = self.function(st, at, *self.args, **self.kwargs)
- child = renpy.easy.displayable(child)
- child.visit_all(lambda c : c.per_interact())
-
- self.child = child
-
- if redraw is not None:
- renpy.display.render.redraw(self, redraw)
-
- return renpy.display.render.render(self.child, w, h, st, at)
-
- def predict_one(self):
- if not self.predict_function:
- return
-
- for i in self.predict_function(*self.args, **self.kwargs):
- if i is not None:
- renpy.display.predict.displayable(i)
-
- def get_placement(self):
- if not self.child:
- self.per_interact()
-
- return self.child.get_placement()
-
-
- def event(self, ev, x, y, st):
- if self.child:
- return self.child.event(ev, x, y, st)
-
-# This chooses the first member of switch that's being shown on the
-# given layer.
-def condition_switch_pick(switch):
- for cond, d in switch:
- if cond is None or renpy.python.py_eval(cond):
- return d
-
- raise Exception("Switch could not choose a displayable.")
-
-def condition_switch_show(st, at, switch):
- return condition_switch_pick(switch), None
-
-def condition_switch_predict(switch):
-
- if renpy.game.lint:
- return [ d for _cond, d in switch ]
-
- return [ condition_switch_pick(switch) ]
-
-def ConditionSwitch(*args, **kwargs):
- """
- :doc: disp_dynamic
-
- This is a displayable that changes what it is showing based on
- python conditions. The positional argument should be given in
- groups of two, where each group consists of:
-
- * A string containing a python condition.
- * A displayable to use if the condition is true.
-
- The first true condition has its displayable shown, at least
- one condition should always be true.
-
- ::
-
- image jill = ConditionSwitch(
- "jill_beers > 4", "jill_drunk.png",
- "True", "jill_sober.png")
- """
-
- kwargs.setdefault('style', 'default')
-
- switch = [ ]
-
- if len(args) % 2 != 0:
- raise Exception('ConditionSwitch takes an even number of arguments')
-
- for cond, d in zip(args[0::2], args[1::2]):
-
- d = renpy.easy.displayable(d)
- switch.append((cond, d))
-
- rv = DynamicDisplayable(condition_switch_show,
- switch,
- _predict_function=condition_switch_predict)
-
- return Position(rv, **kwargs)
-
-
-def ShowingSwitch(*args, **kwargs):
- """
- :doc: disp_dynamic
-
- This is a displayable that changes what it is showing based on the
- images are showing on the screen. The positional argument should
- be given in groups of two, where each group consists of:
-
- * A string giving an image name, or None to indicate the default.
- * A displayable to use if the condition is true.
-
- A default image should be specified.
-
- One use of ShowingSwitch is to have side images change depending on
- the current emotion of a character. For example::
-
- define e = Character("Eileen",
- show_side_image=ShowingSwitch(
- "eileen happy", Image("eileen_happy_side.png", xalign=1.0, yalign=1.0),
- "eileen vhappy", Image("eileen_vhappy_side.png", xalign=1.0, yalign=1.0),
- None, Image("eileen_happy_default.png", xalign=1.0, yalign=1.0),
- )
- )
- """
-
- layer = kwargs.pop('layer', 'master')
-
- if len(args) % 2 != 0:
- raise Exception('ShowingSwitch takes an even number of positional arguments')
-
- condargs = [ ]
-
-
- for name, d in zip(args[0::2], args[1::2]):
- if name is not None:
- if not isinstance(name, tuple):
- name = tuple(name.split())
- cond = "renpy.showing(%r, layer=%r)" % (name, layer)
- else:
- cond = None
-
-
- condargs.append(cond)
- condargs.append(d)
-
- return ConditionSwitch(*condargs, **kwargs)
-
-
-class IgnoresEvents(Container):
-
- def __init__(self, child, **properties):
- super(IgnoresEvents, self).__init__(**properties)
- self.add(child)
-
- def render(self, w, h, st, at):
- cr = renpy.display.render.render(self.child, w, h, st, at)
- cw, ch = cr.get_size()
- rv = renpy.display.render.Render(cw, ch)
- rv.blit(cr, (0, 0), focus=False)
-
- return rv
-
- def get_placement(self):
- return self.child.get_placement()
-
- # Ignores events.
- def event(self, ev, x, y, st):
- return None
-
-def edgescroll_proportional(n):
- """
- An edgescroll function that causes the move speed to be proportional
- from the edge distance.
- """
- return n
-
-class Viewport(Container):
-
- __version__ = 3
-
- def after_upgrade(self, version):
- if version < 1:
- self.xadjustment = renpy.display.behavior.Adjustment(1, 0)
- self.yadjustment = renpy.display.behavior.Adjustment(1, 0)
- self.set_adjustments = False
- self.mousewheel = False
- self.draggable = False
- self.width = 0
- self.height = 0
-
- if version < 2:
- self.drag_position = None
-
- if version < 3:
- self.edge_size = False
- self.edge_speed = False
- self.edge_function = None
- self.edge_xspeed = 0
- self.edge_yspeed = 0
- self.edge_last_st = None
-
- def __init__(self,
- child=None,
- child_size=(None, None),
- offsets=(None, None),
- xadjustment=None,
- yadjustment=None,
- set_adjustments=True,
- mousewheel=False,
- draggable=False,
- edgescroll=None,
- style='viewport',
- xinitial=None,
- yinitial=None,
- replaces=None,
- **properties):
-
- super(Viewport, self).__init__(style=style, **properties)
- if child is not None:
- self.add(child)
-
- if xadjustment is None:
- self.xadjustment = renpy.display.behavior.Adjustment(1, 0)
- else:
- self.xadjustment = xadjustment
-
- if yadjustment is None:
- self.yadjustment = renpy.display.behavior.Adjustment(1, 0)
- else:
- self.yadjustment = yadjustment
-
-
- if isinstance(replaces, Viewport):
- self.xadjustment.range = replaces.xadjustment.range
- self.yadjustment.range = replaces.yadjustment.range
- self.xadjustment.value = replaces.xadjustment.value
- self.yadjustment.value = replaces.yadjustment.value
- self.xoffset = replaces.xoffset
- self.yoffset = replaces.yoffset
- self.drag_position = replaces.drag_position
- else:
- self.xoffset = offsets[0] if (offsets[0] is not None) else xinitial
- self.yoffset = offsets[1] if (offsets[1] is not None) else yinitial
- self.drag_position = None
-
- if self.xadjustment.adjustable is None:
- self.xadjustment.adjustable = True
-
- if self.yadjustment.adjustable is None:
- self.yadjustment.adjustable = True
-
- self.set_adjustments = set_adjustments
-
- self.child_width, self.child_height = child_size
-
- self.mousewheel = mousewheel
- self.draggable = draggable
-
- self.width = 0
- self.height = 0
-
- # The speed at which we scroll in the x and y directions, in pixels
- # per second.
- self.edge_xspeed = 0
- self.edge_yspeed = 0
-
- # The last time we edgescrolled.
- self.edge_last_st = None
-
- if edgescroll is not None:
-
- # The size of the edges that trigger scrolling.
- self.edge_size = edgescroll[0]
-
- # How far from the edge we can scroll.
- self.edge_speed = edgescroll[1]
-
- if len(edgescroll) >= 3:
- self.edge_function = edgescroll[2]
- else:
- self.edge_function = edgescroll_proportional
-
- else:
- self.edge_size = 0
- self.edge_speed = 0
- self.edge_function = edgescroll_proportional
-
-
- def per_interact(self):
- self.xadjustment.register(self)
- self.yadjustment.register(self)
-
- def render(self, width, height, st, at):
-
- self.width = width
- self.height = height
-
- child_width = self.child_width or width
- child_height = self.child_height or height
-
- surf = render(self.child, child_width, child_height, st, at)
-
- cw, ch = surf.get_size()
-
- # width = min(cw, width)
- # height = min(ch, height)
-
- if self.set_adjustments:
- self.xadjustment.range = max(cw - width, 0)
- self.xadjustment.page = width
- self.yadjustment.range = max(ch - height, 0)
- self.yadjustment.page = height
-
- if self.xoffset is not None:
- if isinstance(self.xoffset, int):
- value = self.xoffset
- else:
- value = max(cw - width, 0) * self.xoffset
-
- self.xadjustment.value = value
-
- if self.yoffset is not None:
- if isinstance(self.yoffset, int):
- value = self.yoffset
- else:
- value = max(ch - height, 0) * self.yoffset
-
- self.yadjustment.value = value
-
- if self.edge_size and self.edge_last_st and (self.edge_xspeed or self.edge_yspeed):
-
- duration = max(st - self.edge_last_st, 0)
- self.xadjustment.change(self.xadjustment.value + duration * self.edge_xspeed)
- self.yadjustment.change(self.yadjustment.value + duration * self.edge_yspeed)
-
- self.check_edge_redraw()
-
- self.edge_last_st = st
-
- cxo = -int(self.xadjustment.value)
- cyo = -int(self.yadjustment.value)
-
- self.offsets = [ (cxo, cyo) ]
-
- rv = renpy.display.render.Render(width, height)
- rv.blit(surf, (cxo, cyo))
-
- return rv
-
- def check_edge_redraw(self):
- redraw = False
-
- if (self.edge_xspeed > 0) and (self.xadjustment.value < self.xadjustment.range):
- redraw = True
- if (self.edge_xspeed < 0) and (self.xadjustment.value > 0):
- redraw = True
-
- if (self.edge_yspeed > 0) and (self.yadjustment.value < self.yadjustment.range):
- redraw = True
- if (self.edge_yspeed < 0) and (self.yadjustment.value > 0):
- redraw = True
-
- if redraw:
- renpy.display.render.redraw(self, 0)
-
-
- def event(self, ev, x, y, st):
-
- self.xoffset = None
- self.yoffset = None
-
- rv = super(Viewport, self).event(ev, x, y, st)
- if rv is not None:
- return rv
-
- if self.draggable and renpy.display.focus.get_grab() == self:
-
- oldx, oldy = self.drag_position
- dx = x - oldx
- dy = y - oldy
-
- self.xadjustment.change(self.xadjustment.value - dx)
- self.yadjustment.change(self.yadjustment.value - dy)
-
- self.drag_position = (x, y) # W0201
-
- if renpy.display.behavior.map_event(ev, 'viewport_drag_end'):
- renpy.display.focus.set_grab(None)
- raise renpy.display.core.IgnoreEvent()
-
- if not ((0 <= x < self.width) and (0 <= y <= self.height)):
- return
-
- if self.mousewheel:
-
- if renpy.display.behavior.map_event(ev, 'viewport_up'):
- rv = self.yadjustment.change(self.yadjustment.value - self.yadjustment.step)
- if rv is not None:
- return rv
- else:
- raise renpy.display.core.IgnoreEvent()
-
- if renpy.display.behavior.map_event(ev, 'viewport_down'):
- rv = self.yadjustment.change(self.yadjustment.value + self.yadjustment.step)
- if rv is not None:
- return rv
- else:
- raise renpy.display.core.IgnoreEvent()
-
- if self.draggable:
-
- if renpy.display.behavior.map_event(ev, 'viewport_drag_start'):
- self.drag_position = (x, y)
- renpy.display.focus.set_grab(self)
- raise renpy.display.core.IgnoreEvent()
-
- if self.edge_size:
-
- def speed(n, zero, one):
- """
- Given a position `n`, computes the speed. The speed is 0.0
- when `n` == `zero`, 1.0 when `n` == `one`, and linearly
- interpolated when between.
-
- Returns 0.0 when outside the bounds - in either direction.
- """
-
- n = 1.0 * (n - zero) / (one - zero)
- if n < 0.0:
- return 0.0
- if n > 1.0:
- return 0.0
-
- return n
-
- xspeed = speed(x, self.width - self.edge_size, self.width)
- xspeed -= speed(x, self.edge_size, 0)
- self.edge_xspeed = self.edge_speed * self.edge_function(xspeed)
-
- yspeed = speed(y, self.height - self.edge_size, self.height)
- yspeed -= speed(y, self.edge_size, 0)
- self.edge_yspeed = self.edge_speed * self.edge_function(yspeed)
-
- if xspeed or yspeed:
- self.check_edge_redraw()
- if self.edge_last_st is None:
- self.edge_last_st = st
- else:
- self.edge_last_st = None
-
- return None
-
- def set_xoffset(self, offset):
- self.xoffset = offset
- renpy.display.render.redraw(self, 0)
-
- def set_yoffset(self, offset):
- self.yoffset = offset
- renpy.display.render.redraw(self, 0)
-
-def LiveCrop(rect, child, **properties):
- """
- :doc: disp_imagelike
-
- This created a displayable by cropping `child` to `rect`, where
- `rect` is an (x, y, width, height) tuple. ::
-
- image eileen cropped = LiveCrop((0, 0, 300, 300), "eileen happy")
- """
-
- x, y, w, h = rect
-
- return Viewport(child, offsets=(x, y), xmaximum=w, ymaximum=h, **properties)
-
-class Side(Container):
-
- possible_positions = set([ 'tl', 't', 'tr', 'r', 'br', 'b', 'bl', 'l', 'c'])
-
- def after_setstate(self):
- self.sized = False
-
- def __init__(self, positions, style='side', **properties):
-
- super(Side, self).__init__(style=style, **properties)
-
- if isinstance(positions, str):
- positions = positions.split()
-
- for i in positions:
- if not i in Side.possible_positions:
- raise Exception("Side used with impossible position '%s'." % (i,))
-
- self.positions = tuple(positions)
- self.sized = False
-
- def render(self, width, height, st, at):
-
- pos_d = { }
- pos_i = { }
-
- for i, (pos, d) in enumerate(zip(self.positions, self.children)):
- pos_d[pos] = d
- pos_i[pos] = i
-
- # Figure out the size of each widget (and hence where the
- # widget needs to be placed).
-
- if not self.sized:
- self.sized = True
-
- # Deal with various spacings.
- spacing = self.style.spacing
-
- def spacer(a, b, c, axis):
- if (a in pos_d) or (b in pos_d) or (c in pos_d):
- return spacing, axis - spacing
- else:
- return 0, axis
-
- self.left_space, width = spacer('tl', 'l', 'bl', width) # W0201
- self.right_space, width = spacer('tr', 'r', 'br', width) # W0201
- self.top_space, height = spacer('tl', 't', 'tr', height) # W0201
- self.bottom_space, height = spacer('bl', 'b', 'br', height) # W0201
-
- # The sizes of the various borders.
- left = 0
- right = 0
- top = 0
- bottom = 0
- cwidth = 0
- cheight = 0
-
- def sizeit(pos, width, height, owidth, oheight):
- if pos not in pos_d:
- return owidth, oheight
-
- rend = render(pos_d[pos], width, height, st, at)
- rv = max(owidth, rend.width), max(oheight, rend.height)
- rend.kill()
- return rv
-
- cwidth, cheight = sizeit('c', width, height, 0, 0)
- cwidth, top = sizeit('t', cwidth, height, cwidth, top)
- cwidth, bottom = sizeit('b', cwidth, height, cwidth, bottom)
- left, cheight = sizeit('l', width, cheight, left, cheight)
- right, cheight = sizeit('r', width, cheight, right, cheight)
-
- left, top = sizeit('tl', left, top, left, top)
- left, bottom = sizeit('bl', left, bottom, left, bottom)
- right, top = sizeit('tr', right, top, right, top)
- right, bottom = sizeit('br', right, bottom, right, bottom)
-
- self.cwidth = cwidth # W0201
- self.cheight = cheight # W0201
-
- self.top = top # W0201
- self.bottom = bottom # W0201
- self.left = left # W0201
- self.right = right # W0201
-
- else:
- cwidth = self.cwidth
- cheight = self.cheight
- top = self.top
- bottom = self.bottom
- left = self.left
- right = self.right
-
- # Now, place everything onto the render.
-
- self.offsets = [ None ] * len(self.children)
-
- lefts = self.left_space
- rights = self.right_space
- tops = self.top_space
- bottoms = self.bottom_space
-
-
- cwidth = min(cwidth, width - left - lefts - right - rights)
- cheight = min(cheight, height - top - tops - bottom - bottoms)
-
- rv = renpy.display.render.Render(left + lefts + cwidth + rights + right,
- top + tops + cheight + bottoms + bottom)
-
- def place(pos, x, y, w, h):
-
- if pos not in pos_d:
- return
-
- d = pos_d[pos]
- i = pos_i[pos]
- rend = render(d, w, h, st, at)
- self.offsets[i] = pos_d[pos].place(rv, x, y, w, h, rend)
-
- col1 = 0
- col2 = left + lefts
- col3 = left + lefts + cwidth + rights
-
- row1 = 0
- row2 = top + tops
- row3 = top + tops + cheight + bottoms
-
- place('c', col2, row2, cwidth, cheight)
-
- place('t', col2, row1, cwidth, top)
- place('r', col3, row2, right, cheight)
- place('b', col2, row3, cwidth, bottom)
- place('l', col1, row2, left, cheight)
-
- place('tl', col1, row1, left, top)
- place('tr', col3, row1, right, top)
- place('br', col3, row3, right, bottom)
- place('bl', col1, row3, left, bottom)
-
- return rv
-
-class Alpha(renpy.display.core.Displayable):
- def __init__(self, start, end, time, child=None, repeat=False, bounce=False,
- anim_timebase=False, time_warp=None, **properties):
-
- super(Alpha, self).__init__(**properties)
-
- self.start = start
- self.end = end
- self.time = time
- self.child = renpy.easy.displayable(child)
- self.repeat = repeat
- self.anim_timebase = anim_timebase
- self.time_warp = time_warp
-
- def visit(self):
- return [ self.child ]
-
- def render(self, height, width, st, at):
- if self.anim_timebase:
- t = at
- else:
- t = st
-
- if self.time:
- done = min(t / self.time, 1.0)
- else:
- done = 1.0
-
- if renpy.game.less_updates:
- done = 1.0
- elif self.repeat:
- done = done % 1.0
- renpy.display.render.redraw(self, 0)
- elif done != 1.0:
- renpy.display.render.redraw(self, 0)
-
- if self.time_warp:
- done = self.time_warp(done)
-
- alpha = self.start + done * (self.end - self.start)
-
- rend = renpy.display.render.render(self.child, height, width, st, at)
-
- w, h = rend.get_size()
- rv = renpy.display.render.Render(w, h)
- rv.blit(rend, (0, 0))
- rv.alpha = alpha
-
- return rv
-
-
-class AdjustTimes(Container):
-
- def __init__(self, child, start_time, anim_time, **properties):
- super(AdjustTimes, self).__init__(**properties)
-
- self.start_time = start_time
- self.anim_time = anim_time
-
- self.add(child)
-
- def render(self, w, h, st, at):
-
- if self.start_time is None:
- self.start_time = renpy.game.interface.frame_time
-
- if self.anim_time is None:
- self.anim_time = renpy.game.interface.frame_time
-
- st = renpy.game.interface.frame_time - self.start_time
- at = renpy.game.interface.frame_time - self.anim_time
-
- cr = renpy.display.render.render(self.child, w, h, st, at)
- cw, ch = cr.get_size()
- rv = renpy.display.render.Render(cw, ch)
- rv.blit(cr, (0, 0))
-
- self.offsets = [ (0, 0) ]
-
- return rv
-
- def get_placement(self):
- return self.child.get_placement()
-
-
-class LiveTile(Container):
- """
- :doc: disp_imagelike
-
- Tiles `child` until it fills the area allocated to this displayable.
-
- ::
-
- image bg tile = LiveTile("bg.png")
-
- """
-
- def __init__(self, child, style='tile', **properties):
- super(LiveTile, self).__init__(style=style, **properties)
-
- self.add(child)
-
- def render(self, width, height, st, at):
-
- cr = renpy.display.render.render(self.child, width, height, st, at)
- cw, ch = cr.get_size()
- rv = renpy.display.render.Render(width, height)
-
- width = int(width)
- height = int(height)
- cw = int(cw)
- ch = int(ch)
-
- for y in range(0, height, ch):
- for x in range(0, width, cw):
- rv.blit(cr, (x, y), focus=False)
-
- return rv
diff --git a/unrpyc/renpy/display/minigame.py b/unrpyc/renpy/display/minigame.py
deleted file mode 100644
index 7469922..0000000
--- a/unrpyc/renpy/display/minigame.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2004-2013 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.
-
-
-def Minigame(*args, **kwargs):
- raise Exception("Minigame is no longer implemented.")
-
diff --git a/unrpyc/renpy/display/module.py b/unrpyc/renpy/display/module.py
deleted file mode 100644
index 1bf66e0..0000000
--- a/unrpyc/renpy/display/module.py
+++ /dev/null
@@ -1,275 +0,0 @@
-# Copyright 2004-2013 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.
-
-# This file mediates access to the _renpy module, which is a C module that
-# allows us to enhance the feature set of pygame in a renpy specific way.
-
-VERSION = (6, 12, 0)
-
-import renpy.display
-import pygame; pygame # prevents pyflakes warning.
-
-import sys
-
-try:
- import _renpy
- version = _renpy.version()
-
- if version != VERSION:
- print("Found Ren'Py module version %s, while expecting %s." % (
- ".".join(str(i) for i in version),
- ".".join(str(i) for i in VERSION),
- ))
-
- print("Trying to run anyway, but you should expect errors.", file=sys.stderr)
-
-except:
- print("The _renpy module was not found. Please read module/README.txt for", file=sys.stderr)
- print("more information.", file=sys.stderr)
-
- sys.exit(-1)
-
-def convert_and_call(function, src, dst, *args):
- """
- This calls the function with the source and destination
- surface. The surfaces must have the same alpha.
-
- If the surfaces are not 24 or 32 bits per pixel, or don't have the
- same format, they are converted and then converted back.
- """
-
- # Now that all surfaces are 32bpp, this function doesn't do much
- # of anything anymore.
-
- if (dst.get_masks()[3] != 0) != (src.get_masks()[3] != 0):
- raise Exception("Surface alphas do not match.")
-
- function(src, dst, *args)
-
-
-def pixellate(src, dst, avgwidth, avgheight, outwidth, outheight):
- """
- This pixellates the source surface. First, every pixel in the
- source surface is projected onto a virtual surface, such that
- the average value of every avgwidth x avgheight pixels becomes
- one virtual pixel. It then gets projected back onto the
- destination surface at a ratio of one virtual pixel to every
- outwidth x outheight destination pixels.
-
- If either src or dst is not a 24 or 32 bit surface, they are
- converted... but that may be a significant performance hit.
-
- The two surfaces must either have the same alpha or no alpha.
- """
-
- convert_and_call(_renpy.pixellate,
- src, dst,
- avgwidth, avgheight,
- outwidth, outheight)
-
-
-def scale(s, size):
- """
- Scales down the supplied pygame surface by the given X and Y
- factors.
-
- Always works, but may not be high quality.
- """
-
- d = renpy.display.pgrender.surface(size, True)
-
- bilinear_scale(s, d)
-
- return d
-
-
-# What we have here are a pair of tables mapping masks to byte offsets
-# for 24 and 32 bpp modes. We represent 0xff000000 as positive and negative
-# numbers so that it doesn't yield a warning, and so that it works on
-# 32 and 64 bit platforms.
-if sys.byteorder == 'big':
- bo32 = { 255 : 3, 65280 : 2, 16711680 : 1, 4278190080 : 0, -16777216 : 0, }
-else:
- bo32 = { 255 : 0, 65280 : 1, 16711680 : 2, 4278190080 : 3, -16777216 : 3, }
-
-bo_cache = None
-
-def byte_offset(src):
- """
- Given the surface src, returns a 4-tuple giving the byte offsets
- for the red, green, blue, and alpha components of the pixels in
- the surface. If a component doesn't exist, None is returned.
- """
-
- global bo_cache
-
- if bo_cache is None:
- bo_cache = [ bo32[i] for i in src.get_masks() ]
-
- return bo_cache
-
-def endian_order(src, r, g, b, a):
-
- if bo_cache is None:
- byte_offset(src)
-
- rv = [ a, a, a, a ]
-
- for i, index_i in zip((r, g, b, a), bo_cache):
- rv[index_i] = i
-
- return rv
-
-
-
-def linmap(src, dst, rmap, gmap, bmap, amap):
- """
- This maps the colors between two surfaces. The various map
- parameters should be fixed-point integers, with 1.0 == 256.
- """
-
- convert_and_call(_renpy.linmap,
- src, dst,
- *endian_order(dst, rmap, gmap, bmap, amap))
-
-
-save_png = _renpy.save_png
-
-def map(src, dst, rmap, gmap, bmap, amap): #@ReservedAssignment
- """
- This maps the colors between two surfaces. The various map
- parameters must be 256 character long strings, with the value
- of a character at a given offset being what a particular pixel
- component value is mapped to.
- """
-
- convert_and_call(_renpy.map,
- src, dst,
- *endian_order(dst, rmap, gmap, bmap, amap))
-
-
-
-def twomap(src, dst, white, black):
- """
- Given colors for white and black, linearly maps things
- appropriately, taking the alpha channel from white.
- """
-
- wr = white[0]
- wg = white[1]
- wb = white[2]
- wa = white[3]
-
- br = black[0]
- bg = black[1]
- bb = black[2]
-
- ramp = renpy.display.im.ramp
-
- if br == 0 and bg == 0 and bb == 0:
- linmap(src, dst,
- wr + 1,
- wg + 1,
- wb + 1,
- wa + 1)
- else:
- list(map(src, dst,
- ramp(br, wr),
- ramp(bg, wg),
- ramp(bb, wb),
- ramp(0, wa)))
-
-
-def alpha_munge(src, dst, amap):
- """
- This samples the red channel from src, maps it through amap, and
- place it into the alpha channel of amap.
- """
-
- if src.get_size() != dst.get_size():
- return
-
- red = byte_offset(src)[0]
- alpha = byte_offset(dst)[3]
-
- if red is not None and alpha is not None:
- _renpy.alpha_munge(src, dst, red, alpha, amap)
-
-
-def bilinear_scale(src, dst, sx=0, sy=0, sw=None, sh=None, dx=0, dy=0, dw=None, dh=None, precise=0):
-
- if sw is None:
- sw, sh = src.get_size()
- if dw is None:
- dw, dh = dst.get_size()
-
- while True:
-
- if sw <= dw * 2 and sh <= dh * 2:
- break
-
- nsw = max(sw / 2, dw)
- nsh = max(sh / 2, dh)
-
- nsrc = renpy.display.pgrender.surface((nsw, nsh), src.get_masks()[3])
-
- _renpy.bilinear(src, nsrc, sx, sy, sw, sh, precise=precise)
-
- sx = 0
- sy = 0
- sw = nsw
- sh = nsh
- src = nsrc
-
- _renpy.bilinear(src, dst, sx, sy, sw, sh, dx, dy, dw, dh, precise=precise)
-
-
-transform = _renpy.transform
-
-# Note: Blend requires all surfaces to be the same size.
-blend = _renpy.blend
-
-def imageblend(a, b, dst, img, amap):
- alpha = byte_offset(img)[3]
- _renpy.imageblend(a, b, dst, img, alpha, amap)
-
-
-def colormatrix(src, dst, matrix):
- c = [ matrix[0:5], matrix[5:10], matrix[10:15], matrix[15:20] ]
- offs = byte_offset(src)
-
- o = [ None ] * 4
- for i in range(0, 4):
- o[offs[i]] = i
-
- _renpy.colormatrix(src, dst,
- c[o[0]][o[0]], c[o[0]][o[1]], c[o[0]][o[2]], c[o[0]][o[3]], c[o[0]][4],
- c[o[1]][o[0]], c[o[1]][o[1]], c[o[1]][o[2]], c[o[1]][o[3]], c[o[1]][4],
- c[o[2]][o[0]], c[o[2]][o[1]], c[o[2]][o[2]], c[o[2]][o[3]], c[o[2]][4],
- c[o[3]][o[0]], c[o[3]][o[1]], c[o[3]][o[2]], c[o[3]][o[3]], c[o[3]][4])
-
-
-def subpixel(src, dst, x, y):
-
- shift = src.get_shifts()[3]
- _renpy.subpixel(src, dst, x, y, shift)
-
-
diff --git a/unrpyc/renpy/display/motion.py b/unrpyc/renpy/display/motion.py
deleted file mode 100644
index 1c9d667..0000000
--- a/unrpyc/renpy/display/motion.py
+++ /dev/null
@@ -1,1526 +0,0 @@
-# Copyright 2004-2013 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.
-
-# This file contains displayables that move, zoom, rotate, or otherwise
-# transform displayables. (As well as displayables that support them.)
-import math
-import types #@UnresolvedImport
-
-import renpy.display #@UnusedImport
-from renpy.display.render import render
-from renpy.display.layout import Container
-
-import renpy.display.accelerator
-
-# The null object that's used if we don't have a defined child.
-null = None
-
-def get_null():
- global null
-
- if null is None:
- null = renpy.display.layout.Null()
-
- return null
-
-# Convert a position from cartesian to polar coordinates.
-def cartesian_to_polar(x, y, xaround, yaround):
- """
- Converts cartesian coordinates to polar coordinates.
- """
-
- dx = x - xaround
- dy = y - yaround
-
- radius = math.hypot(dx, dy)
- angle = math.atan2(dx, -dy) / math.pi * 180
-
- if angle < 0:
- angle += 360
-
- return angle, radius
-
-def polar_to_cartesian(angle, radius, xaround, yaround):
- """
- Converts polart coordinates to cartesian coordinates.
- """
-
- angle = angle * math.pi / 180
-
- dx = radius * math.sin(angle)
- dy = -radius * math.cos(angle)
-
- x = type(xaround)(xaround + dx)
- y = type(yaround)(yaround + dy)
-
- return x, y
-
-def first_not_none(*args):
- """
- Returns the first argument that is not None.
- """
-
- for i in args:
- if i is not None:
- return i
- return i
-
-
-class TransformState(renpy.object.Object):
-
- xoffset = None
- yoffset = None
- default_xpos = None
- default_ypos = None
- default_xanchor = None
- default_yanchor = None
- default_xoffset = None
- default_yoffset = None
- transform_anchor = False
-
- def __init__(self): # W0231
- self.alpha = 1
- self.rotate = None
- self.rotate_pad = True
- self.transform_anchor = False
- self.zoom = 1
- self.xzoom = 1
- self.yzoom = 1
-
- self.xpos = None
- self.ypos = None
- self.xanchor = None
- self.yanchor = None
- self.xoffset = 0
- self.yoffset = 0
-
- self.xaround = 0.0
- self.yaround = 0.0
- self.xanchoraround = 0.0
- self.yanchoraround = 0.0
-
- self.subpixel = False
-
- self.crop = None
- self.corner1 = None
- self.corner2 = None
- self.size = None
-
- self.delay = 0
-
- # Note: When adding a new property, we need to add it to:
- # - take_state
- # - diff
- # - renpy.atl.PROPERTIES
- # - Proxies in Transform
-
- # Default values for various properties, taken from our
- # parent.
- self.default_xpos = None
- self.default_ypos = None
- self.default_xanchor = None
- self.default_yanchor = None
-
- def take_state(self, ts):
-
- self.alpha = ts.alpha
- self.rotate = ts.rotate
- self.rotate_pad = ts.rotate_pad
- self.transform_anchor = ts.transform_anchor
- self.zoom = ts.zoom
- self.xzoom = ts.xzoom
- self.yzoom = ts.yzoom
-
- self.xaround = ts.xaround
- self.yaround = ts.yaround
- self.xanchoraround = ts.xanchoraround
- self.yanchoraround = ts.yanchoraround
-
- self.subpixel = ts.subpixel
-
- self.crop = ts.crop
- self.corner1 = ts.corner1
- self.corner2 = ts.corner2
- self.size = ts.size
-
- # Take the computed position properties, not the
- # raw ones.
- (self.default_xpos,
- self.default_ypos,
- self.default_xanchor,
- self.default_yanchor,
- self.xoffset,
- self.yoffset,
- self.subpixel) = ts.get_placement()
-
- # Returns a dict, with p -> (old, new) where p is a property that
- # has changed between this object and the new object.
- def diff(self, newts):
-
- rv = { }
-
- def diff2(prop, new, old):
- if new != old:
- rv[prop] = (old, new)
-
- def diff4(prop, new, default_new, old, default_old):
- if new is None:
- new_value = default_new
- else:
- new_value = new
-
- if old is None:
- old_value = default_old
- else:
- old_value = old
-
- if new_value != old_value:
- rv[prop] = (old_value, new_value)
-
- diff2("alpha", newts.alpha, self.alpha)
- diff2("rotate", newts.rotate, self.rotate)
- diff2("rotate_pad", newts.rotate_pad, self.rotate_pad)
- diff2("transform_anchor", newts.transform_anchor, self.transform_anchor)
- diff2("zoom", newts.zoom, self.zoom)
- diff2("xzoom", newts.xzoom, self.xzoom)
- diff2("yzoom", newts.yzoom, self.yzoom)
-
- diff2("xaround", newts.xaround, self.xaround)
- diff2("yaround", newts.yaround, self.yaround)
- diff2("xanchoraround", newts.xanchoraround, self.xanchoraround)
- diff2("yanchoraround", newts.yanchoraround, self.yanchoraround)
-
- diff2("subpixel", newts.subpixel, self.subpixel)
-
- diff2("crop", newts.crop, self.crop)
- diff2("corner1", newts.corner1, self.corner1)
- diff2("corner2", newts.corner2, self.corner2)
- diff2("size", newts.size, self.size)
-
- diff4("xpos", newts.xpos, newts.default_xpos, self.xpos, self.default_xpos)
-
- diff4("xanchor", newts.xanchor, newts.default_xanchor, self.xanchor, self.default_xanchor)
- diff2("xoffset", newts.xoffset, self.xoffset)
-
- diff4("ypos", newts.ypos, newts.default_ypos, self.ypos, self.default_ypos)
- diff4("yanchor", newts.yanchor, newts.default_yanchor, self.yanchor, self.default_yanchor)
- diff2("yoffset", newts.yoffset, self.yoffset)
-
- return rv
-
- def get_placement(self, cxoffset=0, cyoffset=0):
-
- return (
- first_not_none(self.xpos, self.default_xpos),
- first_not_none(self.ypos, self.default_ypos),
- first_not_none(self.xanchor, self.default_xanchor),
- first_not_none(self.yanchor, self.default_yanchor),
- self.xoffset + cxoffset,
- self.yoffset + cyoffset,
- self.subpixel,
- )
-
- # These update various properties.
- def get_xalign(self):
- return self.xpos
-
- def set_xalign(self, v):
- self.xpos = v
- self.xanchor = v
-
- xalign = property(get_xalign, set_xalign)
-
- def get_yalign(self):
- return self.ypos
-
- def set_yalign(self, v):
- self.ypos = v
- self.yanchor = v
-
- yalign = property(get_yalign, set_yalign)
-
- def get_around(self):
- return (self.xaround, self.yaround)
-
- def set_around(self, value):
- self.xaround, self.yaround = value
- self.xanchoraround, self.yanchoraround = None, None
-
- def set_alignaround(self, value):
- self.xaround, self.yaround = value
- self.xanchoraround, self.yanchoraround = value
-
- around = property(get_around, set_around)
- alignaround = property(get_around, set_alignaround)
-
- def get_angle(self):
- xpos = first_not_none(self.xpos, self.default_xpos, 0)
- ypos = first_not_none(self.ypos, self.default_ypos, 0)
- angle, _radius = cartesian_to_polar(xpos, ypos, self.xaround, self.yaround)
- return angle
-
- def get_radius(self):
- xpos = first_not_none(self.xpos, self.default_xpos, 0)
- ypos = first_not_none(self.ypos, self.default_ypos, 0)
- _angle, radius = cartesian_to_polar(xpos, ypos, self.xaround, self.yaround)
- return radius
-
- def set_angle(self, value):
- xpos = first_not_none(self.xpos, self.default_xpos, 0)
- ypos = first_not_none(self.ypos, self.default_ypos, 0)
- _angle, radius = cartesian_to_polar(xpos, ypos, self.xaround, self.yaround)
- angle = value
- self.xpos, self.ypos = polar_to_cartesian(angle, radius, self.xaround, self.yaround)
-
- if self.xanchoraround:
- self.xanchor, self.yanchor = polar_to_cartesian(angle, radius, self.xaround, self.yaround)
-
- def set_radius(self, value):
- xpos = first_not_none(self.xpos, self.default_xpos, 0)
- ypos = first_not_none(self.ypos, self.default_ypos, 0)
- angle, _radius = cartesian_to_polar(xpos, ypos, self.xaround, self.yaround)
- radius = value
- self.xpos, self.ypos = polar_to_cartesian(angle, radius, self.xaround, self.yaround)
-
- if self.xanchoraround:
- self.xanchor, self.yanchor = polar_to_cartesian(angle, radius, self.xaround, self.yaround)
-
- angle = property(get_angle, set_angle)
- radius = property(get_radius, set_radius)
-
- def get_pos(self):
- return self.xpos, self.ypos
-
- def set_pos(self, value):
- self.xpos, self.ypos = value
-
- pos = property(get_pos, set_pos)
-
- def get_anchor(self):
- return self.xanchor, self.yanchor
-
- def set_anchor(self, value):
- self.xanchor, self.yanchor = value
-
- anchor = property(get_anchor, set_anchor)
-
- def get_align(self):
- return self.xpos, self.ypos
-
- def set_align(self, value):
- self.xanchor, self.yanchor = value
- self.xpos, self.ypos = value
-
- align = property(get_align, set_align)
-
- def get_offset(self):
- return self.xoffset, self.yoffset
-
- def set_offset(self, value):
- self.xoffset, self.yoffset = value
-
- offset = property(get_offset, set_offset)
-
- def set_xcenter(self, value):
- self.xpos = value
- self.xanchor = 0.5
-
- def get_xcenter(self):
- return self.xpos
-
- def set_ycenter(self, value):
- self.ypos = value
- self.yanchor = 0.5
-
- def get_ycenter(self):
- return self.ypos
-
- xcenter = property(get_xcenter, set_xcenter)
- ycenter = property(get_ycenter, set_ycenter)
-
-class Proxy(object):
- """
- This class proxies a field from the transform to its state.
- """
-
- def __init__(self, name):
- self.name = name
-
- def __get__(self, instance, owner):
- return getattr(instance.state, self.name)
-
- def __set__(self, instance, value):
- return setattr(instance.state, self.name, value)
-
-class Transform(Container):
- """
- Documented in sphinx, because we can't scan this object.
- """
-
- __version__ = 5
- transform_event_responder = True
-
- # Proxying things over to our state.
- alpha = Proxy("alpha")
- rotate = Proxy("rotate")
- rotate_pad = Proxy("rotate_pad")
- transform_anchor = Proxy("rotate_pad")
- zoom = Proxy("zoom")
- xzoom = Proxy("xzoom")
- yzoom = Proxy("yzoom")
-
- xpos = Proxy("xpos")
- ypos = Proxy("ypos")
- xanchor = Proxy("xanchor")
- yanchor = Proxy("yanchor")
-
- xalign = Proxy("xalign")
- yalign = Proxy("yalign")
-
- around = Proxy("around")
- alignaround = Proxy("alignaround")
- angle = Proxy("angle")
- radius = Proxy("radius")
-
- xaround = Proxy("xaround")
- yaround = Proxy("yaround")
- xanchoraround = Proxy("xanchoraround")
- yanchoraround = Proxy("yanchoraround")
-
- pos = Proxy("pos")
- anchor = Proxy("anchor")
- align = Proxy("align")
-
- crop = Proxy("crop")
- corner1 = Proxy("corner1")
- corner2 = Proxy("corner2")
- size = Proxy("size")
-
- delay = Proxy("delay")
-
- xoffset = Proxy("xoffset")
- yoffset = Proxy("yoffset")
- offset = Proxy("offset")
-
- subpixel = Proxy("subpixel")
-
- xcenter = Proxy("xcenter")
- ycenter = Proxy("ycenter")
-
- def after_upgrade(self, version):
-
- if version < 1:
- self.active = False
- self.state = TransformState()
-
- self.state.xpos = self.xpos or 0
- self.state.ypos = self.ypos or 0
- self.state.xanchor = self.xanchor or 0
- self.state.yanchor = self.yanchor or 0
- self.state.alpha = self.alpha
- self.state.rotate = self.rotate
- self.state.zoom = self.zoom
- self.state.xzoom = self.xzoom
- self.state.yzoom = self.yzoom
-
- self.hide_request = False
- self.hide_response = True
-
- if version < 2:
- self.st = 0
- self.at = 0
-
- if version < 3:
- self.st_offset = 0
- self.at_offset = 0
- self.child_st_base = 0
-
- if version < 4:
- self.style_arg = 'transform'
-
- if version < 5:
- self.replaced_request = False
- self.replaced_response = True
-
- DEFAULT_ARGUMENTS = {
- "selected_activate" : { },
- "selected_hover" : { },
- "selected_idle" : { },
- "selected_insensitive" : { },
- "activate" : { },
- "hover" : { },
- "idle" : { },
- "insensitive" : { },
- "" : { },
- }
-
- # Compatibility with old versions of the class.
- active = False
- children = False
- arguments = DEFAULT_ARGUMENTS
-
- def __init__(self,
- child=None,
- function=None,
-
- style='transform',
- focus=None,
- default=False,
-
- **kwargs):
-
- self.kwargs = kwargs
- self.style_arg = style
-
- super(Transform, self).__init__(style=style, focus=focus, default=default)
-
- self.function = function
-
- child = renpy.easy.displayable_or_none(child)
- if child is not None:
- self.add(child)
-
- self.state = TransformState()
-
- self.arguments = dict((k, {}) for k in self.DEFAULT_ARGUMENTS)
-
- # Split up the keyword arguments.
- for k, v in kwargs.items():
- if "_" in k:
- prefix, prop = k.rsplit("_", 1)
- else:
- prefix = ""
- prop = k
-
- if prefix not in self.arguments:
- raise Exception("Unknown transform property prefix: %r" % prefix)
-
- if prop not in renpy.atl.PROPERTIES:
- raise Exception("Unknown transform property: %r")
-
- self.arguments[prefix][prop] = v
-
-
- # Apply the keyword arguments.
- for k, v in kwargs.items():
- setattr(self.state, k, v)
-
- # This is the matrix transforming our coordinates into child coordinates.
- self.forward = None
-
- # Have we called the function at least once?
- self.active = False
-
- # Have we been requested to hide?
- self.hide_request = False
-
- # True if it's okay for us to hide.
- self.hide_response = True
-
- # Have we been requested to replaced?
- self.replaced_request = False
-
- # True if it's okay for us to replaced.
- self.replaced_response = True
-
- self.st = 0
- self.at = 0
- self.st_offset = 0
- self.at_offset = 0
-
- self.child_st_base = 0
-
- def visit(self):
- if self.child is None:
- return [ ]
- else:
- return [ self.child ]
-
- # The default function chooses entries from self.arguments that match
- # the style prefix, and applies them to the state.
- def default_function(self, state, st, at):
-
- prefix = self.style.prefix.strip("_")
- prefixes = [ ]
-
- while prefix:
- prefixes.insert(0, prefix)
- _, _, prefix = prefix.partition("_")
-
- prefixes.insert(0, "")
-
- for i in prefixes:
- for k, v in self.arguments[i].items():
- setattr(state, k, v)
-
- return None
-
- def set_transform_event(self, event):
- if self.child is not None:
- self.child.set_transform_event(event)
-
- super(Transform, self).set_transform_event(event)
-
-
- def take_state(self, t):
- """
- Takes the transformation state from object t into this object.
- """
-
- self.state.take_state(t.state)
-
- # The arguments will be applied when the default function is
- # called.
-
-
- def take_execution_state(self, t):
- """
- Takes the execution state from object t into this object. This is
- overridden by renpy.atl.TransformBase.
- """
-
- self.hide_request = t.hide_request
- self.replaced_request = t.replaced_request
-
- self.state.xpos = t.state.xpos
- self.state.ypos = t.state.ypos
- self.state.xanchor = t.state.xanchor
- self.state.yanchor = t.state.yanchor
-
- if isinstance(self.child, Transform) and isinstance(t.child, Transform):
- self.child.take_execution_state(t.child)
-
-
- def copy(self):
- """
- Makes a copy of this transform.
- """
-
- d = self()
- d.kwargs = { }
- d.take_state(self)
- d.take_execution_state(self)
- d.st = self.st
- d.at = self.at
-
- return d
-
- def _change_transform_child(self, child):
- rv = self.copy()
-
- if self.child is not None:
- rv.set_child(self.child._change_transform_child(child))
-
- return rv
-
- def _hide(self, st, at, kind):
-
- if not self.child:
- return None
-
- if not (self.hide_request or self.replaced_request):
- d = self.copy()
- else:
- d = self
-
- d.st_offset = self.st_offset
- d.at_offset = self.at_offset
-
- if kind == "hide":
- d.hide_request = True
- else:
- d.replaced_request = True
-
- d.hide_response = True
- d.replaced_response = True
-
- if d.function is not None:
- d.function(d, st + d.st_offset, at + d.at_offset)
-
- new_child = d.child._hide(st, at, kind)
-
- if new_child is not None:
- d.child = new_child
- d.hide_response = False
- d.replaced_response = False
-
- if (not d.hide_response) or (not d.replaced_response):
- renpy.display.render.redraw(d, 0)
- return d
-
- return None
-
- def set_child(self, child):
-
- child = renpy.easy.displayable(child)
-
- self.child = child
- self.child_st_base = self.st
-
- child.per_interact()
-
- renpy.display.render.redraw(self, 0)
-
- def update_state(self):
- """
- This updates the state to that at self.st, self.at.
- """
-
- # If we have to, call the function that updates this transform.
- if self.function is not None:
- fr = self.function(self, self.st, self.at)
- else:
- fr = self.default_function(self, self.st, self.at)
-
- # Order a redraw, if necessary.
- if fr is not None:
- renpy.display.render.redraw(self, fr)
-
- state = self.state
-
- self.active = True
-
- # Use non-None elements of the child placement as defaults.
- child = self.child
- if child is not None and renpy.config.transform_uses_child_position:
-
- pos = child.get_placement()
-
- if pos[0] is not None:
- state.default_xpos = pos[0]
- if pos[2] is not None:
- state.default_xanchor = pos[2]
- if pos[1] is not None:
- state.default_ypos = pos[1]
- if pos[3] is not None:
- state.default_yanchor = pos[3]
-
- state.subpixel |= pos[6]
-
- # The render method is now defined in accelerator.pyx.
-
- def event(self, ev, x, y, st):
-
- if self.hide_request:
- return None
-
- children = self.children
- offsets = self.offsets
-
- if not offsets:
- return None
-
- for i in range(len(self.children)-1, -1, -1):
-
- d = children[i]
- xo, yo = offsets[i]
-
- cx = x - xo
- cy = y - yo
-
- # Transform screen coordinates to child coordinates.
- cx, cy = self.forward.transform(cx, cy)
-
- rv = d.event(ev, cx, cy, st)
- if rv is not None:
- return rv
-
- return None
-
- def __call__(self, child=None, take_state=True):
-
- if child is None:
- child = self.child
-
- # If we don't have a child for some reason, set it to null.
- if child is None:
- child = get_null()
-
- rv = Transform(
- child=child,
- function=self.function,
- style=self.style_arg,
- **self.kwargs)
-
- rv.take_state(self)
-
- return rv
-
- def get_placement(self):
-
- if not self.active:
- self.update_state()
-
- if self.child is not None:
- _cxpos, _cypos, _cxanchor, _cyanchor, cxoffset, cyoffset, _csubpixel = self.child.get_placement()
- else:
- cxoffset = 0
- cyoffset = 0
-
- cxoffset = cxoffset or 0
- cyoffset = cyoffset or 0
-
- rv = self.state.get_placement(cxoffset, cyoffset)
-
- if self.state.transform_anchor:
-
- xpos, ypos, xanchor, yanchor, xoffset, yoffset, subpixel = rv
- if (xanchor is not None) and (yanchor is not None):
-
- cw, ch = self.child_size
- rw, rh = self.render_size
-
- if isinstance(xanchor, float):
- xanchor *= cw
- if isinstance(yanchor, float):
- yanchor *= ch
-
- xanchor -= cw / 2.0
- yanchor -= ch / 2.0
-
- xanchor, yanchor = self.reverse.transform(xanchor, yanchor)
-
- xanchor += rw / 2.0
- yanchor += rh / 2.0
-
- xanchor = renpy.display.core.absolute(xanchor)
- yanchor = renpy.display.core.absolute(yanchor)
-
- rv = (xpos, ypos, xanchor, yanchor, xoffset, yoffset, subpixel)
-
- return rv
-
- def update(self):
- """
- This should be called when a transform property field is updated outside
- of the callback method, to ensure that the change takes effect.
- """
-
- renpy.display.render.invalidate(self)
-
- def parameterize(self, name, parameters):
- if parameters:
- raise Exception("Image '%s' can't take parameters '%s'. (Perhaps you got the name wrong?)" %
- (' '.join(name), ' '.join(parameters)))
-
- # Note the call here.
- return self()
-
- def _show(self):
- self.update_state()
-
-Transform.render = types.MethodType(renpy.display.accelerator.transform_render, None, Transform)
-
-class ATLTransform(renpy.atl.ATLTransformBase, Transform):
-
- def __init__(self, atl, child=None, context={}, parameters=None, **properties):
- renpy.atl.ATLTransformBase.__init__(self, atl, context, parameters)
- Transform.__init__(self, child=child, function=self.execute, **properties)
-
- self.raw_child = self.child
-
- def _show(self):
- super(ATLTransform, self)._show()
- self.execute(self, self.st, self.at)
-
-
-class Motion(Container):
- """
- This is used to move a child displayable around the screen. It
- works by supplying a time value to a user-supplied function,
- which is in turn expected to return a pair giving the x and y
- location of the upper-left-hand corner of the child, or a
- 4-tuple giving that and the xanchor and yanchor of the child.
-
- The time value is a floating point number that ranges from 0 to
- 1. If repeat is True, then the motion repeats every period
- sections. (Otherwise, it stops.) If bounce is true, the
- time value varies from 0 to 1 to 0 again.
-
- The function supplied needs to be pickleable, which means it needs
- to be defined as a name in an init block. It cannot be a lambda or
- anonymous inner function. If you can get away with using Pan or
- Move, use them instead.
-
- Please note that floats and ints are interpreted as for xpos and
- ypos, with floats being considered fractions of the screen.
- """
-
- def __init__(self, function, period, child=None, new_widget=None, old_widget=None, repeat=False, bounce=False, delay=None, anim_timebase=False, tag_start=None, time_warp=None, add_sizes=False, style='motion', **properties):
- """
- @param child: The child displayable.
-
- @param new_widget: If child is None, it is set to new_widget,
- so that we can speak the transition protocol.
-
- @param old_widget: Ignored, for compatibility with the transition protocol.
-
- @param function: A function that takes a floating point value and returns
- an xpos, ypos tuple.
-
- @param period: The amount of time it takes to go through one cycle, in seconds.
-
- @param repeat: Should we repeat after a period is up?
-
- @param bounce: Should we bounce?
-
- @param delay: How long this motion should take. If repeat is None, defaults to period.
-
- @param anim_timebase: If True, use the animation timebase rather than the shown timebase.
-
- @param time_warp: If not None, this is a function that takes a
- fraction of the period (between 0.0 and 1.0), and returns a
- new fraction of the period. Use this to warp time, applying
- acceleration and deceleration to motions.
-
- This can also be used as a transition. When used as a
- transition, the motion is applied to the new_widget for delay
- seconds.
- """
-
- if child is None:
- child = new_widget
-
- if delay is None and not repeat:
- delay = period
-
- super(Motion, self).__init__(style=style, **properties)
-
- if child is not None:
- self.add(child)
-
- self.function = function
- self.period = period
- self.repeat = repeat
- self.bounce = bounce
- self.delay = delay
- self.anim_timebase = anim_timebase
- self.time_warp = time_warp
- self.add_sizes = add_sizes
-
- self.position = None
-
-
- def get_placement(self):
-
- if self.position is None:
- return super(Motion, self).get_placement()
- else:
- return self.position + (self.style.xoffset, self.style.yoffset, self.style.subpixel)
-
- def render(self, width, height, st, at):
-
- if self.anim_timebase:
- t = at
- else:
- t = st
-
- if renpy.game.less_updates:
- if self.delay:
- t = self.delay
- if self.repeat:
- t = t % self.period
- else:
- t = self.period
- elif self.delay and t >= self.delay:
- t = self.delay
- if self.repeat:
- t = t % self.period
- elif self.repeat:
- t = t % self.period
- renpy.display.render.redraw(self, 0)
- else:
- if t > self.period:
- t = self.period
- else:
- renpy.display.render.redraw(self, 0)
-
- if self.period > 0:
- t /= self.period
- else:
- t = 1
-
- if self.time_warp:
- t = self.time_warp(t)
-
- if self.bounce:
- t = t * 2
- if t > 1.0:
- t = 2.0 - t
-
- child = render(self.child, width, height, st, at)
- cw, ch = child.get_size()
-
- if self.add_sizes:
- res = self.function(t, (width, height, cw, ch))
- else:
- res = self.function(t)
-
- res = tuple(res)
-
- if len(res) == 2:
- self.position = res + (self.style.xanchor, self.style.yanchor)
- else:
- self.position = res
-
- rv = renpy.display.render.Render(cw, ch)
- rv.blit(child, (0, 0))
-
- self.offsets = [ (0, 0) ]
-
- return rv
-
-
-class Interpolate(object):
-
- anchors = {
- 'top' : 0.0,
- 'center' : 0.5,
- 'bottom' : 1.0,
- 'left' : 0.0,
- 'right' : 1.0,
- }
-
- def __init__(self, start, end):
-
- if len(start) != len(end):
- raise Exception("The start and end must have the same number of arguments.")
-
- self.start = [ self.anchors.get(i, i) for i in start ]
- self.end = [ self.anchors.get(i, i) for i in end ]
-
- def __call__(self, t, sizes=(None, None, None, None)):
-
- def interp(a, b, c):
-
- if c is not None:
- if type(a) is float:
- a = a * c
- if type(b) is float:
- b = b * c
-
- rv = a + t * (b - a)
-
- return renpy.display.core.absolute(rv)
-
- return [ interp(a, b, c) for a, b, c in zip(self.start, self.end, sizes) ]
-
-
-def Pan(startpos, endpos, time, child=None, repeat=False, bounce=False,
- anim_timebase=False, style='motion', time_warp=None, **properties):
- """
- This is used to pan over a child displayable, which is almost
- always an image. It works by interpolating the placement of the
- upper-left corner of the screen, over time. It's only really
- suitable for use with images that are larger than the screen,
- and we don't do any cropping on the image.
-
- @param startpos: The initial coordinates of the upper-left
- corner of the screen, relative to the image.
-
- @param endpos: The coordinates of the upper-left corner of the
- screen, relative to the image, after time has elapsed.
-
- @param time: The time it takes to pan from startpos to endpos.
-
- @param child: The child displayable.
-
- @param repeat: True if we should repeat this forever.
-
- @param bounce: True if we should bounce from the start to the end
- to the start.
-
- @param anim_timebase: True if we use the animation timebase, False to use the
- displayable timebase.
-
- @param time_warp: If not None, this is a function that takes a
- fraction of the period (between 0.0 and 1.0), and returns a
- new fraction of the period. Use this to warp time, applying
- acceleration and deceleration to motions.
-
- This can be used as a transition. See Motion for details.
- """
-
- x0, y0 = startpos
- x1, y1 = endpos
-
- return Motion(Interpolate((-x0, -y0), (-x1, -y1)),
- time,
- child,
- repeat=repeat,
- bounce=bounce,
- style=style,
- anim_timebase=anim_timebase,
- time_warp=time_warp,
- add_sizes=True,
- **properties)
-
-def Move(startpos, endpos, time, child=None, repeat=False, bounce=False,
- anim_timebase=False, style='motion', time_warp=None, **properties):
- """
- This is used to pan over a child displayable relative to
- the containing area. It works by interpolating the placement of the
- the child, over time.
-
- @param startpos: The initial coordinates of the child
- relative to the containing area.
-
- @param endpos: The coordinates of the child at the end of the
- move.
-
- @param time: The time it takes to move from startpos to endpos.
-
- @param child: The child displayable.
-
- @param repeat: True if we should repeat this forever.
-
- @param bounce: True if we should bounce from the start to the end
- to the start.
-
- @param anim_timebase: True if we use the animation timebase, False to use the
- displayable timebase.
-
- @param time_warp: If not None, this is a function that takes a
- fraction of the period (between 0.0 and 1.0), and returns a
- new fraction of the period. Use this to warp time, applying
- acceleration and deceleration to motions.
-
- This can be used as a transition. See Motion for details.
- """
-
- return Motion(Interpolate(startpos, endpos),
- time,
- child,
- repeat=repeat,
- bounce=bounce,
- anim_timebase=anim_timebase,
- style=style,
- time_warp=time_warp,
- add_sizes=True,
- **properties)
-
-
-class Revolver(object):
-
- def __init__(self, start, end, child, around=(0.5, 0.5), cor=(0.5, 0.5), pos=None):
- self.start = start
- self.end = end
- self.around = around
- self.cor = cor
- self.pos = pos
- self.child = child
-
- def __call__(self, t, xxx_todo_changeme):
-
- # Converts a float to an integer in the given range, passes
- # integers through unchanged.
- (w, h, cw, ch) = xxx_todo_changeme
- def fti(x, r):
- if x is None:
- x = 0
-
- if isinstance(x, float):
- return int(x * r)
- else:
- return x
-
- if self.pos is None:
- pos = self.child.get_placement()
- else:
- pos = self.pos
-
- xpos, ypos, xanchor, yanchor, _xoffset, _yoffset, _subpixel = pos
-
- xpos = fti(xpos, w)
- ypos = fti(ypos, h)
- xanchor = fti(xanchor, cw)
- yanchor = fti(yanchor, ch)
-
- xaround, yaround = self.around
-
- xaround = fti(xaround, w)
- yaround = fti(yaround, h)
-
- xcor, ycor = self.cor
-
- xcor = fti(xcor, cw)
- ycor = fti(ycor, ch)
-
- angle = self.start + (self.end - self.start) * t
- angle *= math.pi / 180
-
- # The center of rotation, relative to the xaround.
- x = xpos - xanchor + xcor - xaround
- y = ypos - yanchor + ycor - yaround
-
- # Rotate it.
- nx = x * math.cos(angle) - y * math.sin(angle)
- ny = x * math.sin(angle) + y * math.cos(angle)
-
- # Project it back.
- nx = nx - xcor + xaround
- ny = ny - ycor + yaround
-
- return (renpy.display.core.absolute(nx), renpy.display.core.absolute(ny), 0, 0)
-
-
-def Revolve(start, end, time, child, around=(0.5, 0.5), cor=(0.5, 0.5), pos=None, **properties):
-
- return Motion(Revolver(start, end, child, around=around, cor=cor, pos=pos),
- time,
- child,
- add_sizes=True,
- **properties)
-
-
-
-def zoom_render(crend, x, y, w, h, zw, zh, bilinear):
- """
- This creates a render that zooms its child.
-
- `crend` - The render of the child.
- `x`, `y`, `w`, `h` - A rectangle inside the child.
- `zw`, `zh` - The size the rectangle is rendered to.
- `bilinear` - Should we be rendering in bilinear mode?
- """
-
- rv = renpy.display.render.Render(zw, zh)
-
- if zw == 0 or zh == 0 or w == 0 or h == 0:
- return rv
-
-
- rv.forward = renpy.display.render.Matrix2D(w / zw, 0, 0, h / zh)
- rv.reverse = renpy.display.render.Matrix2D(zw / w, 0, 0, zh / h)
-
- rv.clipping = True
-
- rv.blit(crend, rv.reverse.transform(-x, -y))
-
- return rv
-
-
-class ZoomCommon(renpy.display.core.Displayable):
- def __init__(self,
- time, child,
- end_identity=False,
- after_child=None,
- time_warp=None,
- bilinear=True,
- opaque=True,
- anim_timebase=False,
- repeat=False,
- style='motion',
- **properties):
- """
- @param time: The amount of time it will take to
- interpolate from the start to the end rectange.
-
- @param child: The child displayable.
-
- @param after_child: If present, a second child
- widget. This displayable will be rendered after the zoom
- completes. Use this to snap to a sharp displayable after
- the zoom is done.
-
- @param time_warp: If not None, this is a function that takes a
- fraction of the period (between 0.0 and 1.0), and returns a
- new fraction of the period. Use this to warp time, applying
- acceleration and deceleration to motions.
- """
-
- super(ZoomCommon, self).__init__(style=style, **properties)
-
- child = renpy.easy.displayable(child)
-
- self.time = time
- self.child = child
- self.repeat = repeat
-
- if after_child:
- self.after_child = renpy.easy.displayable(after_child)
- else:
- if end_identity:
- self.after_child = child
- else:
- self.after_child = None
-
- self.time_warp = time_warp
- self.bilinear = bilinear
- self.opaque = opaque
- self.anim_timebase = anim_timebase
-
-
- def visit(self):
- return [ self.child, self.after_child ]
-
- def render(self, width, height, st, at):
-
- if self.anim_timebase:
- t = at
- else:
- t = st
-
- if self.time:
- done = min(t / self.time, 1.0)
- else:
- done = 1.0
-
- if self.repeat:
- done = done % 1.0
-
- if renpy.game.less_updates:
- done = 1.0
-
- self.done = done
-
- if self.after_child and done == 1.0:
- return renpy.display.render.render(self.after_child, width, height, st, at)
-
- if self.time_warp:
- done = self.time_warp(done)
-
- rend = renpy.display.render.render(self.child, width, height, st, at)
-
- rx, ry, rw, rh, zw, zh = self.zoom_rectangle(done, rend.width, rend.height)
-
- if rx < 0 or ry < 0 or rx + rw > rend.width or ry + rh > rend.height:
- raise Exception("Zoom rectangle %r falls outside of %dx%d parent surface." % ((rx, ry, rw, rh), rend.width, rend.height))
-
- rv = zoom_render(rend, rx, ry, rw, rh, zw, zh, self.bilinear)
-
- if self.done < 1.0:
- renpy.display.render.redraw(self, 0)
-
- return rv
-
- def event(self, ev, x, y, st):
-
- if not self.time:
- done = 1.0
- else:
- done = min(st / self.time, 1.0)
-
- if done == 1.0 and self.after_child:
- return self.after_child.event(ev, x, y, st)
- else:
- return None
-
-
-class Zoom(ZoomCommon):
-
- def __init__(self, size, start, end, time, child, **properties):
-
- end_identity = (end == (0.0, 0.0) + size)
-
- super(Zoom, self).__init__(time, child, end_identity=end_identity, **properties)
-
- self.size = size
- self.start = start
- self.end = end
-
- def zoom_rectangle(self, done, width, height):
-
- rx, ry, rw, rh = [ (a + (b - a) * done) for a, b in zip(self.start, self.end) ]
-
- return rx, ry, rw, rh, self.size[0], self.size[1]
-
-
-class FactorZoom(ZoomCommon):
-
- def __init__(self, start, end, time, child, **properties):
-
- end_identity = (end == 1.0)
-
- super(FactorZoom, self).__init__(time, child, end_identity=end_identity, **properties)
-
- self.start = start
- self.end = end
-
- def zoom_rectangle(self, done, width, height):
-
- factor = self.start + (self.end - self.start) * done
-
- return 0, 0, width, height, factor * width, factor * height
-
-
-
-class SizeZoom(ZoomCommon):
-
- def __init__(self, start, end, time, child, **properties):
-
- end_identity = False
-
- super(SizeZoom, self).__init__(time, child, end_identity=end_identity, **properties)
-
- self.start = start
- self.end = end
-
- def zoom_rectangle(self, done, width, height):
-
- sw, sh = self.start
- ew, eh = self.end
-
- zw = sw + (ew - sw) * done
- zh = sh + (eh - sh) * done
-
- return 0, 0, width, height, zw, zh
-
-
-class RotoZoom(renpy.display.core.Displayable):
-
- transform = None
-
- def __init__(self,
- rot_start,
- rot_end,
- rot_delay,
- zoom_start,
- zoom_end,
- zoom_delay,
- child,
- rot_repeat=False,
- zoom_repeat=False,
- rot_bounce=False,
- zoom_bounce=False,
- rot_anim_timebase=False,
- zoom_anim_timebase=False,
- rot_time_warp=None,
- zoom_time_warp=None,
- opaque=False,
- style='motion',
- **properties):
-
- super(RotoZoom, self).__init__(style=style, **properties)
-
- self.rot_start = rot_start
- self.rot_end = rot_end
- self.rot_delay = rot_delay
-
- self.zoom_start = zoom_start
- self.zoom_end = zoom_end
- self.zoom_delay = zoom_delay
-
- self.child = renpy.easy.displayable(child)
-
- self.rot_repeat = rot_repeat
- self.zoom_repeat = zoom_repeat
-
- self.rot_bounce = rot_bounce
- self.zoom_bounce = zoom_bounce
-
- self.rot_anim_timebase = rot_anim_timebase
- self.zoom_anim_timebase = zoom_anim_timebase
-
- self.rot_time_warp = rot_time_warp
- self.zoom_time_warp = zoom_time_warp
-
- self.opaque = opaque
-
-
- def visit(self):
- return [ self.child ]
-
-
- def render(self, width, height, st, at):
-
- if self.rot_anim_timebase:
- rot_time = at
- else:
- rot_time = st
-
- if self.zoom_anim_timebase:
- zoom_time = at
- else:
- zoom_time = st
-
- if self.rot_delay == 0:
- rot_time = 1.0
- else:
- rot_time /= self.rot_delay
-
- if self.zoom_delay == 0:
- zoom_time = 1.0
- else:
- zoom_time /= self.zoom_delay
-
- if self.rot_repeat:
- rot_time %= 1.0
-
- if self.zoom_repeat:
- zoom_time %= 1.0
-
- if self.rot_bounce:
- rot_time *= 2
- rot_time = min(rot_time, 2.0 - rot_time)
-
- if self.zoom_bounce:
- zoom_time *= 2
- zoom_time = min(zoom_time, 2.0 - zoom_time)
-
- if renpy.game.less_updates:
- rot_time = 1.0
- zoom_time = 1.0
-
- rot_time = min(rot_time, 1.0)
- zoom_time = min(zoom_time, 1.0)
-
- if self.rot_time_warp:
- rot_time = self.rot_time_warp(rot_time)
-
- if self.zoom_time_warp:
- zoom_time = self.zoom_time_warp(zoom_time)
-
-
- angle = self.rot_start + (1.0 * self.rot_end - self.rot_start) * rot_time
- zoom = self.zoom_start + (1.0 * self.zoom_end - self.zoom_start) * zoom_time
- # angle = -angle * math.pi / 180
-
- zoom = max(zoom, 0.001)
-
- if self.transform is None:
- self.transform = Transform(self.child)
-
- self.transform.rotate = angle
- self.transform.zoom = zoom
-
- rv = renpy.display.render.render(self.transform, width, height, st, at)
-
- if rot_time <= 1.0 or zoom_time <= 1.0:
- renpy.display.render.redraw(self.transform, 0)
-
- return rv
-
-
-# For compatibility with old games.
-renpy.display.layout.Transform = Transform
-renpy.display.layout.RotoZoom = RotoZoom
-renpy.display.layout.SizeZoom = SizeZoom
-renpy.display.layout.FactorZoom = FactorZoom
-renpy.display.layout.Zoom = Zoom
-renpy.display.layout.Revolver = Revolver
-renpy.display.layout.Motion = Motion
-renpy.display.layout.Interpolate = Interpolate
-
-# Leave these functions around - they might have been pickled somewhere.
-renpy.display.layout.Revolve = Revolve # function
-renpy.display.layout.Move = Move # function
-renpy.display.layout.Pan = Pan # function
diff --git a/unrpyc/renpy/display/movetransition.py b/unrpyc/renpy/display/movetransition.py
deleted file mode 100644
index 1b3bdd7..0000000
--- a/unrpyc/renpy/display/movetransition.py
+++ /dev/null
@@ -1,640 +0,0 @@
-# Copyright 2004-2013 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.
-
-# NOTE:
-# Transitions need to be able to work even when old_widget and new_widget
-# are None, at least to the point of making it through __init__. This is
-# so that prediction of images works.
-
-import renpy.display
-
-# Utility function used by MoveTransition et al.
-def position(d):
-
- xpos, ypos, xanchor, yanchor, _xoffset, _yoffset, _subpixel = d.get_placement()
-
- if xpos is None:
- xpos = 0
- if ypos is None:
- ypos = 0
- if xanchor is None:
- xanchor = 0
- if yanchor is None:
- yanchor = 0
-
- return xpos, ypos, xanchor, yanchor
-
-def offsets(d):
-
- _xpos, _ypos, _xanchor, _yanchor, xoffset, yoffset, _subpixel = d.get_placement()
-
- if renpy.config.movetransition_respects_offsets:
- return { 'xoffset' : xoffset, 'yoffset' : yoffset }
- else:
- return { }
-
-
-# These are used by MoveTransition.
-def MoveFactory(pos1, pos2, delay, d, **kwargs):
- if pos1 == pos2:
- return d
-
- return renpy.display.motion.Move(pos1, pos2, delay, d, **kwargs)
-
-def default_enter_factory(pos, delay, d, **kwargs):
- return d
-
-def default_leave_factory(pos, delay, d, **kwargs):
- return None
-
-# These can be used to move things in and out of the screen.
-def MoveIn(pos, pos1, delay, d, **kwargs):
-
- def aorb(a, b):
- if a is None:
- return b
- return a
-
- pos = tuple([aorb(a, b) for a, b in zip(pos, pos1)])
- return renpy.display.motion.Move(pos, pos1, delay, d, **kwargs)
-
-def MoveOut(pos, pos1, delay, d, **kwargs):
-
- def aorb(a, b):
- if a is None:
- return b
- return a
-
- pos = tuple([aorb(a, b) for a, b in zip(pos, pos1)])
- return renpy.display.motion.Move(pos1, pos, delay, d, **kwargs)
-
-def ZoomInOut(start, end, pos, delay, d, **kwargs):
-
- xpos, ypos, xanchor, yanchor = pos
-
- FactorZoom = renpy.display.motion.FactorZoom
-
- if end == 1.0:
- return FactorZoom(start, end, delay, d, after_child=d, opaque=False,
- xpos=xpos, ypos=ypos, xanchor=xanchor, yanchor=yanchor, **kwargs)
- else:
- return FactorZoom(start, end, delay, d, opaque=False,
- xpos=xpos, ypos=ypos, xanchor=xanchor, yanchor=yanchor, **kwargs)
-
-def RevolveInOut(start, end, pos, delay, d, **kwargs):
- return renpy.display.motion.Revolve(start, end, delay, d, pos=pos, **kwargs)
-
-
-def OldMoveTransition(delay, old_widget=None, new_widget=None, factory=None, enter_factory=None, leave_factory=None, old=False, layers=[ 'master' ]):
- """
- Returns a transition that attempts to find images that have changed
- position, and moves them from the old position to the new transition, taking
- delay seconds to complete the move.
-
- If `factory` is given, it is expected to be a function that takes as
- arguments: an old position, a new position, the delay, and a
- displayable, and to return a displayable as an argument. If not
- given, the default behavior is to move the displayable from the
- starting to the ending positions. Positions are always given as
- (xpos, ypos, xanchor, yanchor) tuples.
-
- If `enter_factory` or `leave_factory` are given, they are expected
- to be functions that take as arguments a position, a delay, and a
- displayable, and return a displayable. They are applied to
- displayables that are entering or leaving the scene,
- respectively. The default is to show in place displayables that
- are entering, and not to show those that are leaving.
-
- If `old` is True, then factory moves the old displayable with the
- given tag. Otherwise, it moves the new displayable with that
- tag.
-
- `layers` is a list of layers that the transition will be applied
- to.
-
- Images are considered to be the same if they have the same tag, in
- the same way that the tag is used to determine which image to
- replace or to hide. They are also considered to be the same if
- they have no tag, but use the same displayable.
-
- Computing the order in which images are displayed is a three-step
- process. The first step is to create a list of images that
- preserves the relative ordering of entering and moving images. The
- second step is to insert the leaving images such that each leaving
- image is at the lowest position that is still above all images
- that were below it in the original scene. Finally, the list
- is sorted by zorder, to ensure no zorder violations occur.
-
- If you use this transition to slide an image off the side of the
- screen, remember to hide it when you are done. (Or just use
- a leave_factory.)
- """
-
- if factory is None:
- factory = MoveFactory
-
- if enter_factory is None:
- enter_factory = default_enter_factory
-
- if leave_factory is None:
- leave_factory = default_leave_factory
-
- use_old = old
-
- def merge_slide(old, new):
-
- # If new does not have .layers or .scene_list, then we simply
- # insert a move from the old position to the new position, if
- # a move occured.
-
- if (not isinstance(new, renpy.display.layout.MultiBox)
- or (new.layers is None and new.layer_name is None)):
-
- if use_old:
- child = old
- else:
- child = new
-
- old_pos = position(old)
- new_pos = position(new)
-
- if old_pos != new_pos:
- return factory(old_pos,
- new_pos,
- delay,
- child,
- **offsets(child)
- )
-
- else:
- return child
-
- # If we're in the layers_root widget, merge the child widgets
- # for each layer.
- if new.layers:
-
- assert old.layers
-
- rv = renpy.display.layout.MultiBox(layout='fixed')
- rv.layers = { }
-
- for layer in renpy.config.layers:
-
- f = new.layers[layer]
-
- if (isinstance(f, renpy.display.layout.MultiBox)
- and layer in layers
- and f.scene_list is not None):
-
- f = merge_slide(old.layers[layer], new.layers[layer])
-
- rv.layers[layer] = f
- rv.add(f)
-
- return rv
-
- # Otherwise, we recompute the scene list for the two widgets, merging
- # as appropriate.
-
- # Wraps the displayable found in SLE so that the various timebases
- # are maintained.
- def wrap(sle):
- return renpy.display.layout.AdjustTimes(sle.displayable, sle.show_time, sle.animation_time)
-
- def tag(sle):
- return sle.tag or sle.displayable
-
- def merge(sle, d):
- rv = sle.copy()
- rv.show_time = 0
- rv.displayable = d
- return rv
-
- def entering(sle):
- new_d = wrap(new_sle)
- move = enter_factory(position(new_d), delay, new_d, **offsets(new_d))
-
- if move is None:
- return
-
- rv_sl.append(merge(new_sle, move))
-
- def leaving(sle):
- old_d = wrap(sle)
- move = leave_factory(position(old_d), delay, old_d, **offsets(old_d))
-
- if move is None:
- return
-
- move = renpy.display.layout.IgnoresEvents(move)
- rv_sl.append(merge(old_sle, move))
-
-
- def moving(old_sle, new_sle):
- old_d = wrap(old_sle)
- new_d = wrap(new_sle)
-
- if use_old:
- child = old_d
- else:
- child = new_d
-
- move = factory(position(old_d), position(new_d), delay, child, **offsets(child))
- if move is None:
- return
-
- rv_sl.append(merge(new_sle, move))
-
-
- # The old, new, and merged scene_lists.
- old_sl = old.scene_list[:]
- new_sl = new.scene_list[:]
- rv_sl = [ ]
-
-
- # A list of tags in old_sl, new_sl, and rv_sl.
- old_map = dict((tag(i), i) for i in old_sl if i is not None)
- new_tags = set(tag(i) for i in new_sl if i is not None)
- rv_tags = set()
-
- while old_sl or new_sl:
-
- # If we have something in old_sl, then
- if old_sl:
-
- old_sle = old_sl[0]
- old_tag = tag(old_sle)
-
- # If the old thing has already moved, then remove it.
- if old_tag in rv_tags:
- old_sl.pop(0)
- continue
-
- # If the old thing does not match anything in new_tags,
- # have it enter.
- if old_tag not in new_tags:
- leaving(old_sle)
- rv_tags.add(old_tag)
- old_sl.pop(0)
- continue
-
-
- # Otherwise, we must have something in new_sl. We want to
- # either move it or have it enter.
-
- new_sle = new_sl.pop(0)
- new_tag = tag(new_sle)
-
- # If it exists in both, move.
- if new_tag in old_map:
- old_sle = old_map[new_tag]
-
- moving(old_sle, new_sle)
- rv_tags.add(new_tag)
- continue
-
- else:
- entering(new_sle)
- rv_tags.add(new_tag)
- continue
-
- # Sort everything by zorder, to ensure that there are no zorder
- # violations in the result.
- rv_sl.sort(key=lambda a : a.zorder)
-
- layer = new.layer_name
- rv = renpy.display.layout.MultiBox(layout='fixed', focus=layer, **renpy.game.interface.layer_properties[layer])
- rv.append_scene_list(rv_sl)
- rv.layer_name = layer
-
- return rv
-
-
- # This calls merge_slide to actually do the merging.
-
- rv = merge_slide(old_widget, new_widget)
- rv.delay = delay # W0201
-
- return rv
-
-##############################################################################
-# New Move Transition (since 6.14)
-
-
-class MoveInterpolate(renpy.display.core.Displayable):
- """
- This displayable has two children. It interpolates between the positions
- of its two children to place them on the screen.
- """
-
- def __init__(self, delay, old, new, use_old, time_warp):
- super(MoveInterpolate, self).__init__()
-
- # The old and new displayables.
- self.old = old
- self.new = new
-
- # Should we display the old displayable?
- self.use_old = False
-
- # Time warp function or None.
- self.time_warp = time_warp
-
- # The width of the screen.
- self.screen_width = 0
- self.screen_height = 0
-
- # The width of the selected child.
- self.child_width = 0
- self.child_height = 0
-
- # The delay and st.
- self.delay = delay
- self.st = 0
-
- def render(self, width, height, st, at):
- self.screen_width = width
- self.screen_height = height
-
- old_r = renpy.display.render.render(self.old, width, height, st, at)
- new_r = renpy.display.render.render(self.new, width, height, st, at)
-
- if self.use_old:
- cr = old_r
- else:
- cr = new_r
-
- self.child_width, self.child_height = cr.get_size()
- self.st = st
-
- if self.st < self.delay:
- renpy.display.render.redraw(self, 0)
-
- return cr
-
- def child_placement(self, child):
-
- def based(v, base):
- if v is None:
- return 0
- elif isinstance(v, int):
- return v
- elif isinstance(v, renpy.display.core.absolute):
- return v
- else:
- return v * base
-
- xpos, ypos, xanchor, yanchor, xoffset, yoffset, subpixel = child.get_placement()
-
- xpos = based(xpos, self.screen_width)
- ypos = based(ypos, self.screen_height)
- xanchor = based(xanchor, self.child_width)
- yanchor = based(yanchor, self.child_height)
-
- return xpos, ypos, xanchor, yanchor, xoffset, yoffset, subpixel
-
- def get_placement(self):
-
- if self.st > self.delay:
- done = 1.0
- else:
- done = self.st / self.delay
-
- if self.time_warp is not None:
- done = self.time_warp(done)
-
- absolute = renpy.display.core.absolute
-
- def I(a, b):
- return absolute(a + done * (b - a))
-
- old_xpos, old_ypos, old_xanchor, old_yanchor, old_xoffset, old_yoffset, old_subpixel = self.child_placement(self.old)
- new_xpos, new_ypos, new_xanchor, new_yanchor, new_xoffset, new_yoffset, new_subpixel = self.child_placement(self.new)
-
- xpos = I(old_xpos, new_xpos)
- ypos = I(old_ypos, new_ypos)
- xanchor = I(old_xanchor, new_xanchor)
- yanchor = I(old_yanchor, new_yanchor)
- xoffset = I(old_xoffset, new_xoffset)
- yoffset = I(old_yoffset, new_yoffset)
- subpixel = old_subpixel or new_subpixel
-
- return xpos, ypos, xanchor, yanchor, xoffset, yoffset, subpixel
-
-
-def MoveTransition(delay, old_widget=None, new_widget=None, enter=None, leave=None, old=False, layers=[ 'master' ], time_warp=None, enter_time_warp=None, leave_time_warp=None):
- """
- :doc: transition function
- :args: (delay, enter=None, leave=None, old=False, layers=['master'], time_warp=None, enter_time_warp=None, leave_time_warp=None)
- :name: MoveTransition
-
- Returns a transition that interpolates the position of images (with the
- same tag) in the old and new scenes.
-
- `delay`
- The time it takes for the interpolation to finish.
-
- `enter`
- If not None, images entering the scene will also be moved. The value
- of `enter` should be a transform that is applied to the image to
- get its starting position.
-
- `leave`
- If not None, images leaving the scene will also be move. The value
- of `leave` should be a transform that is applied to the image to
- get its ending position.
-
- `old`
- If true, the old image will be used in preference to the new one.
-
- `layers`
- A list of layers that moves are applied to.
-
- `time_warp`
- A time warp function that's applied to the interpolation. This
- takes a number between 0.0 and 1.0, and should return a number in
- the same range.
-
- `enter_time_warp`
- A time warp function that's applied to images entering the scene.
-
- `enter_time_warp`
- A time warp function that's applied to images leaving the scene.
-
- """
-
- use_old = old
-
- def merge_slide(old, new):
-
- # If new does not have .layers or .scene_list, then we simply
- # insert a move from the old position to the new position, if
- # a move occured.
-
- if (not isinstance(new, renpy.display.layout.MultiBox)
- or (new.layers is None and new.layer_name is None)):
-
- if old is new:
- return new
- else:
- return MoveInterpolate(delay, old, new, use_old, time_warp)
-
-
- # If we're in the layers_root widget, merge the child widgets
- # for each layer.
- if new.layers:
-
- assert old.layers
-
- rv = renpy.display.layout.MultiBox(layout='fixed')
-
- for layer in renpy.config.layers:
-
- f = new.layers[layer]
-
- if (isinstance(f, renpy.display.layout.MultiBox)
- and layer in layers
- and f.scene_list is not None):
-
- f = merge_slide(old.layers[layer], new.layers[layer])
-
- rv.add(f)
-
- return rv
-
- # Otherwise, we recompute the scene list for the two widgets, merging
- # as appropriate.
-
- # Wraps the displayable found in SLE so that the various timebases
- # are maintained.
- def wrap(sle):
- return renpy.display.layout.AdjustTimes(sle.displayable, sle.show_time, sle.animation_time)
-
- def tag(sle):
- return sle.tag or sle.displayable
-
- def merge(sle, d):
- rv = sle.copy()
- rv.show_time = 0
- rv.displayable = d
- return rv
-
- def entering(sle):
-
- if not enter:
- return
-
- new_d = wrap(new_sle)
- move = MoveInterpolate(delay, enter(new_d), new_d, False, enter_time_warp)
- rv_sl.append(merge(new_sle, move))
-
- def leaving(sle):
-
- if not leave:
- return
-
- old_d = wrap(sle)
- move = MoveInterpolate(delay, old_d, leave(old_d), True, leave_time_warp)
- move = renpy.display.layout.IgnoresEvents(move)
- rv_sl.append(merge(old_sle, move))
-
-
- def moving(old_sle, new_sle):
-
- if old_sle.displayable is new_sle.displayable:
- rv_sl.append(new_sle)
- return
-
- old_d = wrap(old_sle)
- new_d = wrap(new_sle)
-
- move = MoveInterpolate(delay, old_d, new_d, use_old, time_warp)
-
- rv_sl.append(merge(new_sle, move))
-
-
- # The old, new, and merged scene_lists.
- old_sl = old.scene_list[:]
- new_sl = new.scene_list[:]
- rv_sl = [ ]
-
- # A list of tags in old_sl, new_sl, and rv_sl.
- old_map = dict((tag(i), i) for i in old_sl if i is not None)
- new_tags = set(tag(i) for i in new_sl if i is not None)
- rv_tags = set()
-
- while old_sl or new_sl:
-
- # If we have something in old_sl, then
- if old_sl:
-
- old_sle = old_sl[0]
- old_tag = tag(old_sle)
-
- # If the old thing has already moved, then remove it.
- if old_tag in rv_tags:
- old_sl.pop(0)
- continue
-
- # If the old thing does not match anything in new_tags,
- # have it enter.
- if old_tag not in new_tags:
- leaving(old_sle)
- rv_tags.add(old_tag)
- old_sl.pop(0)
- continue
-
-
- # Otherwise, we must have something in new_sl. We want to
- # either move it or have it enter.
-
- new_sle = new_sl.pop(0)
- new_tag = tag(new_sle)
-
- # If it exists in both, move.
- if new_tag in old_map:
- old_sle = old_map[new_tag]
-
- moving(old_sle, new_sle)
- rv_tags.add(new_tag)
- continue
-
- else:
- entering(new_sle)
- rv_tags.add(new_tag)
- continue
-
- # Sort everything by zorder, to ensure that there are no zorder
- # violations in the result.
- rv_sl.sort(key=lambda a : a.zorder)
-
- layer = new.layer_name
- rv = renpy.display.layout.MultiBox(layout='fixed', focus=layer, **renpy.game.interface.layer_properties[layer])
- rv.append_scene_list(rv_sl)
-
- return rv
-
- # Call merge_slide to actually do the merging.
- rv = merge_slide(old_widget, new_widget)
- rv.delay = delay
-
- return rv
-
diff --git a/unrpyc/renpy/display/particle.py b/unrpyc/renpy/display/particle.py
deleted file mode 100644
index cf52417..0000000
--- a/unrpyc/renpy/display/particle.py
+++ /dev/null
@@ -1,615 +0,0 @@
-# Copyright 2004-2013 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.
-
-# This code supports sprite and particle animation.
-
-from renpy.display.render import render, BLIT
-
-import renpy.display
-import random
-
-
-class SpriteCache(renpy.object.Object):
- """
- This stores information about a displayble, including the identity
- of the displayable, and when it was first displayed. It is also
- responsible for caching the displayable surface, so it doesn't
- need to be re-rendered.
- """
-
- # Private Fields:
- #
- # child - The child displayable.
- #
- # st - The shown time when this was first displayed, or None if it hasn't
- # been rendered.
- #
- # render - The render of child.
- #
- # If true, then the render is simple enough it can just be appended to
- # the manager's render's children list.
-
-class Sprite(renpy.object.Object):
- """
- :doc: sprites class
-
- This represents a sprite that is managed by the SpriteManager. It contains
- fields that control the placement of the sprite on the screen. Sprites
- should not be created directly. Instead, they should be created by
- calling :meth:`SpriteManager.create`.
-
- The fields of a sprite object are:
-
- `x`, `y`
- The x and y coordinates of the upper-left corner of the sprite,
- relative to the SpriteManager.
-
- `zorder`
- An integer that's used to control the order of this sprite in the
- relative to the other sprites in the SpriteManager. The larger the
- number is, the closer to the viewer the sprite is.
-
- `events`
- If True, then events are passed to child. If False, the default,
- the children igore events (and hence don't spend time processing
- them).
-
- The methods of a Sprite object are:
- """
-
- # Fields:
- #
- # child - the displayable that is the child of this sprite.
- # cache - the SpriteCache of child.
- # live - True if this sprite is still alive.
- # manager - A reference to the SpriteManager.
-
- def set_child(self, d):
- """
- :doc: sprites method
-
- Changes the Displayable associated with this sprite to `d`.
- """
-
- id_d = id(d)
-
- sc = self.manager.displayable_map.get(id_d, None)
- if sc is None:
- d = renpy.easy.displayable(d)
-
- sc = SpriteCache()
- sc.render = None
- sc.child = d
- sc.st = None
-
- self.manager.displayable_map[id_d] = sc
-
- self.cache = sc
-
- def destroy(self):
- """
- :doc: sprites method
-
- Destroys this sprite, preventing it from being displayed and
- removing it from the SpriteManager.
- """
-
- self.manager.dead_child = True
- self.live = False
- self.events = False
-
-
-
-class SpriteManager(renpy.display.core.Displayable):
- """
- :doc: sprites class
-
- This displayable manages a collection of sprites, and displays
- them at the fastest speed possible.
- """
-
- def __init__(self, update=None, event=None, predict=None, ignore_time=False, **properties):
- """
- `update`
- If not None, a function that is called each time a sprite
- is rendered by this sprite manager. It is called with one
- argument, the time in seconds since this sprite manager
- was first displayed. It is expected to return the number
- of seconds until the function is called again, and the
- SpriteManager is rendered again.
-
- `event`
- If not None, a function that is called when an event occurs.
- It takes as arguments:
- * A pygame event object.
- * The x coordinate of the event.
- * The y coordinate of the event.
- * The time since the sprite manager was first shown.
- If it returns a non-None value, the interaction ends, and
- that value is returned.
-
- `predict`
- If not None, a function that returns a list of
- displayables. These displayables are predicted when the
- sprite manager is.
-
- `ignore_time`
- If True, then time is ignored when rendering displayables. This
- should be used when the sprite manager is used with a relatively
- small pool of images, and those images do not change over time.
- This should only be used with a small number of displayables, as
- it will keep all displayables used in memory for the life of the
- SpriteManager.
-
- After being rendered once (before the `update` function is called),
- SpriteManagers have the following fields:
-
- `width`, `height`
-
- The width and height of this SpriteManager, in pixels.
-
-
- SpriteManagers have the following methods:
- """
-
- super(SpriteManager, self).__init__(self, **properties)
-
- self.update_function = update
- self.event_function = event
- self.predict_function = predict
- self.ignore_time = ignore_time
-
- # A map from a displayable to the SpriteDisplayable object
- # representing that displayable.
- self.displayable_map = { }
-
- # A list of children of this displayable, in zorder. (When sorted.)
- # This is a list of Sprites.
- self.children = [ ]
-
- # True if at least one child has been killed.
- self.dead_child = False
-
- # True if at least one child responds to events.
- self.events = False
-
- # The width and height.
- self.width = None
- self.height = None
-
- def create(self, d):
- """
- :doc: sprites method
-
- Creates a new Sprite for the displayable `d`, and adds it to this
- SpriteManager.
- """
-
- id_d = id(d)
-
- sc = self.displayable_map.get(id_d, None)
- if sc is None:
- d = renpy.easy.displayable(d)
-
- sc = SpriteCache()
- sc.render = None
- sc.child = d
- sc.st = None
- self.displayable_map[id_d] = sc
-
- s = Sprite()
- s.x = 0
- s.y = 0
- s.zorder = 0
- s.cache = sc
- s.live = True
- s.manager = self
- s.events = False
-
- self.children.append(s)
-
- return s
-
- def predict_one(self):
- if self.predict_function is not None:
- for i in self.predict_function():
- renpy.display.predict.displayable(i)
-
-
- def redraw(self, delay=0):
- """
- :doc: sprite method
-
- Causes this SpriteManager to be redrawn in `delay` seconds.
- """
-
- renpy.display.render.redraw(self, delay)
-
- def render(self, width, height, st, at):
-
- self.width = width
- self.height = height
-
- if self.update_function is not None:
-
- redraw = self.update_function(st)
-
- if redraw is not None:
- renpy.display.render.redraw(self, redraw)
-
- if not self.ignore_time:
- self.displayable_map.clear()
-
- if self.dead_child:
- self.children = [ i for i in self.children if i.live ]
-
- self.children.sort(key=lambda sc:sc.zorder)
-
- caches = [ ]
-
- rv = renpy.display.render.Render(width, height)
-
- events = False
-
- for i in self.children:
-
- events |= i.events
-
- cache = i.cache
- r = i.cache.render
- if cache.render is None:
- if cache.st is None:
- cache.st = st
-
- cst = st - cache.st
-
- cache.render = r = render(cache.child, width, height, cst, cst)
- cache.fast = (r.operation == BLIT) and (r.forward is None) and (r.alpha == 1.0)
- rv.depends_on(r)
-
- caches.append(cache)
-
-
- if cache.fast:
- for child, xo, yo, _focus, _main in r.children:
- rv.children.append((child,
- xo + i.x,
- yo + i.y,
- False,
- False))
-
- else:
- rv.subpixel_blit(r, (i.x, i.y))
-
- for i in caches:
- i.render = None
-
- return rv
-
- def event(self, ev, x, y, st):
- for i in range(len(self.children) -1, -1, -1):
- s = self.children[i]
-
- if s.events:
- rv = s.cache.child.event(ev, x - s.x, y - s.y, st - s.cache.st)
- if rv is not None:
- return rv
-
- if self.event_function is not None:
- return self.event_function(ev, x, y, st)
- else:
- return None
-
- def visit(self):
- rv = [ ]
-
- try:
- if self.predict_function:
- pl = self.predict_function()
- for i in pl:
- i = renpy.easy.displayable(i)
- rv.append(i)
- except:
- pass
-
- return rv
-
- def destroy_all(self):
- self.children = [ ]
-
-
-class Particles(renpy.display.core.Displayable, renpy.python.NoRollback):
- """
- Supports particle motion, using the old API.
- """
-
- __version__ = 1
-
- nosave = [ 'particles' ]
-
- def after_upgrade(self, version):
- if version < 1:
- self.sm = SpriteManager(update=self.update_callback, predict=self.predict_callback)
-
- def after_setstate(self):
- self.particles = None
-
- def __init__(self, factory, **properties):
- """
- @param factory: A factory object.
- """
-
- super(Particles, self).__init__(**properties)
-
- self.sm = SpriteManager(update=self.update_callback, predict=self.predict_callback)
-
- self.factory = factory
- self.particles = None
-
- def update_callback(self, st):
-
- particles = self.particles
-
- if st == 0 or particles is None:
- self.sm.destroy_all()
- particles = [ ]
-
- add_parts = self.factory.create(particles, st)
-
- new_particles = [ ]
-
- for sprite, p in particles:
- update = p.update(st)
-
- if update is None:
- sprite.destroy()
- continue
-
- x, y, _t, d = update
-
- if d is not sprite.cache.child:
- sprite.set_child(d)
-
- sprite.x = x
- sprite.y = y
-
- new_particles.append((sprite, p))
-
- if add_parts:
- for p in add_parts:
- update = p.update(st)
-
- if update is None:
- continue
-
- x, y, _t, d = update
-
- if d is None:
- continue
-
- sprite = self.sm.create(d)
- sprite.x = x
- sprite.y = y
-
- new_particles.append((sprite, p))
-
- self.particles = new_particles
-
- return 0
-
- def predict_callback(self):
- return self.factory.predict()
-
- def render(self, w, h, st, at):
- return renpy.display.render.render(self.sm, w, h, st, at)
-
-class SnowBlossomFactory(renpy.python.NoRollback):
-
- rotate = False
-
- def __setstate__(self, state):
- self.start = 0
- vars(self).update(state)
- self.init()
-
- def __init__(self, image, count, xspeed, yspeed, border, start, fast, rotate=False):
- self.image = renpy.easy.displayable(image)
- self.count = count
- self.xspeed = xspeed
- self.yspeed = yspeed
- self.border = border
- self.start = start
- self.fast = fast
- self.rotate = rotate
- self.init()
-
- def init(self):
- self.starts = [ random.uniform(0, self.start) for _i in range(0, self.count) ] # W0201
- self.starts.append(self.start)
- self.starts.sort()
-
- def create(self, particles, st):
-
- def ranged(n):
- if isinstance(n, tuple):
- return random.uniform(n[0], n[1])
- else:
- return n
-
- if not particles and self.fast:
- rv = [ ]
-
- for _i in range(0, self.count):
- rv.append(SnowBlossomParticle(self.image,
- ranged(self.xspeed),
- ranged(self.yspeed),
- self.border,
- st,
- random.uniform(0, 100),
- fast=True,
- rotate=self.rotate))
- return rv
-
-
- if particles is None or len(particles) < self.count:
-
- # Check to see if we have a particle ready to start. If not,
- # don't start it.
- if particles and st < self.starts[len(particles)]:
- return None
-
- return [ SnowBlossomParticle(self.image,
- ranged(self.xspeed),
- ranged(self.yspeed),
- self.border,
- st,
- random.uniform(0, 100),
- fast=False,
- rotate=self.rotate) ]
-
- def predict(self):
- return [ self.image ]
-
-
-class SnowBlossomParticle(renpy.python.NoRollback):
-
- def __init__(self, image, xspeed, yspeed, border, start, offset, fast, rotate):
-
- # safety.
- if yspeed == 0:
- yspeed = 1
-
- self.image = image
- self.xspeed = xspeed
- self.yspeed = yspeed
- self.border = border
- self.start = start
- self.offset = offset
- self.rotate = rotate
-
-
- if not rotate:
- sh = renpy.config.screen_height
- sw = renpy.config.screen_width
- else:
- sw = renpy.config.screen_height
- sh = renpy.config.screen_width
-
-
- if self.yspeed > 0:
- self.ystart = -border
- else:
- self.ystart = sh + border
-
-
- travel_time = (2.0 * border + sh) / abs(yspeed)
-
- xdist = xspeed * travel_time
-
- x0 = min(-xdist, 0)
- x1 = max(sw + xdist, sw)
-
- self.xstart = random.uniform(x0, x1)
-
- if fast:
- self.ystart = random.uniform(-border, sh + border)
- self.xstart = random.uniform(0, sw)
-
- def update(self, st):
- to = st - self.start
-
- xpos = self.xstart + to * self.xspeed
- ypos = self.ystart + to * self.yspeed
-
- if not self.rotate:
- sh = renpy.config.screen_height
- else:
- sh = renpy.config.screen_width
-
- if ypos > sh + self.border:
- return None
-
- if ypos < -self.border:
- return None
-
- if not self.rotate:
- return int(xpos), int(ypos), to + self.offset, self.image
- else:
- return int(ypos), int(xpos), to + self.offset, self.image
-
-def SnowBlossom(d,
- count=10,
- border=50,
- xspeed=(20, 50),
- yspeed=(100, 200),
- start=0,
- fast=False,
- horizontal=False):
-
- """
- :doc: sprites_extra
-
- The snowblossom effect moves multiple instances of a sprite up,
- down, left or right on the screen. When a sprite leaves the screen, it
- is returned to the start.
-
- `d`
- The displayable to use for the sprites.
-
- `border`
- The size of the border of the screen. The sprite is considered to be
- on the screen until it clears the border, ensuring that sprites do
- not disappear abruptly.
-
- `xspeed`, `yspeed`
- The speed at which the sprites move, in the horizontal and vertical
- directions, respectively. These can be a single number or a tuple of
- two numbers. In the latter case, each particle is assigned a random
- speed between the two numbers. The speeds can be positive or negative,
- as long as the second number in a tuple is larger than the first.
-
- `start`
- The delay, in seconds, before each particle is added. This can be
- allows the particles to start at the top of the screen, while not
- looking like a "wave" effect.
-
- `fast`
- If true, particles start in the center of the screen, rather than
- only at the edges.
-
- `horizontal`
- If true, particles appear on the left or right side of the screen,
- rather than the top or bottom.
- """
-
- # If going horizontal, swap the xspeed and the yspeed.
- if horizontal:
- xspeed, yspeed = yspeed, xspeed
-
- return Particles(SnowBlossomFactory(image=d,
- count=count,
- border=border,
- xspeed=xspeed,
- yspeed=yspeed,
- start=start,
- fast=fast,
- rotate=horizontal))
-
diff --git a/unrpyc/renpy/display/pgrender.py b/unrpyc/renpy/display/pgrender.py
deleted file mode 100644
index 32d1df8..0000000
--- a/unrpyc/renpy/display/pgrender.py
+++ /dev/null
@@ -1,167 +0,0 @@
-# Copyright 2004-2013 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.
-
-# This module wraps the pygame surface class (and associated functions). It
-# ensures that returned surfaces have a 2px border around them.
-
-import sys
-import pygame
-import renpy.display
-
-# Sample surfaces, with and without alpha.
-sample_alpha = None
-sample_noalpha = None
-
-def set_rgba_masks():
- """
- This rebuilds the sample surfaces, to ones that use the given
- masks.
- """
-
- # Annoyingly, the value for the big mask seems to vary from
- # platform to platform. So we read it out of a surface.
-
- global sample_alpha
- global sample_noalpha
-
- # Create a sample surface.
- s = pygame.Surface((10, 10), 0, 32)
- sample_alpha = s.convert_alpha()
-
- # Sort the components by absolute value.
- masks = list(sample_alpha.get_masks())
- masks.sort(key=lambda a : abs(a))
-
- # Choose the masks.
- if sys.byteorder == 'big':
- masks = ( masks[3], masks[2], masks[1], masks[0] )
- else:
- masks = ( masks[0], masks[1], masks[2], masks[3] )
-
- # Create the sample surface.
- sample_alpha = pygame.Surface((10, 10), 0, 32, masks)
- sample_noalpha = pygame.Surface((10, 10), 0, 32, masks[:3] + (0,))
-
-
-class Surface(pygame.Surface):
- """
- This allows us to wrap around pygame's surface, to change
- its mode, as necessary.
- """
-
- opaque = False
-
- def is_opaque(self):
- return self.opaque
-
- def convert_alpha(self, surface=None):
- return copy_surface_unscaled(self, True)
-
- def convert(self, surface=None):
- return copy_surface(self, False)
-
- def copy(self):
- return copy_surface(self, self)
-
- def subsurface(self, rect):
- rv = pygame.Surface.subsurface(self, rect)
- return rv
-
-def surface(xxx_todo_changeme, alpha):
- """
- Constructs a new surface. The allocated surface is actually a subsurface
- of a surface that has a 2 pixel border in all directions.
-
- `alpha` - True if the new surface should have an alpha channel.
- """
- (width, height) = xxx_todo_changeme
- if isinstance(alpha, pygame.Surface):
- alpha = alpha.get_masks()[3]
-
- if alpha:
- sample = sample_alpha
- else:
- sample = sample_noalpha
-
- # We might not have initialized properly yet. This is enough
- # to get us underway.
- if sample is None:
- sample = pygame.Surface((4, 4), pygame.SRCALPHA, 32)
-
- surf = Surface((width + 4, height + 4), 0, sample)
- return surf.subsurface((2, 2, width, height)) # E1101
-
-surface_unscaled = surface
-
-def copy_surface(surf, alpha=True):
- """
- Creates a copy of the surface.
- """
-
- rv = surface_unscaled(surf.get_size(), alpha)
- renpy.display.accelerator.nogil_copy(surf, rv) # @UndefinedVariable
- return rv
-
-copy_surface_unscaled = copy_surface
-
-
-# Wrapper around image loading.
-
-def load_image(f, filename):
- surf = pygame.image.load(f, renpy.exports.fsencode(filename))
- rv = copy_surface_unscaled(surf)
- return rv
-
-load_image_unscaled = load_image
-
-
-# Wrapper around functions we use from pygame.surface.
-
-def flip(surf, horizontal, vertical):
- surf = pygame.transform.flip(surf, horizontal, vertical)
- return copy_surface_unscaled(surf)
-
-flip_unscaled = flip
-
-
-def rotozoom(surf, angle, zoom):
-
- surf = pygame.transform.rotozoom(surf, angle, zoom)
- return copy_surface_unscaled(surf)
-
-rotozoom_unscaled = rotozoom
-
-
-def transform_scale(surf, size):
- surf = pygame.transform.scale(surf, size)
- return copy_surface_unscaled(surf, surf)
-
-transform_scale_unscaled = transform_scale
-
-
-def transform_rotate(surf, angle):
- surf = pygame.transform.rotate(surf, angle)
- return copy_surface(surf)
-
-transform_rotate_unscaled = transform_rotate
-
-
-
diff --git a/unrpyc/renpy/display/predict.py b/unrpyc/renpy/display/predict.py
deleted file mode 100644
index 2bdc16f..0000000
--- a/unrpyc/renpy/display/predict.py
+++ /dev/null
@@ -1,156 +0,0 @@
-# Copyright 2004-2013 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.
-
-# This file contains the routines that manage image prediction.
-
-import renpy.display
-
-# Called to indicate an image should be loaded or preloaded. This is
-# a function that takes an image manipulator, set by reset and predict,
-# and winds up bound to either im.cache.get or im.cache.preload_image
-image = None
-
-# The set of displayables we've predicted since reset was last called.
-predicted = set()
-
-# A flag that indicates if we're currently predicting.
-predicting = False
-
-# A list of (screen name, argument dict) tuples, giving the screens we'd
-# like to predict.
-screens = [ ]
-
-def displayable(d):
- """
- Called to predict that the displayable `d` will be shown.
- """
-
- if d is None:
- return
-
- if d not in predicted:
- predicted.add(d)
- d.visit_all(lambda i : i.predict_one())
-
-def screen(_screen_name, *args, **kwargs):
- """
- Called to predict that the named screen is about to be shown
- with the given arguments.
- """
-
- screens.append((_screen_name, args, kwargs))
-
-
-def reset():
- global image
- image = renpy.display.im.cache.get
- predicted.clear()
- del screens[:]
-
-
-def prediction_coroutine(root_widget):
- """
- The image prediction co-routine. This predicts the images that can
- be loaded in the near future, and passes them to the image cache's
- preload_image method to be queued up for loading.
-
- The .send should be called with True to do a expensive prediction,
- and with False to either do an inexpensive prediction or no
- prediction at all.
-
- Returns True if there's more predicting to be done, or False
- if there's no more predicting worth doing.
- """
-
- global predicting
-
- # Wait to be told to start.
- yield True
-
- # Start the prediction thread (to clean out the cache).
- renpy.display.im.cache.start_prediction()
-
- # Set up the image prediction method.
- global image
- image = renpy.display.im.cache.preload_image
-
- # Predict images that are going to be reached in the next few
- # clicks.
- predicting = True
-
- for _i in renpy.game.context().predict():
-
- predicting = False
- yield True
- predicting = True
-
- # If there's a parent context, predict we'll be returning to it
- # shortly. Otherwise, call the functions in
- # config.predict_callbacks.
-
- if len(renpy.game.contexts) >= 2:
- sls = renpy.game.contexts[-2].scene_lists
-
- for l in sls.layers.values():
- for sle in l:
- try:
- displayable(sle.displayable)
- except:
- pass
-
- else:
- for i in renpy.config.predict_callbacks:
- i()
-
- predicting = False
-
- while not (yield True):
- continue
-
- # Predict things (especially screens) that are reachable through
- # an action.
- predicting = True
-
- try:
- root_widget.visit_all(lambda i : i.predict_one_action())
- except:
- pass
-
- predicting = False
-
- # Predict the screens themselves.
- for name, args, kwargs in screens:
- while not (yield True):
- continue
-
- predicting = True
-
- try:
- renpy.display.screen.predict_screen(name, *args, **kwargs)
- except:
- if renpy.config.debug_image_cache:
- renpy.display.ic_log.write("While predicting screen %s %r", name, kwargs)
- renpy.display.ic_log.exception()
-
- predicting = False
-
- yield False
-
diff --git a/unrpyc/renpy/display/presplash.py b/unrpyc/renpy/display/presplash.py
deleted file mode 100644
index 54717fd..0000000
--- a/unrpyc/renpy/display/presplash.py
+++ /dev/null
@@ -1,113 +0,0 @@
-# Copyright 2004-2013 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.
-
-# Pre-splash code. The goal of this code is to try to get a pre-splash
-# screen up as soon as possible, to let the user know something is
-# going on.
-
-# The presplash process, if any.
-proc = None
-
-# Called from the main process. This determines if
-# we're even doing presplash, and if so what will be shown to the
-# user. If it decides to show something to the user, uses subprocess
-# to actually handle the showing.
-def start(basedir, gamedir):
- import os.path
-
- if "RENPY_LESS_UPDATES" in os.environ:
- return
-
- global proc
-
- filenames = [ "/presplash.png", "/presplash.jpg" ]
- for fn in filenames:
- fn = gamedir + fn
- if os.path.exists(fn):
- break
- else:
- return
-
- try:
- import subprocess
- import sys
-
- cmd = [sys.executable, "-EOO", sys.argv[0], "show", "presplash", fn]
-
- def fsencode(s):
- if isinstance(s, str):
- return s
-
- return s.encode(sys.getfilesystemencoding() or "utf-8", "replace")
-
- proc = subprocess.Popen([ fsencode(i) for i in cmd ], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
- except:
- pass
-
-
-# Called just before we initialize the display for real, to
-# hide the splash, and terminate window centering.
-def end():
-
- global proc
-
- if not proc:
- return
-
- proc.stdin.close()
- proc.wait()
-
- proc = None
-
-# Called in the presplash process, to actually display the presplash.
-def show(fn):
-
- import pygame.display
- import pygame.constants
- import sys
- import os
-
- os.environ['SDL_VIDEO_CENTERED'] = "1"
-
- try:
- import pygame.macosx
- pygame.macosx.init() #@UndefinedVariable
- except:
- pass
-
- try:
- import pygame.macosx #@Reimport
- pygame.macosx.Video_AutoInit()
- except:
- pass
-
- pygame.display.init()
-
- img = pygame.image.load(fn, fn)
- screen = pygame.display.set_mode(img.get_size(), pygame.constants.NOFRAME)
- screen.blit(img, (0, 0))
- pygame.display.update()
-
- sys.stdout.write("READY\r\n")
- sys.stdout.flush()
- sys.stdin.read()
-
- sys.exit(0)
diff --git a/unrpyc/renpy/display/render.pxd b/unrpyc/renpy/display/render.pxd
deleted file mode 100644
index a4d8d68..0000000
--- a/unrpyc/renpy/display/render.pxd
+++ /dev/null
@@ -1,47 +0,0 @@
-cdef class Matrix2D:
- cdef public double xdx
- cdef public double xdy
- cdef public double ydx
- cdef public double ydy
-
- cpdef tuple transform(Matrix2D self, double x, double y)
-
-cdef class Render:
-
- cdef public bint mark, cache_killed
-
- cdef public float width, height
- cdef public object layer_name
-
- cdef public list children
- cdef public set parents
- cdef public list depends_on_list
-
- cdef public int operation
- cdef public double operation_complete
- cdef public bint operation_alpha
- cdef public object operation_parameter
-
- cdef public Matrix2D forward, reverse
- cdef public double alpha
-
- cdef public list focuses
- cdef public list pass_focuses
- cdef public object draw_func
- cdef public object render_of
-
- cdef public bint opaque
- cdef public list visible_children
-
- cdef public bint clipping
-
- cdef public object surface, alpha_surface, half_cache
-
- cdef public bint modal
-
- cpdef int blit(Render self, source, tuple pos, object focus=*, object main=*, object index=*)
- cpdef int subpixel_blit(Render self, source, tuple pos, object focus=*, object main=*, object index=*)
-
-
-cpdef render(object d, object widtho, object heighto, double st, double at)
-
diff --git a/unrpyc/renpy/display/render.pyx b/unrpyc/renpy/display/render.pyx
deleted file mode 100644
index 5aa45a0..0000000
--- a/unrpyc/renpy/display/render.pyx
+++ /dev/null
@@ -1,1174 +0,0 @@
-#cython: profile=False
-# Copyright 2004-2013 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 collections
-import pygame
-import threading
-import renpy
-import gc
-
-# We grab the blit lock each time it is necessary to blit
-# something. This allows call to the pygame.transform functions to
-# disable blitting, should it prove necessary.
-blit_lock = threading.Condition()
-
-# This is a dictionary containing all the renders that we know of. It's a
-# map from displayable to dictionaries containing the render of that
-# displayable.
-render_cache = collections.defaultdict(dict)
-
-# The queue of redraws. A list of (time, displayable) pairs.
-redraw_queue = [ ]
-
-# The render returned from render_screen.
-screen_render = None
-
-# A list of renders the system knows about, and thinks are still alive.
-cdef list live_renders
-live_renders = [ ]
-
-# A copy of renpy.display.interface.frame_time, for speed reasons.
-cdef double frame_time
-frame_time = 0
-
-def free_memory():
- """
- Frees memory used by the render system.
- """
-
- global screen_render
- screen_render = None
-
- mark_sweep()
-
- render_cache.clear()
-
- # This can hang onto a render.
- renpy.display.interface.surftree = None
-
-
-def check_at_shutdown():
- """
- This is called at shutdown time to check that everything went okay.
- The big thing it checks for is memory leaks.
- """
-
- if not renpy.config.developer:
- return
-
- free_memory()
-
- gc.collect()
- l = gc.get_objects()
-
- count = 0
- objects = gc.get_objects()
-
- for i in objects:
- if isinstance(i, Render):
- count += 1
-
- if count:
- raise Exception("%d Renders are alive at shutdown. This is probably a memory leak bug in Ren'Py." % count)
-
-
-
-cpdef render(d, object widtho, object heighto, double st, double at):
- """
- :doc: udd_utility
- :args: (d, width, height, st, at)
-
- Causes a displayable to be rendered, and a renpy.Render object to
- be returned.
-
- `d`
- The displayable to render.
-
- `width`, `height`
- The width and height available for the displayable to render into.
-
- `st`, `at`
- The shown and animation timebases.
-
- Renders returned by this object may be cached, and should not be modified
- once they have been retrieved.
- """
-
- cdef float width, height
- cdef float orig_width, orig_height
- cdef tuple orig_wh, wh
- cdef dict render_cache_d
- cdef Render rv
-
- orig_wh = (widtho, heighto, frame_time-st, frame_time-at)
-
- render_cache_d = render_cache[d]
- rv = render_cache_d.get(orig_wh, None)
-
- if rv is not None:
- return rv
-
- orig_width = width = widtho
- orig_height = height = heighto
-
- style = d.style
- xmaximum = style.xmaximum
- ymaximum = style.ymaximum
-
- if xmaximum is not None:
- if isinstance(xmaximum, float):
- width = width * xmaximum
- else:
- width = min(xmaximum, width)
-
- if ymaximum is not None:
- if isinstance(ymaximum, float):
- height = height * ymaximum
- else:
- height = min(ymaximum, height)
-
- if width < 0:
- width = 0
- if height < 0:
- height = 0
-
- if orig_width != width or orig_height != height:
- widtho = width
- heighto = height
- wh = (widtho, heighto, frame_time-st, frame_time-at)
- rv = render_cache_d.get(wh, None)
-
- if rv is not None:
- return rv
-
- else:
- wh = orig_wh
-
- rv = d.render(widtho, heighto, st, at)
-
- rv.render_of.append(d)
-
- if style.clipping:
- rv = rv.subsurface((0, 0, rv.width, rv.height), focus=True)
- rv.render_of.append(d)
-
- render_cache_d[wh] = rv
-
- if wh is not orig_wh:
- render_cache_d[orig_wh] = rv
-
- return rv
-
-
-# This is true if something has been invalidated, and a redraw needs
-# to occur. It's automatically cleared to False at the end of each
-# redraw.
-invalidated = False
-
-def invalidate(d):
- """
- Removes d from the render cache. If we're not in a redraw, triggers
- a redraw to start.
- """
-
- global invalidated
-
- if d in render_cache:
- for v in render_cache[d].values():
- v.kill_cache()
-
- invalidated = True
-
-
-def process_redraws():
- """
- Called to determine if any redraws are pending. Returns true if we
- need to redraw the screen now, false otherwise.
- """
-
- global redraw_queue
-
- redraw_queue.sort()
-
- now = renpy.display.core.get_time()
- rv = invalidated
-
- new_redraw_queue = [ ]
- seen = set()
-
- for t in redraw_queue:
- when, d = t
-
- if d in seen:
- continue
-
- seen.add(d)
-
- if d not in render_cache:
- continue
-
- if when <= now:
- # Remove this displayable and all its parents from the
- # render cache. But don't kill them yet, as that will kill the
- # children that we want to reuse.
-
- for v in render_cache[d].values():
- v.kill_cache()
-
- rv = True
-
- else:
- new_redraw_queue.append(t)
-
- redraw_queue = new_redraw_queue
-
- return rv
-
-
-def redraw_time():
- """
- Returns the time at which the next redraw is scheduled.
- """
-
- if redraw_queue:
- return redraw_queue[0][0]
-
- return None
-
-
-def redraw(d, when):
- """
- :doc: udd_utility
-
- Causes the displayable `d` to be redrawn after `when` seconds have
- elapsed.
- """
-
- if not renpy.game.interface:
- return
-
- redraw_queue.append((when + renpy.game.interface.frame_time, d))
-
-
-cdef class Matrix2D:
- """
- This represents a 2d matrix that can be used to transform
- points and things like that.
- """
-
- def __getstate__(self):
- return dict(
- xdx = self.xdx,
- xdy = self.xdy,
- ydx = self.ydx,
- ydy = self.ydy)
-
- def __setstate__(self, state):
- self.xdx = state['xdx']
- self.xdy = state['xdy']
- self.ydx = state['ydx']
- self.ydy = state['ydy']
-
- def __init__(Matrix2D self, double xdx, double xdy, double ydx, double ydy):
- self.xdx = xdx
- self.xdy = xdy
- self.ydx = ydx
- self.ydy = ydy
-
- cpdef tuple transform(Matrix2D self, double x, double y):
- return (x * self.xdx + y * self.xdy), (x * self.ydx + y * self.ydy)
-
- def __mul__(Matrix2D self, Matrix2D other):
- return Matrix2D(
- other.xdx * self.xdx + other.xdy * self.ydx,
- other.xdx * self.xdy + other.xdy * self.ydy,
- other.ydx * self.xdx + other.ydy * self.ydx,
- other.ydx * self.xdy + other.ydy * self.ydy)
-
- def __repr__(self):
- return "Matrix2D(xdx=%f, xdy=%f, ydx=%f, ydy=%f)" % (self.xdx, self.xdy, self.ydx, self.ydy)
-
-IDENTITY = Matrix2D(1, 0, 0, 1)
-
-def take_focuses(focuses):
- """
- Adds a list of rectangular focus regions to the focuses list.
- """
-
- screen_render.take_focuses(
- 0, 0, screen_render.width, screen_render.height,
- IDENTITY, 0, 0, focuses)
-
-# The result of focus_at_point for a modal render. This overrides any
-# specific focus from below us.
-Modal = object()
-
-def focus_at_point(x, y):
- """
- Returns a focus object corresponding to the uppermost displayable
- at point, or None if nothing focusable is at point.
- """
-
- if screen_render is None:
- return None
-
- cf = screen_render.focus_at_point(x, y)
- if cf is None or cf is Modal:
- return None
- else:
- d, arg = cf
- return renpy.display.focus.Focus(d, arg, None, None, None, None)
-
-
-def mutated_surface(surf):
- """
- Called to indicate that the given surface has changed.
- """
-
- renpy.display.draw.mutated_surface(surf)
-
-
-def render_screen(root, width, height):
- """
- Renders `root` (a displayable) as the root of a screen with the given
- `width` and `height`.
- """
-
-
-
- global old_screen_render
- global screen_render
- global invalidated
- global frame_time
-
- frame_time = renpy.display.interface.frame_time
-
- rv = render(root, width, height, 0, 0)
- screen_render = rv
-
- invalidated = False
-
- rv.is_opaque()
-
- return rv
-
-def mark_sweep():
- """
- This performs mark-and-sweep garbage collection on the live_renders
- list.
- """
-
- global live_renders
-
- cdef list worklist
- cdef int i
- cdef Render r, j
-
- worklist = [ ]
-
- if screen_render is not None:
- worklist.append(screen_render)
-
- i = 0
-
- while i < len(worklist):
- r = worklist[i]
-
- for j in r.depends_on_list:
- if not j.mark:
- j.mark = True
- worklist.append(j)
-
- i += 1
-
- for r in live_renders:
- if not r.mark:
- r.kill_cache()
- else:
- r.mark = False
-
- live_renders = worklist
-
-def compute_subline(sx0, sw, cx0, cw):
- """
- Given a source line (start sx0, width sw) and a crop line (cx0, cw),
- return three things:
-
- * The offset of the portion of the source line that overlaps with
- the crop line, relative to the crop line.
- * The offset of the portion of the source line that overlaps with the
- the crop line, relative to the source line.
- * The length of the overlap in pixels. (can be <= 0)
- """
-
- sx1 = sx0 + sw
- cx1 = cx0 + cw
-
- if sx0 > cx0:
- start = sx0
- else:
- start = cx0
-
- offset = start - cx0
- crop = start - sx0
-
- if sx1 < cx1:
- width = sx1 - start
- else:
- width = cx1 - start
-
-
- return offset, crop, width
-
-
-
-
-# Possible operations that can be done as part of a render.
-BLIT = 0
-DISSOLVE = 1
-IMAGEDISSOLVE = 2
-PIXELLATE = 3
-
-cdef class Render:
-
- def __init__(Render self, float width, float height, draw_func=None, layer_name=None, bint opaque=None): #@DuplicatedSignature
- """
- Creates a new render corresponding to the given widget with
- the specified width and height.
-
- If `layer_name` is given, then this render corresponds to a
- layer.
- """
-
- # The mark bit, used for mark/sweep-style garbage collection of
- # renders.
- self.mark = False
-
- # Is has this render been removed from the cache?
- self.cache_killed = False
-
-
- self.width = width
- self.height = height
-
- self.layer_name = layer_name
-
- # A list of (surface/render, xoffset, yoffset, focus, main) tuples, ordered from
- # back to front.
- self.children = [ ]
-
- # The set of renders that either have us as children, or depend on
- # us.
- self.parents = set()
-
- # The renders we depend on, including our children.
- self.depends_on_list = [ ]
-
- # The operation we're performing. (BLIT, DISSOLVE, OR IMAGE_DISSOLVE)
- self.operation = BLIT
-
- # The fraction of the operation that is complete.
- self.operation_complete = 0.0
-
- # Should the dissolve operations preserve alpha?
- self.operation_alpha = False
-
- # The parameter to the operation.
- self.operation_parameter = 0
-
- # Forward is used to transform from screen coordinates to child
- # coordinates.
- # Reverse is used to transform from child coordinates to screen
- # coordinates.
- #
- # For performance reasons, these aren't used to transform the
- # x and y offsets found in self.children. Those offsets should
- # be of the (0, 0) point in the child coordinate space.
- self.forward = None
- self.reverse = None
-
- # This is used to adjust the alpha of children of this render.
- self.alpha = 1
-
- # A list of focus regions in this displayable.
- self.focuses = None
-
- # Other renders that we should pass focus onto.
- self.pass_focuses = None
-
- # The displayable(s) that this is a render of. (Set by render)
- self.render_of = [ ]
-
- # If set, this is a function that's called to draw this render
- # instead of the default.
- self.draw_func = draw_func
-
- # Is this displayable opaque? (May be set on init, or later on
- # if we have opaque children.) This may be True, False, or None
- # to indicate we don't know yet.
- self.opaque = opaque
-
- # A list of our visible children. (That is, children above and
- # including our uppermost opaque child.) If nothing is opaque,
- # includes all children.
- self.visible_children = self.children
-
- # Should children be clipped to a rectangle?
- self.clipping = False
-
- # Caches of the texture created by rendering this surface.
- self.surface = None
- self.alpha_surface = None
-
- # Cache of the texture created by rendering this surface at half size.
- # (This is set in gldraw.)
- self.half_cache = None
-
- # Are we modal?
- self.modal = False
-
- live_renders.append(self)
-
- def __repr__(self): #@DuplicatedSignature
- return "<Render %x of %r>" % (id(self), self.render_of)
-
- def __getstate__(self): #@DuplicatedSignature
- if renpy.config.developer:
- raise Exception("Can't pickle a Render.")
- else:
- return { }
-
- def __setstate__(self, state): #@DuplicatedSignature
- return
-
- cpdef int blit(Render self, source, tuple pos, object focus=True, object main=True, object index=None):
- """
- Blits `source` (a Render or Surface) to this Render, offset by
- xo and yo.
-
- If `focus` is true, then focuses are added from the child to the
- parent.
-
- This will only blit on integer pixel boundaries.
- """
-
- (xo, yo) = pos
-
- if source is self:
- raise Exception("Blitting to self.")
-
- xo = int(xo)
- yo = int(yo)
-
- if index is None:
- self.children.append((source, xo, yo, focus, main))
- else:
- self.children.insert(index, (source, xo, yo, focus, main))
-
- if isinstance(source, Render):
- self.depends_on_list.append(source)
- source.parents.add(self)
-
- return 0
-
- cpdef int subpixel_blit(Render self, source, tuple pos, object focus=True, object main=True, object index=None):
- """
- Blits `source` (a Render or Surface) to this Render, offset by
- xo and yo.
-
- If `focus` is true, then focuses are added from the child to the
- parent.
-
- This blits at fractional pixel boundaries.
- """
-
- (xo, yo) = pos
-
- xo = float(xo)
- yo = float(yo)
-
- if index is None:
- self.children.append((source, xo, yo, focus, main))
- else:
- self.children.insert(index, (source, xo, yo, focus, main))
-
- if isinstance(source, Render):
- self.depends_on_list.append(source)
- source.parents.add(self)
-
- return 0
-
- def get_size(self):
- """
- Returns the size of this Render, a mostly ficticious value
- that's taken from the inputs to the constructor. (As in, we
- don't clip to this size.)
- """
-
- return self.width, self.height
-
-
- def render_to_texture(self, alpha=True):
- """
- Returns a texture constructed from this render. This may return
- a cached textue, if one has already been rendered.
-
- `alpha` is a hint that controls if the surface should have
- alpha or not.
- """
-
- if alpha:
- if self.alpha_surface is not None:
- return self.alpha_surface
- else:
- if self.surface is not None:
- return self.surface
-
- rv = None
-
- opaque = self.is_opaque()
-
- # If we can, reuse a child's texture.
- if opaque or alpha:
-
- if not self.forward and len(self.children) == 1:
- child, x, y, focus, main = self.children[0]
- cw, ch = child.get_size()
- if x <= 0 and y <= 0 and cw + x >= self.width and ch + y >= self.height:
- # Our single child overlaps us.
- if isinstance(child, Render):
- child = child.render_to_texture(alpha)
-
- if x != 0 or y != 0 or cw != self.width or ch != self.height:
- rv = child.subsurface((-x, -y, self.width, self.height))
- else:
- rv = child
-
- # Otherwise, render to a texture.
- if rv is None:
- # is_opaque has already been called.
- rv = renpy.display.draw.render_to_texture(self, alpha)
-
- # Stash and return the surface.
- if alpha:
- self.alpha_surface = rv
- else:
- self.surface = rv
-
- return rv
-
- pygame_surface = render_to_texture
-
- def subsurface(self, rect, focus=False):
- """
- Returns a subsurface of this render. If `focus` is true, then
- the focuses are copied from this render to the child.
- """
-
- (x, y, w, h) = rect
- rv = Render(w, h)
-
- reverse = self.reverse
-
- # This doesn't actually make a subsurface, as we can't easily do
- # so for non-rectangle-aligned renders.
- if (reverse is not None) and (
- reverse.xdx != 1.0 or
- reverse.xdy != 0.0 or
- reverse.ydx != 0.0 or
- reverse.ydy != 1.0):
-
- rv.clipping = True
- rv.blit(self, (-x, -y), focus=focus, main=True)
- return rv
-
- # This is the path that executes for rectangle-aligned surfaces,
- # making an actual subsurface.
-
- for child, cx, cy, cfocus, cmain in self.children:
-
- cw, ch = child.get_size()
- xo, cx, cw = compute_subline(cx, cw, x, w)
- yo, cy, ch = compute_subline(cy, ch, y, h)
-
- if cw <= 0 or ch <= 0:
- continue
-
- crop = (cx, cy, cw, ch)
- offset = (xo, yo)
-
- if isinstance(child, Render):
- newchild = child.subsurface(crop, focus=focus)
- newchild.render_of = child.render_of[:]
- else:
- newchild = child.subsurface(crop)
- renpy.display.draw.mutated_surface(newchild)
-
- rv.blit(newchild, offset, focus=cfocus, main=cmain)
-
- if focus and self.focuses:
-
- for (d, arg, xo, yo, fw, fh, mx, my, mask) in self.focuses:
-
- if xo is None:
- rv.add_focus(d, arg, xo, yo, fw, fh, mx, my, mask)
- continue
-
- xo, cx, fw = compute_subline(xo, fw, x, w)
- yo, cy, fh = compute_subline(yo, fh, y, h)
-
- if cw <= 0 or ch <= 0:
- continue
-
- if mx is not None:
-
- mw, mh = mask.get_size()
-
- mx, mcx, mw = compute_subline(mx, mw, x, w)
- my, mcy, mh = compute_subline(my, mh, y, h)
-
- if mw <= 0 or mh <= 0:
- mx = None
- my = None
- mask = None
- else:
- mask = mask.subsurface((mcx, mcy, mw, mh))
-
- rv.add_focus(d, arg, xo, yo, fw, fh, mx, my, mask)
-
- rv.depends_on(self)
- rv.alpha = self.alpha
- rv.operation = self.operation
- rv.operation_alpha = self.operation_alpha
- rv.operation_complete = self.operation_complete
-
- return rv
-
-
- def depends_on(self, source, focus=False):
- """
- Used to indicate that this render depends on another
- render. Useful, for example, if we use pygame_surface to make
- a surface, and then blit that surface into another render.
- """
-
- if source is self:
- raise Exception("Render depends on itself.")
-
- self.depends_on_list.append(source)
- source.parents.add(self)
-
- if focus:
- if self.pass_focuses is None:
- self.pass_focuses = [ source ]
- else:
- self.pass_focuses.append(source)
-
-
- def kill_cache(self):
- """
- Removes this render and its transitive parents from the cache.
- """
-
- if self.cache_killed:
- return
-
- self.cache_killed = True
-
- for i in self.parents:
- i.kill_cache()
-
- self.parents.clear()
-
- for i in self.depends_on_list:
- if not i.cache_killed:
- i.parents.discard(self)
-
- for ro in self.render_of:
- cache = render_cache[ro]
- for k, v in cache.items():
- if v is self:
- del cache[k]
-
- if not cache:
- del render_cache[ro]
-
- def kill(self):
- """
- Retained for compatibility.
- """
-
- def add_focus(self, d, arg=None, x=0, y=0, w=None, h=None, mx=None, my=None, mask=None):
- """
- This is called to indicate a region of the screen that can be
- focused.
-
- `d` - the displayable that is being focused.
- `arg` - an argument.
-
- The rest of the parameters are a rectangle giving the portion of
- this region corresponding to the focus. If they are all None, than
- this focus is assumed to be the singular full-screen focus.
- """
-
- if mask is not None and mask is not self:
- self.depends_on(mask)
-
- t = (d, arg, x, y, w, h, mx, my, mask)
-
- if self.focuses is None:
- self.focuses = [ t ]
- else:
- self.focuses.append(t)
-
- def take_focuses(self, cminx, cminy, cmaxx, cmaxy, reverse, x, y, focuses): #@DuplicatedSignature
- """
- This adds to focuses Focus objects corresponding to the focuses
- added to this object and its children, transformed into screen
- coordinates.
-
- `cminx`, `cminy`, `cmaxx`, `cmaxy` - The clipping rectangle.
- `reverse` - The transform from render to screen coordinates.
- `x`, `y` - The offset of the upper-left corner of the render.
- `focuses` - The list of focuses to add to.
- """
-
- if self.modal:
- focuses[:] = [ ]
-
- if self.reverse:
- reverse = reverse * self.reverse
-
- if self.focuses:
-
- for (d, arg, xo, yo, w, h, mx, my, mask) in self.focuses:
-
- if xo is None:
- focuses.append(renpy.display.focus.Focus(d, arg, None, None, None, None))
- continue
-
- x1, y1 = reverse.transform(xo, yo)
- x2, y2 = reverse.transform(xo + w, yo + h)
-
- minx = min(x1, x2) + x
- miny = min(y1, y2) + y
- maxx = max(x1, x2) + x
- maxy = max(y1, y2) + y
-
- minx = max(minx, cminx)
- miny = max(miny, cminy)
- maxx = min(maxx, cmaxx)
- maxy = min(maxy, cmaxy)
-
- if minx >= maxx or miny >= maxy:
- continue
-
- focuses.append(renpy.display.focus.Focus(d, arg, minx, miny, maxx - minx, maxy - miny))
-
- if self.clipping:
- cminx = max(cminx, x)
- cminy = max(cminy, y)
- cmaxx = min(cmaxx, x + self.width)
- cmaxy = min(cmaxx, x + self.height)
-
- for child, xo, yo, focus, main in self.children:
- if not focus or not isinstance(child, Render):
- continue
-
- xo, yo = reverse.transform(xo, yo)
- child.take_focuses(cminx, cminy, cmaxx, cmaxy, reverse, x + xo, y + yo, focuses)
-
- if self.pass_focuses:
- for child in self.pass_focuses:
- child.take_focuses(cminx, cminy, cmaxx, cmaxy, reverse, x, y, focuses)
-
- def focus_at_point(self, x, y): #@DuplicatedSignature
- """
- This returns the focus of this object at the given point.
- """
-
- if self.clipping:
- if x < 0 or x >= self.width or y < 0 or y >= self.height:
- return None
-
- rv = None
-
- if self.focuses:
- for (d, arg, xo, yo, w, h, mx, my, mask) in self.focuses:
-
- if xo is None:
- continue
-
- elif mx is not None:
- cx = x - mx
- cy = y - my
-
- if self.forward:
- cx, cy = self.forward.transform(cx, cy)
-
- if mask.is_pixel_opaque(cx, cy):
- rv = d, arg
-
- elif xo <= x < xo + w and yo <= y < yo + h:
- rv = d, arg
-
- for child, xo, yo, focus, main in self.children:
-
- if not focus or not isinstance(child, Render):
- continue
-
- cx = x - xo
- cy = y - yo
-
- if self.forward:
- cx, cy = self.forward.transform(cx, cy)
-
- cf = child.focus_at_point(cx, cy)
- if cf is not None:
- rv = cf
-
- if self.pass_focuses:
- for child in self.pass_focuses:
- cf = child.focus_at_point(x, y)
- if cf is not None:
- rv = cf
-
- if rv is None and self.modal:
- rv = Modal
-
- return rv
-
-
- def main_displayables_at_point(self, x, y, layers, depth=None):
- """
- Returns the displayable at `x`, `y` on one of the layers in
- the set or list `layers`.
- """
-
- rv = [ ]
-
- if x < 0 or y < 0 or x >= self.width or y >= self.height:
- return rv
-
- if depth is not None:
- for d in self.render_of:
- rv.append((depth, self.width, self.height, d))
- depth += 1
- elif self.layer_name in layers:
- depth = 0
-
- for (child, xo, yo, focus, main) in self.children:
- if not main or not isinstance(child, Render):
- continue
-
- cx = x - xo
- cy = y - yo
-
- if self.forward:
- cx, cy = self.forward.transform(cx, cy)
-
- cf = child.main_displayables_at_point(cx, cy, layers, depth)
- rv.extend(cf)
-
- return rv
-
-
- def is_opaque(self):
- """
- Returns true if this displayable is opaque, or False otherwise.
- Also sets self.visible_children.
- """
-
- if self.opaque is not None:
- return self.opaque
-
- # A rotated image is never opaque. (This isn't actually true, but it
- # saves us from the expensive calculations require to prove it is.)
- if self.forward:
- self.opaque = False
- return False
-
- rv = False
- vc = [ ]
-
- for i in self.children:
- child, xo, yo, focus, main = i
-
- if xo <= 0 and yo <= 0:
- cw, ch = child.get_size()
- if cw + xo < self.width or ch + yo < self.height:
- if child.is_opaque():
- vc = [ ]
- rv = True
-
- vc.append(i)
-
- self.visible_children = vc
- self.opaque = rv
- return rv
-
-
- def is_pixel_opaque(self, x, y):
- """
- Determine if the pixel at x and y is opaque or not.
- """
-
- if x < 0 or y < 0 or x >= self.width or y >= self.height:
- return False
-
- if self.is_opaque():
- return True
-
- return renpy.display.draw.is_pixel_opaque(self, x, y)
-
-
- def fill(self, color):
- """
- Fills this Render with the given color.
- """
-
- color = renpy.easy.color(color)
- solid = renpy.display.imagelike.Solid(color)
- surf = render(solid, self.width, self.height, 0, 0)
- self.blit(surf, (0, 0), focus=False, main=False)
-
-
- def canvas(self):
- """
- Returns a canvas object that draws to this Render.
- """
-
- surf = renpy.display.pgrender.surface((self.width, self.height), True)
-
- mutated_surface(surf)
-
- self.blit(surf, (0, 0))
-
- return Canvas(surf)
-
-
-class Canvas(object):
-
- def __init__(self, surf): #@DuplicatedSignature
- self.surf = surf
-
- def rect(self, color, rect, width=0):
-
- try:
- blit_lock.acquire()
- pygame.draw.rect(self.surf,
- renpy.easy.color(color),
- rect,
- width)
- finally:
- blit_lock.release()
-
- def polygon(self, color, pointlist, width=0):
- try:
- blit_lock.acquire()
- pygame.draw.polygon(self.surf,
- renpy.easy.color(color),
- pointlist,
- width)
- finally:
- blit_lock.release()
-
- def circle(self, color, pos, radius, width=0):
-
- try:
- blit_lock.acquire()
- pygame.draw.circle(self.surf,
- renpy.easy.color(color),
- pos,
- radius,
- width)
-
- finally:
- blit_lock.release()
-
- def ellipse(self, color, rect, width=0):
- try:
- blit_lock.acquire()
- pygame.draw.ellipse(self.surf,
- renpy.easy.color(color),
- rect,
- width)
- finally:
- blit_lock.release()
-
-
- def arc(self, color, rect, start_angle, stop_angle, width=1):
- try:
- blit_lock.acquire()
- pygame.draw.arc(self.surf,
- renpy.easy.color(color),
- rect,
- start_angle,
- stop_angle,
- width)
- finally:
- blit_lock.release()
-
-
- def line(self, color, start_pos, end_pos, width=1):
- try:
- blit_lock.acquire()
- pygame.draw.line(self.surf,
- renpy.easy.color(color),
- start_pos,
- end_pos,
- width)
- finally:
- blit_lock.release()
-
- def lines(self, color, closed, pointlist, width=1):
- try:
- blit_lock.acquire()
- pygame.draw.lines(self.surf,
- renpy.easy.color(color),
- closed,
- pointlist,
- width)
- finally:
- blit_lock.release()
-
- def aaline(self, color, startpos, endpos, blend=1):
- try:
- blit_lock.acquire()
- pygame.draw.aaline(self.surf,
- renpy.easy.color(color),
- startpos,
- endpos,
- blend)
- finally:
- blit_lock.release()
-
- def aalines(self, color, closed, pointlist, blend=1):
- try:
- blit_lock.acquire()
- pygame.draw.aalines(self.surf,
- renpy.easy.color(color),
- closed,
- pointlist,
- blend)
- finally:
- blit_lock.release()
diff --git a/unrpyc/renpy/display/scale.py b/unrpyc/renpy/display/scale.py
deleted file mode 100644
index c9efbcc..0000000
--- a/unrpyc/renpy/display/scale.py
+++ /dev/null
@@ -1,109 +0,0 @@
-# Copyright 2004-2013 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.
-
-# This used to hack pygame to support resolution-scaling. Now it just kinda
-# sits here, to provide compatibility with what it used to be.
-
-import pygame
-import renpy.display
-import renpy.display.pgrender as pgrender
-
-import _renpy
-
-##############################################################################
-# The scaling API that's used if we don't enable scaling.
-
-# Gets the real pygame surface.
-def real(s):
- return s
-
-# Scales the number, n.
-def scale(n):
- return n
-
-def real_bilinear(src, size):
- rv = pgrender.surface_unscaled(size, src)
- renpy.display.module.bilinear_scale(src, rv)
- return rv
-
-# Does pygame.transform.scale.
-def real_transform_scale(surf, size):
- return pgrender.transform_scale_unscaled(surf, size)
-
-# Loads an image, without scaling it.
-def image_load_unscaled(f, hint, convert=True):
- rv = pgrender.load_image_unscaled(f, hint)
- return rv
-
-# Saves an image without rescaling.
-def image_save_unscaled(surf, filename):
- pygame.image.save(surf, renpy.exports.fsencode(filename))
-
-# Scales down a surface.
-def surface_scale(full):
- return full
-
-real_renpy_pixellate = _renpy.pixellate
-real_renpy_transform = _renpy.transform
-
-def real_smoothscale(src, size, dest=None):
- """
- This scales src up or down to size. This uses both the pixellate
- and the transform operations to handle the scaling.
- """
-
- width, height = size
- srcwidth, srcheight = src.get_size()
- iwidth, iheight = srcwidth, srcheight
-
- if dest is None:
- dest = pgrender.surface_unscaled(size, src)
-
- if width == 0 or height == 0:
- return dest
-
- xshrink = 1
- yshrink = 1
-
- while iwidth >= width * 2:
- xshrink *= 2
- iwidth /= 2
-
- while iheight >= height * 2:
- yshrink *= 2
- iheight /= 2
-
- if iwidth != srcwidth or iheight != srcheight:
- inter = pgrender.surface_unscaled((iwidth, iheight), src)
- real_renpy_pixellate(src, inter, xshrink, yshrink, 1, 1)
- src = inter
-
- real_renpy_transform(src, dest,
- 0, 0,
- 1.0 * iwidth / width , 0,
- 0, 1.0 * iheight / height,
- precise=1,
- )
-
- return dest
-
-smoothscale = real_smoothscale
-
diff --git a/unrpyc/renpy/display/screen.py b/unrpyc/renpy/display/screen.py
deleted file mode 100644
index 8fd529b..0000000
--- a/unrpyc/renpy/display/screen.py
+++ /dev/null
@@ -1,639 +0,0 @@
-# Copyright 2004-2013 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 renpy.display
-
-class Screen(renpy.object.Object):
- """
- A screen is a collection of widgets that are displayed together.
- This class stores information about the screen.
- """
-
- def __init__(self,
- name,
- function,
- modal="False",
- zorder="0",
- tag=None,
- predict=None,
- variant=None,
- parameters=False):
-
- # The name of this screen.
- if isinstance(name, str):
- name = tuple(name.split())
-
- self.name = name
-
- screens[name[0], variant] = self
-
- # The function that is called to display this screen.
- self.function = function
-
- # Expression: Are we modal? (A modal screen ignores screens under it.)
- self.modal = modal
-
- # Expression: Our zorder.
- self.zorder = zorder
-
- # The tag associated with the screen.
- self.tag = tag or name[0]
-
- # Can this screen be predicted?
- if predict is None:
- predict = renpy.config.predict_screens
-
- self.predict = predict
-
- # True if this screen takes parameters via _args and _kwargs.
- self.parameters = parameters
-
-
-class ScreenDisplayable(renpy.display.layout.Container):
- """
- A screen is a collection of widgets that are displayed together. This
- class is responsible for managing the display of a screen.
- """
-
- nosave = [ 'screen', 'child', 'transforms', 'widgets', 'old_widgets', 'old_transforms' ]
-
- restarting = False
-
- def after_setstate(self):
- self.screen = get_screen_variant(self.screen_name[0])
- self.child = None
- self.transforms = { }
- self.widgets = { }
- self.old_widgets = None
- self.old_transforms = None
-
- def __init__(self, screen, tag, layer, widget_properties={}, scope={}, **properties):
-
- super(ScreenDisplayable, self).__init__(**properties)
-
- # Stash the properties, so we can re-create the screen.
- self.properties = properties
-
- # The screen, and it's name. (The name is used to look up the
- # screen on save.)
- self.screen = screen
- self.screen_name = screen.name
-
- # The tag and layer screen was displayed with.
- self.tag = tag
- self.layer = layer
-
- # The scope associated with this statement. This is passed in
- # as keyword arguments to the displayable.
- self.scope = renpy.python.RevertableDict(scope)
-
- # The child associated with this screen.
- self.child = None
-
- # Widget properties given to this screen the last time it was
- # shown.
- self.widget_properties = widget_properties
-
- # A map from name to the widget with that name.
- self.widgets = { }
-
- if tag and layer:
- old_screen = get_screen(tag, layer)
- else:
- old_screen = None
-
- # A map from name to the transform with that name. (This is
- # taken from the old version of the screen, if it exists.
- if old_screen is not None:
- self.transforms = old_screen.transforms
- else:
- self.transforms = { }
-
- # What widgets and transforms were the last time this screen was
- # updated. Used to communicate with the ui module, and only
- # valid during an update - not used at other times.
- self.old_widgets = None
- self.old_transforms = None
-
- # Should we transfer data from the old_screen? This becomes
- # true once this screen finishes updating for the first time,
- # and also while we're using something.
- self.old_transfers = (old_screen and old_screen.screen_name == self.screen_name)
-
- # The current transform event, and the last transform event to
- # be processed.
- self.current_transform_event = None
-
- # A dict-set of widgets (by id) that have been hidden from us.
- self.hidden_widgets = { }
-
- # Are we hiding?
- self.hiding = False
-
- # Are we restarting?
- self.restarting = False
-
- # Modal and zorder.
- self.modal = renpy.python.py_eval(self.screen.modal, locals=self.scope)
- self.zorder = renpy.python.py_eval(self.screen.zorder, locals=self.scope)
-
- def __repr__(self):
- return "<ScreenDisplayable: %r>" % (self.screen_name,)
-
- def visit(self):
- return [ self.child ]
-
- def per_interact(self):
- renpy.display.render.redraw(self, 0)
- self.update()
-
- def set_transform_event(self, event):
- super(ScreenDisplayable, self).set_transform_event(event)
- self.current_transform_event = event
-
- def find_focusable(self, callback, focus_name):
- if self.child and not self.hiding:
- self.child.find_focusable(callback, focus_name)
-
- def _hide(self, st, at, kind):
-
- if self.hiding:
- hid = self
- else:
- hid = ScreenDisplayable(self.screen, self.tag, self.layer, self.widget_properties, self.scope, **self.properties)
- hid.transforms = self.transforms.copy()
- hid.widgets = self.widgets.copy()
- hid.old_transfers = True
-
- hid.hiding = True
-
- hid.current_transform_event = kind
- hid.update()
-
- renpy.display.render.redraw(hid, 0)
-
- rv = None
-
- # Compute the reverse of transforms and widgets.
- reverse_transforms = dict((id(v), k) for k, v in hid.transforms.items())
- reverse_widgets = dict((id(v), k) for k, v in hid.widgets.items())
-
- # Assumption: the only displayables that can keep us around
- # are Transforms that handle hide.
-
- # Iterate over our immediate children, trying to hide them.
- for d in list(hid.child.children):
-
- id_d = id(d)
-
- # If we have a transform, call its _hide method. If that comes
- # back non-None, store the new transform, and keep us alive.
- #
- # Otherwise, remove the child.
- name = reverse_transforms.get(id_d, None)
-
- if name is not None:
- c = d._hide(st, at, kind)
-
- if c is not None:
- hid.transforms[name] = c
- rv = hid
- else:
- hid.hidden_widgets[name] = True
- hid.child.remove(d)
-
- continue
-
- # Remove any non-transform children.
- name = reverse_widgets.get(id_d, None)
-
- if name is not None:
- hid.hidden_widgets[name] = True
- hid.child.remove(d)
-
- return rv
-
- def update(self):
-
- # If we're restarting, do not update - the update can use variables
- # that are no longer in scope.
- if self.restarting:
- if not self.child:
- self.child = renpy.display.layout.Null()
-
- return self.widgets
-
- # Update _current_screen
- global _current_screen
- old_screen = _current_screen
- _current_screen = self
-
- # Cycle widgets and transforms.
- self.old_widgets = self.widgets
- self.old_transforms = self.transforms
- self.widgets = { }
- self.transforms = { }
-
- # Render the child.
- old_ui_screen = renpy.ui.screen
- renpy.ui.screen = self
-
- renpy.ui.detached()
- self.child = renpy.ui.fixed(focus="_screen_" + "_".join(self.screen_name))
- self.children = [ self.child ]
-
- self.scope["_scope"] = self.scope
- self.scope["_name"] = 0
-
- self.screen.function(**self.scope)
-
- renpy.ui.close()
-
- renpy.ui.screen = old_ui_screen
- _current_screen = old_screen
-
- # Visit all the children, to get them started.
- self.child.visit_all(lambda c : c.per_interact())
-
- # Finish up.
- self.old_widgets = None
- self.old_transforms = None
- self.old_transfers = True
-
- if self.current_transform_event:
-
- for i in self.child.children:
- i.set_transform_event(self.current_transform_event)
-
- self.current_transform_event = None
-
- return self.widgets
-
- def render(self, w, h, st, at):
-
- if not self.child:
- self.update()
-
- child = renpy.display.render.render(self.child, w, h, st, at)
-
- rv = renpy.display.render.Render(w, h)
-
- rv.blit(child, (0, 0), focus=not self.hiding, main=not self.hiding)
- rv.modal = self.modal and not self.hiding
-
- return rv
-
- def get_placement(self):
- if not self.child:
- self.update()
-
- return self.child.get_placement()
-
- def event(self, ev, x, y, st):
-
- if self.hiding:
- return
-
- global _current_screen
- old_screen = _current_screen
- _current_screen = self
-
- rv = self.child.event(ev, x, y, st)
-
- _current_screen = old_screen
-
- if rv is not None:
- return rv
-
- if self.modal:
- raise renpy.display.layout.IgnoreLayers()
-
-
-# The name of the screen that is currently being displayed, or
-# None if no screen is being currently displayed.
-_current_screen = None
-
-# A map from (screen_name, variant) tuples to screen.
-screens = { }
-
-def get_screen_variant(name):
- """
- Get a variant screen object for `name`.
- """
-
- for i in renpy.config.variants:
- rv = screens.get((name, i), None)
- if rv is not None:
- return rv
-
- return None
-
-def define_screen(*args, **kwargs):
- """
- :doc: screens
- :args: (name, function, modal="False", zorder="0", tag=None, variant=None)
-
- Defines a screen with `name`, which should be a string.
-
- `function`
- The function that is called to display the screen. The
- function is called with the screen scope as keyword
- arguments. It should ignore additional keyword arguments.
-
- The function should call the ui functions to add things to the
- screen.
-
- `modal`
- A string that, when evaluated, determines of the created
- screen should be modal. A modal screen prevents screens
- underneath it from receiving input events.
-
- `zorder`
- A string that, when evaluated, should be an integer. The integer
- controls the order in which screens are displayed. A screen
- with a greater zorder number is displayed above screens with a
- lesser zorder number.
-
- `tag`
- The tag associated with this screen. When the screen is shown,
- it replaces any other screen with the same tag. The tag
- defaults to the name of the screen.
-
- `predict`
- If true, this screen can be loaded for image prediction. If false,
- it can't. Defaults to true.
-
- `variant`
- String. Gives the variant of the screen to use.
-
- """
-
- Screen(*args, **kwargs)
-
-
-
-def get_screen(name, layer="screens"):
- """
- :doc: screens
-
- Returns the ScreenDisplayable with the given `tag`, on
- `layer`. If no displayable with the tag is not found, it is
- interpreted as screen name. If it's still not found, None is returned.
- """
-
- if isinstance(name, str):
- name = tuple(name.split())
-
- tag = name[0]
-
- sl = renpy.exports.scene_lists()
-
- sd = sl.get_displayable_by_tag(layer, tag)
-
- if sd is None:
- sd = sl.get_displayable_by_name(layer, name)
-
- return sd
-
-def has_screen(name):
- """
- Returns true if a screen with the given name exists.
- """
-
- if not isinstance(name, tuple):
- name = tuple(name.split())
-
- if not name:
- return False
-
- if get_screen_variant(name[0]):
- return True
- else:
- return False
-
-def show_screen(_screen_name, *_args, **kwargs):
- """
- :doc: screens
-
- The programmatic equivalent of the show screen statement.
-
- Shows the named screen. This takes the following keyword arguments:
-
- `_screen_name`
- The name of the screen to show.
- `_layer`
- The layer to show the screen on.
- `_tag`
- The tag to show the screen with. If not specified, defaults to
- the tag associated with the screen. It that's not specified,
- defaults to the name of the screen.,
- `_widget_properties`
- A map from the id of a widget to a property name -> property
- value map. When a widget with that id is shown by the screen,
- the specified properties are added to it.
- `_transient`
- If true, the screen will be automatically hidden at the end of
- the current interaction.
-
- Keyword arguments not beginning with underscore (_) are used to
- initialize the screen's scope.
- """
-
- _layer = kwargs.pop("_layer", "screens")
- _tag = kwargs.pop("_tag", None)
- _widget_properties = kwargs.pop("_widget_properties", {})
- _transient = kwargs.pop("_transient", False)
-
- name = _screen_name
-
- if not isinstance(name, tuple):
- name = tuple(name.split())
-
- screen = get_screen_variant(name[0])
-
- if screen is None:
- raise Exception("Screen %s is not known.\n" % (name[0],))
-
- if _tag is None:
- _tag = screen.tag
-
- scope = { }
-
- if screen.parameters:
- scope["_kwargs" ] = kwargs
- scope["_args"] = _args
- else:
- scope.update(kwargs)
-
- d = ScreenDisplayable(screen, _tag, _layer, _widget_properties, scope)
- renpy.exports.show(name, tag=_tag, what=d, layer=_layer, zorder=d.zorder, transient=_transient, munge_name=False)
-
-
-def predict_screen(_screen_name, *_args, **kwargs):
- """
- Predicts the displayables that make up the given screen.
-
- `_screen_name`
- The name of the screen to show.
- `_widget_properties`
- A map from the id of a widget to a property name -> property
- value map. When a widget with that id is shown by the screen,
- the specified properties are added to it.
-
- Keyword arguments not beginning with underscore (_) are used to
- initialize the screen's scope.
- """
-
- _widget_properties = kwargs.pop("_widget_properties", {})
- _scope = kwargs.pop
-
- kwargs["_kwargs" ] = kwargs.copy()
- kwargs["_args"] = _args
-
- name = _screen_name
-
- if renpy.config.debug_image_cache:
- renpy.display.ic_log.write("Predict screen %s", name)
-
- if not isinstance(name, tuple):
- name = tuple(name.split())
-
- screen = get_screen_variant(name[0])
-
- scope = { }
-
- if screen.parameters:
- scope["_kwargs" ] = kwargs
- scope["_args"] = _args
- else:
- scope.update(kwargs)
-
- try:
-
- if screen is None:
- raise Exception("Screen %s is not known.\n" % (name[0],))
-
- if not screen.predict:
- return
-
- d = ScreenDisplayable(screen, None, None, _widget_properties, scope)
-
- d.update()
- renpy.display.predict.displayable(d)
-
- except:
- if renpy.config.debug_image_cache:
- import traceback
-
- print("While predicting screen", screen)
- traceback.print_exc()
-
- renpy.ui.reset()
-
-
-def hide_screen(tag, layer='screens'):
- """
- :doc: screens
-
- The programmatic equivalent of the hide screen statement.
-
- Hides the screen with `tag` on `layer`.
- """
-
- screen = get_screen(tag, layer)
-
- if screen is not None:
- renpy.exports.hide(screen.tag, layer=layer)
-
-def use_screen(_screen_name, *_args, **kwargs):
-
- _name = kwargs.pop("_name", ())
- _scope = kwargs.pop("_scope", { })
-
- name = _screen_name
-
- if not isinstance(name, tuple):
- name = tuple(name.split())
-
- screen = get_screen_variant(name[0])
-
- if screen is None:
- raise Exception("Screen %r is not known." % name)
-
- old_transfers = _current_screen.old_transfers
- _current_screen.old_transfers = True
-
- scope = _scope.copy()
-
- if screen.parameters:
- scope["_kwargs"] = kwargs
- scope["_args"] = _args
- else:
- scope.update(kwargs)
-
- scope["_scope"] = scope
- scope["_name"] = (_name, name)
-
- screen.function(**scope)
-
- _current_screen.old_transfers = old_transfers
-
-def current_screen():
- return _current_screen
-
-def get_widget(screen, id, layer='screens'): #@ReservedAssignment
- """
- :doc: screens
-
- From the `screen` on `layer`, returns the widget with
- `id`. Returns None if the screen doesn't exist, or there is no
- widget with that id on the screen.
- """
-
- if screen is None:
- screen = current_screen()
- else:
- screen = get_screen(screen, layer)
-
- if not isinstance(screen, ScreenDisplayable):
- return None
-
- if screen.child is None:
- screen.update()
-
- rv = screen.widgets.get(id, None)
- return rv
-
-def before_restart():
- """
- This is called before Ren'Py restarts to put the screens into restart
- mode, which prevents crashes due to variables being used that are no
- longer defined.
- """
-
- for k, layer in renpy.display.interface.old_scene.items():
- if k is None:
- continue
-
- for i in layer.children:
- if isinstance(i, ScreenDisplayable):
- i.restarting = True
-
diff --git a/unrpyc/renpy/display/swdraw.py b/unrpyc/renpy/display/swdraw.py
deleted file mode 100644
index d3ecb51..0000000
--- a/unrpyc/renpy/display/swdraw.py
+++ /dev/null
@@ -1,1102 +0,0 @@
-# Copyright 2004-2013 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 renpy.display
-import pygame
-import math
-import weakref
-import time
-import os
-
-from renpy.display.render import blit_lock, IDENTITY, BLIT, DISSOLVE, IMAGEDISSOLVE, PIXELLATE
-
-# A map from cached surface to rle version of cached surface.
-rle_cache = weakref.WeakKeyDictionary()
-
-class Clipper(object):
- """
- This is used to calculate the clipping rectangle and update rectangles
- used for a particular draw of the screen.
- """
-
- def __init__(self):
-
- # Lists of (x0, y0, x1, y1, clip, surface, transform) tuples,
- # representing how a displayable is drawn to the screen.
- self.blits = [ ]
- self.old_blits = [ ]
-
- # Sets of (x0, y0, x1, y1) tuples, representing areas that
- # aren't part of any displayable.
- self.forced = set()
- self.old_forced = set()
-
- # The set of surfaces that have been mutated recently.
- self.mutated = set()
-
- def compute(self, full_redraw):
- """
- This returns a clipping rectangle, and a list of update rectangles
- that cover the changes between the old and new frames.
- """
-
- # First, get things out of the fields, and update them. This
- # allows us to just return without having to do any cleanup
- # code.
- bl0 = self.old_blits
- bl1 = self.blits
- old_forced = self.old_forced
- forced = self.forced
- mutated = self.mutated
-
- self.old_blits = bl1
- self.blits = [ ]
- self.old_forced = forced
- self.forced = set()
- self.mutated = set()
-
- sw = renpy.config.screen_width
- sh = renpy.config.screen_height
- sa = sw * sh
-
- # A tuple representing the size of the fullscreen.
- fullscreen = (0, 0, sw, sh)
-
- # Check to see if a full redraw has been forced, and return
- # early.
- if full_redraw:
- return fullscreen, [ fullscreen ]
-
- # Quick checks to see if a dissolve is happening, or something like
- # that.
- changes = forced | old_forced
-
- if fullscreen in changes:
- return fullscreen, [ fullscreen ]
-
- # Compute the differences between the two sets, and add those
- # to changes.
- i0 = 0
- i1 = 0
- bl1set = set(bl1)
-
- while True:
- if i0 >= len(bl0) or i1 >= len(bl1):
- break
-
- b0 = bl0[i0]
- b1 = bl1[i1]
-
- if b0 == b1:
- if id(b0[5]) in mutated:
- changes.add(b0[:5])
-
- i0 += 1
- i1 += 1
-
- elif b0 not in bl1set:
- changes.add(b0[:5])
- i0 += 1
-
- else:
- changes.add(b1[:5])
- i1 += 1
-
- changes.update(i[:5] for i in bl0[i0:])
- changes.update(i[:5] for i in bl1[i1:])
-
- # No changes? Quit.
- if not changes:
- return None, [ ]
-
- # Compute the sizes of the updated rectangles.
- sized = [ ]
-
- for x0, y0, x1, y1, (sx0, sy0, sx1, sy1) in changes:
-
- # Round up by a pixel, to prevent visual artifacts when scaled down.
- x1 += 1
- y1 += 1
-
- if x0 < sx0:
- x0 = sx0
- if y0 < sy0:
- y0 = sy0
- if x1 > sx1:
- x1 = sx1
- if y1 > sy1:
- y1 = sy1
-
- w = x1 - x0
- h = y1 - y0
-
- if w <= 0 or h <= 0:
- continue
-
- area = w * h
-
- if area >= sa:
- return fullscreen, [ fullscreen ]
-
- sized.append((area, x0, y0, x1, y1))
-
- sized.sort()
-
- # The list of non-contiguous updates.
- noncont = [ ]
-
- # The total area of noncont.
- nca = 0
-
- # Pick the largest area, merge with all overlapping smaller areas, repeat
- # until no merge possible.
- while sized:
- area, x0, y0, x1, y1 = sized.pop()
-
-
- merged = False
-
- if nca + area >= sa:
- return (0, 0, sw, sh), [ (0, 0, sw, sh) ]
-
- i = 0
-
- while i < len(sized):
- _iarea, ix0, iy0, ix1, iy1 = sized[i]
-
- if (x0 <= ix0 <= x1 or x0 <= ix1 <= x1) and \
- (y0 <= iy0 <= y1 or y0 <= iy1 <= y1):
-
- merged = True
- x0 = min(x0, ix0)
- x1 = max(x1, ix1)
- y0 = min(y0, iy0)
- y1 = max(y1, iy1)
-
- area = (x1 - x0) * (y1 - y0)
-
- sized.pop(i)
-
- else:
- i += 1
-
- if merged:
- sized.append((area, x0, y0, x1, y1))
- else:
- noncont.append((x0, y0, x1, y1))
- nca += area
-
- if not noncont:
- return None, [ ]
-
- x0, y0, x1, y1 = noncont.pop()
- x0 = int(x0)
- y0 = int(y0)
- x1 = int(math.ceil(x1))
- y1 = int(math.ceil(y1))
-
- # A list of (x, y, w, h) tuples for each update.
- updates = [ (x0, y0, x1 - x0, y1 - y0) ]
-
- for ix0, iy0, ix1, iy1 in noncont:
-
- ix0 = int(ix0)
- iy0 = int(iy0)
- ix1 = int(math.ceil(ix1))
- iy1 = int(math.ceil(iy1))
-
- x0 = min(x0, ix0)
- y0 = min(y0, iy0)
- x1 = max(x1, ix1)
- y1 = max(y1, iy1)
-
- updates.append((ix0, iy0, ix1 - ix0, iy1 - iy0))
-
- return (x0, y0, x1 - x0, y1 - y0), updates
-
-clippers = [ Clipper() ]
-
-def surface(w, h, alpha):
- """
- Creates a surface that shares a pixel format with the screen. The created
- surface will
- """
-
- if alpha:
- rv = pygame.Surface((w + 4, h + 4), pygame.SRCALPHA)
- else:
- rv = pygame.Surface((w + 4, h + 4), 0)
-
- return rv.subsurface((2, 2, w, h))
-
-def copy_surface(surf):
- w, h = surf.get_size()
- rv = surface(w, h, True)
-
- renpy.display.accelerator.nogil_copy(surf, rv) # @UndefinedVariable
- return rv
-
-def draw_special(what, dest, x, y):
- """
- This handles the special drawing operations, such as dissolve and
- image dissolve. `x` and `y` are the offsets of the thing to be drawn
- relative to the destination rectangle, and are always negative.
- """
-
- dw, dh = dest.get_size()
-
- w = min(dw, what.width + x)
- h = min(dh, what.height + y)
-
- if w <= 0 or h <= 0:
- return
-
- if what.operation == DISSOLVE:
-
- bottom = what.children[0][0].render_to_texture(True)
- top = what.children[1][0].render_to_texture(True)
-
- if what.operation_alpha:
- target = surface(w, h, True)
- else:
- target = dest.subsurface((0, 0, w, h))
-
- renpy.display.module.blend(
- bottom.subsurface((-x, -y, w, h)),
- top.subsurface((-x, -y, w, h)),
- target,
- int(what.operation_complete * 255))
-
- if what.operation_alpha:
- dest.blit(target, (0, 0))
-
- elif what.operation == IMAGEDISSOLVE:
-
- image = what.children[0][0].render_to_texture(True)
- bottom = what.children[1][0].render_to_texture(True)
- top = what.children[2][0].render_to_texture(True)
-
- if what.operation_alpha:
- target = surface(w, h, True)
- else:
- target = dest.subsurface((0, 0, w, h))
-
- ramplen = what.operation_parameter
-
- ramp = "\x00" * 256
-
- for i in range(0, ramplen):
- ramp += chr(255 * i / ramplen)
-
- ramp += "\xff" * 256
-
- step = int( what.operation_complete * (256 + ramplen) )
- ramp = ramp[step:step+256]
-
- renpy.display.module.imageblend(
- bottom.subsurface((-x, -y, w, h)),
- top.subsurface((-x, -y, w, h)),
- target,
- image.subsurface((-x, -y, w, h)),
- ramp)
-
- if what.operation_alpha:
- dest.blit(target, (0, 0))
-
- elif what.operation == PIXELLATE:
-
- surf = what.children[0][0].render_to_texture(False)
-
- px = what.operation_parameter
-
- renpy.display.module.pixellate(
- surf.subsurface((-x, -y, w, h)),
- dest.subsurface((0, 0, w, h)),
- px, px, px, px)
-
- else:
- raise Exception("Unknown operation: %d" % what.operation)
-
-
-def draw(dest, clip, what, xo, yo, screen):
- """
- This is the simple draw routine, which only works when alpha is 1.0
- and the matrices are None. If those aren't the case, draw_complex
- is used instead.
-
- `dest` - Either a destination surface, or a clipper.
- `clip` - If None, we should draw. Otherwise we should clip, and this is
- the rectangle to clip to.
- `what` - The Render or Surface we're drawing to.
- `xo` - The X offset.
- `yo` - The Y offset.
- `screen` - True if this is a blit to the screen, False otherwise.
- """
-
- if not isinstance(what, renpy.display.render.Render):
-
- # Pixel-Aligned blit.
- if isinstance(xo, int) and isinstance(yo, int):
- if screen:
- what = rle_cache.get(what, what)
-
- if clip:
- w, h = what.get_size()
- dest.blits.append((xo, yo, xo + w, yo + h, clip, what, None))
- else:
- try:
- blit_lock.acquire()
- dest.blit(what, (xo, yo))
- finally:
- blit_lock.release()
-
- # Subpixel blit.
- else:
- if clip:
- w, h = what.get_size()
- dest.blits.append((xo, yo, xo + w, yo + h, clip, what, None))
- else:
- renpy.display.module.subpixel(what, dest, xo, yo)
-
- return
-
- # Deal with draw functions.
- if what.operation != BLIT:
-
- xo = int(xo)
- yo = int(yo)
-
- if clip:
- dx0, dy0, dx1, dy1 = clip
- dw = dx1 - dx0
- dh = dy1 - dy0
- else:
- dw, dh = dest.get_size()
-
- if xo >= 0:
- newx = 0
- subx = xo
- else:
- newx = xo
- subx = 0
-
- if yo >= 0:
- newy = 0
- suby = yo
- else:
- newy = yo
- suby = 0
-
- if subx >= dw or suby >= dh:
- return
-
- # newx and newy are the offset of this render relative to the
- # subsurface. They can only be negative or 0, as otherwise we
- # would make a smaller subsurface.
-
- subw = min(dw - subx, what.width + newx)
- subh = min(dh - suby, what.height + newy)
-
- if subw <= 0 or subh <= 0:
- return
-
- if clip:
- dest.forced.add((subx, suby, subx + subw, suby + subh, clip))
- else:
- newdest = dest.subsurface((subx, suby, subw, subh))
- # what.draw_func(newdest, newx, newy)
- draw_special(what, newdest, newx, newy)
-
-
- return
-
- # Deal with clipping, if necessary.
- if what.clipping:
-
- if clip:
- cx0, cy0, cx1, cy1 = clip
-
- cx0 = max(cx0, xo)
- cy0 = max(cy0, yo)
- cx1 = min(cx1, xo + what.width)
- cy1 = min(cy1, yo + what.height)
-
- if cx0 > cx1 or cy0 > cy1:
- return
-
- clip = (cx0, cy0, cx1, cy1)
-
- dest.forced.add(clip + (clip,))
- return
-
- else:
-
- # After this code, x and y are the coordinates of the subsurface
- # relative to the destination. xo and yo are the offset of the
- # upper-left corner relative to the subsurface.
-
- if xo >= 0:
- x = xo
- xo = 0
- else:
- x = 0
- # xo = xo
-
- if yo >= 0:
- y = yo
- yo = 0
- else:
- y = 0
- # yo = yo
-
- dw, dh = dest.get_size()
-
- width = min(dw - x, what.width + xo)
- height = min(dh - y, what.height + yo)
-
- if width < 0 or height < 0:
- return
-
- dest = dest.subsurface((x, y, width, height))
-
- # Deal with alpha and transforms by passing them off to draw_transformed.
- if what.alpha != 1 or (what.forward is not None and what.forward is not IDENTITY):
- for child, cxo, cyo, _focus, _main in what.visible_children:
- draw_transformed(dest, clip, child, xo + cxo, yo + cyo,
- what.alpha, what.forward, what.reverse)
- return
-
- for child, cxo, cyo, _focus, _main in what.visible_children:
- draw(dest, clip, child, xo + cxo, yo + cyo, screen)
-
-def draw_transformed(dest, clip, what, xo, yo, alpha, forward, reverse):
-
- # If our alpha has hit 0, don't do anything.
- if alpha <= 0.003: # (1 / 256)
- return
-
- if forward is None:
- forward = IDENTITY
- reverse = IDENTITY
-
- if not isinstance(what, renpy.display.render.Render):
-
- # Figure out where the other corner of the transformed surface
- # is on the screen.
- sw, sh = what.get_size()
- if clip:
-
- dx0, dy0, dx1, dy1 = clip
- dw = dx1 - dx0
- dh = dy1 - dy0
-
- else:
- dw, dh = dest.get_size()
-
- x0, y0 = 0.0, 0.0
- x1, y1 = reverse.transform(sw, 0.0)
- x2, y2 = reverse.transform(sw, sh)
- x3, y3 = reverse.transform(0.0, sh)
-
- minx = math.floor(min(x0, x1, x2, x3) + xo)
- maxx = math.ceil(max(x0, x1, x2, x3) + xo)
- miny = math.floor(min(y0, y1, y2, y3) + yo)
- maxy = math.ceil(max(y0, y1, y2, y3) + yo)
-
- if minx < 0:
- minx = 0
- if miny < 0:
- miny = 0
-
- if maxx > dw:
- maxx = dw
- if maxy > dh:
- maxy = dh
-
- if minx > dw or miny > dh or maxx < 0 or maxy < 0:
- return
-
- cx, cy = forward.transform(minx - xo, miny - yo)
-
- if clip:
-
- dest.blits.append(
- (minx, miny, maxx + dx0, maxy + dy0, clip, what,
- (cx, cy,
- forward.xdx, forward.ydx,
- forward.xdy, forward.ydy,
- alpha)))
-
- else:
-
- dest = dest.subsurface((minx, miny, maxx - minx, maxy - miny))
-
- renpy.display.module.transform(
- what, dest,
- cx, cy,
- forward.xdx, forward.ydx,
- forward.xdy, forward.ydy,
- alpha, True)
-
- return
-
- if what.clipping:
-
- if reverse.xdy or reverse.ydx:
- draw_transformed(dest, clip, what.pygame_surface(True), xo, yo, alpha, forward, reverse)
- return
-
- width = what.width * reverse.xdx
- height = what.height * reverse.ydy
-
- if clip:
- cx0, cy0, cx1, cy1 = clip
-
- cx0 = max(cx0, xo)
- cy0 = max(cy0, yo)
- cx1 = min(cx1, xo + width)
- cy1 = min(cy1, yo + height)
-
- if cx0 > cx1 or cy0 > cy1:
- return
-
- clip = (cx0, cy0, cx1, cy1)
-
- dest.forced.add(clip + (clip,))
- return
-
- else:
-
- # After this code, x and y are the coordinates of the subsurface
- # relative to the destination. xo and yo are the offset of the
- # upper-left corner relative to the subsurface.
-
- if xo >= 0:
- x = xo
- xo = 0
- else:
- x = 0
- # xo = xo
-
- if yo >= 0:
- y = yo
- yo = 0
- else:
- y = 0
- # yo = yo
-
- dw, dh = dest.get_size()
-
- width = min(dw - x, width + xo)
- height = min(dh - y, height + yo)
-
- if width < 0 or height < 0:
- return
-
- dest = dest.subsurface((x, y, width, height))
-
- if what.draw_func or what.operation != BLIT:
- child = what.pygame_surface(True)
- draw_transformed(dest, clip, child, xo, yo, alpha, forward, reverse)
- return
-
- for child, cxo, cyo, _focus, _main in what.visible_children:
-
- cxo, cyo = reverse.transform(cxo, cyo)
-
- if what.forward:
- child_forward = forward * what.forward
- child_reverse = what.reverse * reverse
- else:
- child_forward = forward
- child_reverse = reverse
-
- draw_transformed(dest, clip, child, xo + cxo, yo + cyo, alpha * what.alpha, child_forward, child_reverse)
-
-
-
-def do_draw_screen(screen_render, full_redraw, swdraw):
- """
- Draws the render produced by render_screen to the screen.
- """
-
- yoffset = xoffset = 0
-
- screen_render.is_opaque()
-
- clip = (xoffset, yoffset, xoffset + screen_render.width, yoffset + screen_render.height)
- clipper = clippers[0]
-
- draw(clipper, clip, screen_render, xoffset, yoffset, True)
-
- cliprect, updates = clipper.compute(full_redraw)
-
- if cliprect is None:
- return [ ]
-
- x, y, _w, _h = cliprect
-
- dest = swdraw.window.subsurface(cliprect)
- draw(dest, None, screen_render, -x, -y, True)
-
- return updates
-
-
-class SWDraw(object):
- """
- This uses the software renderer to draw to the screen.
- """
-
- def __init__(self):
- self.display_info = None
-
- self.reset()
-
- def reset(self):
-
- # Should we draw the screen?
- self.suppressed_blit = False
-
- # The earliest time at which the next frame can be redrawn.
- self.next_frame = 0
-
- # Mouse re-drawing.
- self.mouse_location = None
- self.mouse_backing = None
- self.mouse_backing_pos = None
- self.mouse_info = None
-
-
- # Is the mouse currently visible?
- self.mouse_old_visible = None
-
- # This is used to cache the surface->texture operation.
- self.texture_cache = weakref.WeakKeyDictionary()
-
- # This is used to display video to the screen.
- self.fullscreen_surface = None
-
- # Info.
- self.info = { "renderer" : "sw", "resizable" : False }
-
- pygame.display.init()
- renpy.display.interface.post_init()
-
- if self.display_info is None:
- self.display_info = pygame.display.Info()
-
- # The scale factor we use for this display.
- self.scale_factor = 1.0
-
- # Should we scale fast, or scale good-looking?
- self.scale_fast = "RENPY_SCALE_FAST" in os.environ
-
- # The screen returned to us from pygame.
- self.screen = None
-
- # The window that we render into, if not the screen. This has a
- # 1px border around it iff we're scaling.
- self.window = None
-
- def set_mode(self, virtual_size, physical_size, fullscreen):
-
- # Reset before resize.
- renpy.display.interface.kill_textures_and_surfaces()
- self.reset()
-
- width, height = virtual_size
-
- # Set up scaling, if necessary.
- screen_width = self.display_info.current_w
- screen_height = self.display_info.current_h
-
- if not fullscreen:
- screen_height -= 102
- screen_width -= 102
-
- scale_factor = min(1.0 * screen_width / width, 1.0 * screen_height / height, 1.0)
- if "RENPY_SCALE_FACTOR" in os.environ:
- scale_factor = float(os.environ["RENPY_SCALE_FACTOR"])
- self.scale_factor = scale_factor
-
- # Figure out the fullscreen info.
- if fullscreen:
- fsflag = pygame.FULLSCREEN
- else:
- fsflag = 0
-
- # If a window exists of the right size and flags, use it. Otherwise,
- # make our own window.
- old_screen = pygame.display.get_surface()
-
- scaled_width = int(width * scale_factor)
- scaled_height = int(height * scale_factor)
-
- if ((old_screen is not None) and
- (old_screen.get_size() == (scaled_width, scaled_height)) and
- (old_screen.get_flags() & pygame.FULLSCREEN == fsflag)):
-
- self.screen = old_screen
-
- else:
- self.screen = pygame.display.set_mode((scaled_width, scaled_height), fsflag, 32)
-
- if scale_factor != 1.0:
- self.window = surface(width, height, True)
- else:
- self.window = self.screen
-
- renpy.display.pgrender.set_rgba_masks()
-
- # Should we redraw the screen from scratch?
- self.full_redraw = True
-
- # The surface used to display fullscreen video.
- self.fullscreen_surface = self.screen
-
- # Reset this on a mode change.
- self.mouse_location = None
- self.mouse_backing = None
- self.mouse_backing_pos = None
- self.mouse_info = None
-
- return True
-
- # private
- def show_mouse(self, pos, info):
- """
- Actually shows the mouse.
- """
-
- self.mouse_location = pos
- self.mouse_info = info
-
- mxo, myo, tex = info
-
- mx, my = pos
- mw, mh = tex.get_size()
-
- bx = mx - mxo
- by = my - myo
-
- self.mouse_backing_pos = (bx, by)
- self.mouse_backing = surface(mw, mh, False)
- self.mouse_backing.blit(self.window, (0, 0), (bx, by, mw, mh))
-
- self.screen.blit(tex, (bx, by))
-
- return bx, by, mw, mh
-
- # private
- def hide_mouse(self):
- """
- Actually hides the mouse.
- """
-
- size = self.mouse_backing.get_size()
- self.screen.blit(self.mouse_backing, self.mouse_backing_pos)
-
- rv = self.mouse_backing_pos + size
-
- self.mouse_backing = None
- self.mouse_backing_pos = None
- self.mouse_location = None
-
- return rv
-
- # private
- def draw_mouse(self, show_mouse):
- """
- This draws the mouse to the screen, if necessary. It uses the
- buffer to minimize the amount of the screen that needs to be
- drawn, and only redraws if the mouse has actually been moved.
- """
-
- hardware, x, y, tex = renpy.game.interface.get_mouse_info()
-
- if self.mouse_old_visible != hardware:
- pygame.mouse.set_visible(hardware)
- self.mouse_old_visible = hardware
-
- # The rest of this is for the software mouse.
-
- if self.suppressed_blit:
- return [ ]
-
- if not show_mouse:
- tex = None
-
- info = (x, y, tex)
- pos = pygame.mouse.get_pos()
-
- if (pos == self.mouse_location and tex and info == self.mouse_info):
- return [ ]
-
- updates = [ ]
-
- if self.mouse_location:
- updates.append(self.hide_mouse())
-
- if tex and pos and renpy.game.interface.focused:
- updates.append(self.show_mouse(pos, info))
-
- return updates
-
- def update_mouse(self):
- """
- Draws the mouse, and then updates the screen.
- """
-
- updates = self.draw_mouse(True)
-
- if updates:
- pygame.display.update(updates)
-
- def mouse_event(self, ev):
- x, y = getattr(ev, 'pos', pygame.mouse.get_pos())
-
- x /= self.scale_factor
- y /= self.scale_factor
-
- return x, y
-
- def get_mouse_pos(self):
- x, y = pygame.mouse.get_pos()
-
- x /= self.scale_factor
- y /= self.scale_factor
-
- return x, y
-
-
- def screenshot(self, surftree, fullscreen_video):
- """
- Returns a pygame surface containing a screenshot.
- """
-
- return self.window
-
- def should_redraw(self, needs_redraw, first_pass):
- """
- Uses the framerate to determine if we can and should redraw.
- """
-
- if not needs_redraw:
- return False
-
- framerate = renpy.config.framerate
-
- if framerate is None:
- return True
-
- next_frame = self.next_frame
- now = pygame.time.get_ticks()
-
- frametime = 1000.0 / framerate
-
- # Handle timer rollover.
- if next_frame > now + frametime:
- next_frame = now
-
- # It's not yet time for the next frame.
- if now < next_frame and not first_pass:
- return False
-
- # Otherwise, it is. Schedule the next frame.
- # if next_frame + frametime < now:
- next_frame = now + frametime
- # else:
- # next_frame += frametime
-
- self.next_frame = next_frame
-
- return True
-
-
- def draw_screen(self, surftree, fullscreen_video):
- """
- Draws the screen.
- """
-
- if not fullscreen_video:
-
- updates = [ ]
-
- updates.extend(self.draw_mouse(False))
-
- damage = do_draw_screen(surftree, self.full_redraw, self)
-
- if damage:
- updates.extend(damage)
-
- self.full_redraw = False
-
- if self.window is self.screen:
-
- updates.extend(self.draw_mouse(True))
- pygame.display.update(updates)
-
- else:
-
- if self.scale_fast:
- pygame.transform.scale(self.window, self.screen.get_size(), self.screen)
- else:
- renpy.display.scale.smoothscale(self.window, self.screen.get_size(), self.screen)
-
- self.draw_mouse(True)
- pygame.display.flip()
-
- else:
- pygame.display.flip()
- self.full_redraw = True
-
- self.suppressed_blit = fullscreen_video
-
-
- def render_to_texture(self, render, alpha):
-
- rv = surface(render.width, render.height, alpha)
- draw(rv, None, render, 0, 0, False)
-
- return rv
-
- def is_pixel_opaque(self, what, x, y):
-
- if x < 0 or y < 0 or x >= what.width or y >= what.height:
- return 0
-
- for (child, xo, yo, _focus, _main) in what.visible_children:
- cx = x - xo
- cy = y - yo
-
- if what.forward:
- cx, cy = what.forward.transform(cx, cy)
-
-
- if isinstance(child, renpy.display.render.Render):
- if self.is_pixel_opaque(child, x, y):
- return True
-
- else:
- cx = int(cx)
- cy = int(cy)
-
- cw, ch = child.get_size()
- if cx >= cw or cy >= ch:
- return False
-
-
-
- if not child.get_masks()[3] or child.get_at((cx, cy))[3]:
- return True
-
- return False
-
-
- def mutated_surface(self, surf):
- """
- Called to indicate that the given surface has changed.
- """
-
- for i in clippers:
- i.mutated.add(id(surf))
-
- if surf in rle_cache:
- del rle_cache[surf]
-
-
- def load_texture(self, surf, transient=False):
- """
- Creates a texture from the surface. In the software implementation,
- the only difference between a texture and a surface is that a texture
- is in the RLE cache.
- """
-
- surf = copy_surface(surf)
- self.mutated_surface(surf)
-
- if transient:
- return surf
-
- if renpy.game.less_memory:
- return surf
-
- if surf not in rle_cache:
- rle_surf = copy_surface(surf)
- rle_surf.set_alpha(255, pygame.RLEACCEL)
- self.mutated_surface(rle_surf)
-
- rle_cache[surf] = rle_surf
-
- return surf
-
- def solid_texture(self, w, h, color):
- """
- Creates a texture filled to the edges with color.
- """
-
- surf = surface(w + 4, h + 4, True)
- surf.fill(color)
- self.mutated_surface(surf)
-
- surf = surf.subsurface((2, 2, w, h))
-
- self.mutated_surface(surf)
- return surf
-
-
- def free_memory(self):
- """
- Frees up memory.
- """
-
- rle_cache.clear()
-
- def deinit(self):
- """
- Called when we're restarted.
- """
-
- renpy.display.render.free_memory()
-
- return
-
- def quit(self): #@ReservedAssignment
- """
- Shuts down the drawing system.
- """
-
- pygame.display.quit()
-
- return
-
- def event_peek_sleep(self):
- """
- Wait a little bit so the CPU doesn't speed up.
- """
-
- time.sleep(.0001)
-
- def get_physical_size(self):
- """
- Return the physical width and height of the screen.
- """
- return renpy.config.screen_width, renpy.config.screen_height
diff --git a/unrpyc/renpy/display/transition.py b/unrpyc/renpy/display/transition.py
deleted file mode 100644
index 7f93312..0000000
--- a/unrpyc/renpy/display/transition.py
+++ /dev/null
@@ -1,922 +0,0 @@
-# Copyright 2004-2013 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.
-
-# NOTE:
-# Transitions need to be able to work even when old_widget and new_widget
-# are None, at least to the point of making it through __init__. This is
-# so that prediction of images works.
-
-import renpy.display
-from renpy.display.render import render
-
-
-class Transition(renpy.display.core.Displayable):
- """
- This is the base class of most transitions. It takes care of event
- dispatching.
- """
-
- def __init__(self, delay, **properties):
- super(Transition, self).__init__(**properties)
- self.delay = delay
- self.events = True
-
- def event(self, ev, x, y, st):
-
- if self.events or ev.type == renpy.display.core.TIMEEVENT:
- return self.new_widget.event(ev, x, y, st) # E1101
- else:
- return None
-
- def visit(self):
- return [ self.new_widget, self.old_widget ] # E1101
-
-
-def null_render(d, width, height, st, at):
-
- d.events = True
- surf = renpy.display.render.render(d.new_widget,
- width,
- height,
- st, at)
-
- rv = renpy.display.render.Render(surf.width, surf.height)
- rv.blit(surf, (0, 0))
-
- return rv
-
-class NoTransition(Transition):
- """
- :doc: transition function
- :args: (delay)
-
- Returns a transition that only displays the new screen for `delay` seconds.
- It can be useful as part of a MultipleTransition.
- """
-
- def __init__(self, delay, old_widget=None, new_widget=None, **properties):
- super(NoTransition, self).__init__(delay, **properties)
-
- self.old_widget = old_widget
- self.new_widget = new_widget
- self.events = True
-
- def render(self, width, height, st, at):
- return null_render(self, width, height, st, at)
-
-
-class MultipleTransition(Transition):
- """
- :doc: transition function
- :args: (args)
-
- Returns a transition that allows multiple transitions to be displayed, one
- after the other.
-
- `args`
- A list containing an odd number of items. The first, third, and
- other odd-numbered items must be scenes, and the even items
- must be transitions. A scene can be one of:
-
- * A displayable.
- * False, to use the old scene.
- * True, to use the new scene.
-
- Almost always, the first argument will be False and the last True.
-
- The transitions in `args` are applied in order. For each transition,
- the old scene is the screen preceding it, and the new scene is the
- scene following it. For example::
-
- define logodissolve = MultipleTransition(
- False, Dissolve(0.5)
- "logo.jpg", NoTransition(1.0),
- "logo.jpg", dissolve,
- True)
-
- This example will dissolve to logo.jpg, wait 1 second, and then
- dissolve to the new scene.
- """
-
- def __init__(self, args, old_widget=None, new_widget=None, **properties):
-
- if len(args) % 2 != 1 or len(args) < 3:
- raise Exception("MultipleTransition requires an odd number of arguments, and at least 3 arguments.")
-
- self.transitions = [ ]
-
- # The screens that we use for the transition.
- self.screens = [ renpy.easy.displayable(i) for i in args[0::2] ]
-
- def oldnew(w):
- if w is False:
- return old_widget
- if w is True:
- return new_widget
-
- return w
-
- for old, trans, new in zip(self.screens[0:], args[1::2], self.screens[1:]):
- old = oldnew(old)
- new = oldnew(new)
-
- self.transitions.append(trans(old_widget=old, new_widget=new))
-
- super(MultipleTransition, self).__init__(sum([i.delay for i in self.transitions]), **properties)
-
- self.new_widget = self.transitions[-1]
- self.events = False
-
- def visit(self):
- return [ i for i in self.screens if isinstance(i, renpy.display.core.Displayable)] + self.transitions
-
- def event(self, ev, x, y, st):
-
- if self.events or ev.type == renpy.display.core.TIMEEVENT:
- return self.transitions[-1].event(ev, x, y, st)
- else:
- return None
-
- def render(self, width, height, st, at):
-
- if renpy.game.less_updates:
- return null_render(self, width, height, st, at)
-
- for trans in self.transitions[:-1]:
-
- if trans.delay > st:
- break
-
- st -= trans.delay
-
- else:
-
- trans = self.transitions[-1]
- self.events = True
-
- if trans is not self.transitions[-1]:
- renpy.display.render.render(self.transitions[-1], width, height, 0, 0)
-
- surf = renpy.display.render.render(trans, width, height, st, at)
- width, height = surf.get_size()
- rv = renpy.display.render.Render(width, height)
- rv.blit(surf, (0, 0))
-
- if st < trans.delay:
- renpy.display.render.redraw(self, trans.delay - st)
-
- return rv
-
-
-def Fade(out_time,
- hold_time,
- in_time,
- old_widget=None,
- new_widget=None,
- color=None,
- widget=None,
- alpha=False,
- ):
-
- """
- :doc: transition function
- :args: (out_time, hold_time, in_time, color="#000")
- :name: Fade
-
- Returns a transition that takes `out_time` seconds to fade to
- a screen filled with `color`, holds at that screen for `hold_time`
- seconds, and then takes `in_time` to fade to then new screen.
-
- ::
-
- # Fade to black and back.
- define fade = Fade(0.5, 0.0, 0.5)
-
- # Hold at black for a bit.
- define fadehold = Fade(0.5, 1.0, 0.5)
-
- # Camera flash - quickly fades to white, then back to the scene.
- define flash = Fade(0.1, 0.0, 0.5, color="#fff")
- """
-
- dissolve = renpy.curry.curry(Dissolve)
- notrans = renpy.curry.curry(NoTransition)
-
- widget = renpy.easy.displayable_or_none(widget)
-
- if color:
- widget = renpy.display.image.Solid(color)
-
- if not widget:
- widget = renpy.display.image.Solid((0, 0, 0, 255))
-
- args = [ False, dissolve(out_time, alpha=alpha), widget ]
-
- if hold_time:
- args.extend([ notrans(hold_time), widget, ])
-
- args.extend([dissolve(in_time, alpha=alpha), True ])
-
- return MultipleTransition(args, old_widget=old_widget, new_widget=new_widget)
-
-
-class Pixellate(Transition):
- """
- :doc: transition function
- :args: (time, steps)
- :name: Pixellate
-
- Returns a transition that pixellates out the old screen, and then
- pixellates in the new screen.
-
- `time`
- The total time the transition will take, in seconds.
-
- `steps`
- The number of steps that will occur, in each direction. Each step
- creates pixels about twice the size of those in the previous step,
- so a 5-step pixellation will create 32x32 pixels.
- """
-
- def __init__(self, time, steps, old_widget=None, new_widget=None, **properties):
-
- time = float(time)
-
- super(Pixellate, self).__init__(time, **properties)
-
- self.time = time
- self.steps = steps
-
- self.old_widget = old_widget
- self.new_widget = new_widget
-
- self.events = False
-
- self.quantum = time / (2 * steps)
-
- def render(self, width, height, st, at):
-
- if renpy.game.less_updates:
- return null_render(self, width, height, st, at)
-
- if st >= self.time:
- self.events = True
- return render(self.new_widget, width, height, st, at)
-
- step = st // self.quantum + 1
- visible = self.old_widget
-
- if step > self.steps:
- step = (self.steps * 2) - step + 1
- visible = self.new_widget
- self.events = True
-
-
- rdr = render(visible, width, height, st, at)
- rv = renpy.display.render.Render(rdr.width, rdr.height)
-
- rv.blit(rdr, (0, 0))
-
- rv.operation = renpy.display.render.PIXELLATE
- rv.operation_parameter = 2 ** step
-
- renpy.display.render.redraw(self, 0)
-
- return rv
-
-
-class Dissolve(Transition):
- """
- :doc: transition function
- :args: (time, alpha=False, time_warp=None)
- :name: Dissolve
-
- Returns a transition that dissolves from the old scene to the new scene.
-
- `time`
- The time the dissolve will take.
-
- `alpha`
- If true, the dissolve will alpha-composite the the result of the
- transition with the screen. If false, the result of the transition
- will replace the screen, which is more efficient.
-
- `time_warp`
- A function that adjusts the timeline. If not None, this should be a
- function that takes a fractional time between 0.0 and 1.0, and returns
- a number in the same range.
- """
-
- __version__ = 1
-
- def after_upgrade(self, version):
- if version < 1:
- self.alpha = False
-
- time_warp = None
-
- def __init__(self, time, old_widget=None, new_widget=None, alpha=False, time_warp=None, **properties):
- super(Dissolve, self).__init__(time, **properties)
-
- self.time = time
- self.old_widget = old_widget
- self.new_widget = new_widget
- self.events = False
- self.alpha = alpha
- self.time_warp = time_warp
-
-
- def render(self, width, height, st, at):
-
- if renpy.game.less_updates:
- return null_render(self, width, height, st, at)
-
- if st >= self.time:
- self.events = True
- return render(self.new_widget, width, height, st, at)
-
- complete = min(1.0, st / self.time)
-
- if self.time_warp is not None:
- complete = self.time_warp(complete)
-
- bottom = render(self.old_widget, width, height, st, at)
- top = render(self.new_widget, width, height, st, at)
-
- width = min(top.width, bottom.width)
- height = min(top.height, bottom.height)
-
- rv = renpy.display.render.Render(width, height, opaque=not self.alpha)
-
- rv.operation = renpy.display.render.DISSOLVE
- rv.operation_alpha = self.alpha
- rv.operation_complete = complete
-
- rv.blit(bottom, (0, 0), focus=False, main=False)
- rv.blit(top, (0, 0), focus=True, main=True)
-
- renpy.display.render.redraw(self, 0)
-
- return rv
-
-
-class ImageDissolve(Transition):
- """
- :doc: transition function
- :args: (image, time, ramplen=8, reverse=False, alpha=True, time_warp=None)
- :name: ImageDissolve
-
- Returns a transition that dissolves the old scene into the new scene, using
- an image to control the dissolve process. This means that white pixels will
- dissolve in first, and black pixels will dissolve in last.
-
- `image`
- A control image to use. This must be either an image file or
- image manipulator. The control image should be the size of
- the scenes being dissolved.
-
- `time`
- The time the dissolve will take.
-
- `ramplen`
- The length of the ramp to use. This must be an integer power
- of 2. When this is the default value of 8, when a white pixel
- is fully dissolved, a pixel 8 shades of gray darker will have
- completed one step of dissolving in.
-
- `reverse`
- If true, black pixels will dissolve in before white pixels.
-
- `alpha`
- If true, the dissolve will alpha-composite the the result of the
- transition with the screen. If false, the result of the transition
- will replace the screen, which is more efficient.
-
- `time_warp`
- A function that adjusts the timeline. If not None, this should be a
- function that takes a fractional time between 0.0 and 1.0, and returns
- a number in the same range.
-
- ::
-
- define circirisout = ImageDissolve("circiris.png", 1.0)
- define circirisin = ImageDissolve("circiris.png", 1.0, reverse=True)
- define circiristbigramp = ImageDissolve("circiris.png", 1.0, ramplen=256)
- """
-
- __version__ = 1
-
- def after_upgrade(self, version):
- if version < 1:
- self.alpha = False
-
- time_warp = None
-
- def __init__(
- self,
- image,
- time,
- ramplen=8,
- ramptype='linear',
- ramp=None,
- reverse=False,
- alpha=False,
- old_widget=None,
- new_widget=None,
- time_warp=None,
- **properties):
-
- # ramptype and ramp are now unused, but are kept for compatbility with
- # older code.
-
- super(ImageDissolve, self).__init__(time, **properties)
-
- self.old_widget = old_widget
- self.new_widget = new_widget
- self.events = False
- self.alpha = alpha
- self.time_warp = time_warp
-
- if not reverse:
-
- # Copies red -> alpha
- matrix = renpy.display.im.matrix(
- 0, 0, 0, 0, 1,
- 0, 0, 0, 0, 1,
- 0, 0, 0, 0, 1,
- 1, 0, 0, 0, 0)
-
- else:
-
- # Copies 1-red -> alpha
- matrix = renpy.display.im.matrix(
- 0, 0, 0, 0, 1,
- 0, 0, 0, 0, 1,
- 0, 0, 0, 0, 1,
- - 1, 0, 0, 0, 1)
-
- self.image = renpy.display.im.MatrixColor(image, matrix)
-
- if ramp is not None:
- ramplen = len(ramp)
-
- # The length of the ramp.
- self.ramplen = max(ramplen, 1)
-
-
- def visit(self):
- return super(ImageDissolve, self).visit() + [ self.image ]
-
-
- def render(self, width, height, st, at):
-
- if renpy.game.less_updates or renpy.display.less_imagedissolve:
- return null_render(self, width, height, st, at)
-
- if st >= self.delay:
- self.events = True
- return render(self.new_widget, width, height, st, at)
-
- image = render(self.image, width, height, st, at)
- bottom = render(self.old_widget, width, height, st, at)
- top = render(self.new_widget, width, height, st, at)
-
- width = min(bottom.width, top.width, image.width)
- height = min(bottom.height, top.height, image.height)
-
- rv = renpy.display.render.Render(width, height, opaque=not self.alpha)
-
- complete = st / self.delay
-
- if self.time_warp is not None:
- complete = self.time_warp(complete)
-
- rv.operation = renpy.display.render.IMAGEDISSOLVE
- rv.operation_alpha = self.alpha
- rv.operation_complete = complete
- rv.operation_parameter = self.ramplen
-
- rv.blit(image, (0, 0), focus=False, main=False)
- rv.blit(bottom, (0, 0), focus=False, main=False)
- rv.blit(top, (0, 0), focus=True, main=True)
-
- renpy.display.render.redraw(self, 0)
-
- return rv
-
-
-class AlphaDissolve(Transition):
- """
- :doc: transition function
- :args: (control, delay=0.0, alpha=False, reverse=False)
-
- Returns a transition that uses a control displayable (almost always some
- sort of animated transform) to transition from one screen to another. The
- transform is evaluated. The new screen is used where the transform is
- opaque, and the old image is used when it is transparent.
-
- `control`
- The control transform.
-
- `delay`
- The time the transition takes, before ending.
-
- `alpha`
- If true, the image is composited with what's behind it. If false,
- the default, the image is opaque and overwrites what's behind it.
-
- `reverse`
- If true, the alpha channel is reversed. Opaque areas are taken
- from the old image, while transparent areas are taken from the
- new image.
- """
-
- def __init__(
- self,
- control,
- delay=0.0,
- old_widget=None,
- new_widget=None,
- alpha=False,
- reverse=False,
- **properties):
-
- super(AlphaDissolve, self).__init__(delay, **properties)
-
- self.control = renpy.display.layout.Fixed()
- self.control.add(control)
-
- self.old_widget = renpy.easy.displayable(old_widget)
- self.new_widget = renpy.easy.displayable(new_widget)
- self.events = False
-
- self.alpha = alpha
- self.reverse = reverse
-
- def visit(self):
- return super(AlphaDissolve, self).visit() + [ self.control ]
-
- def render(self, width, height, st, at):
-
- if renpy.game.less_updates or renpy.display.less_imagedissolve:
- return null_render(self, width, height, st, at)
-
- if st >= self.delay:
- self.events = True
-
- bottom = render(self.old_widget, width, height, st, at)
- top = render(self.new_widget, width, height, st, at)
-
- width = min(bottom.width, top.width)
- height = min(bottom.height, top.height)
-
- control = render(self.control, width, height, st, at)
-
- rv = renpy.display.render.Render(width, height, opaque=not self.alpha)
-
- rv.operation = renpy.display.render.IMAGEDISSOLVE
- rv.operation_alpha = self.alpha
- rv.operation_complete = 256.0 / (256.0 + 256.0)
- rv.operation_parameter = 256
-
- rv.blit(control, (0, 0), focus=False, main=False)
-
- if not self.reverse:
- rv.blit(bottom, (0, 0), focus=False, main=False)
- rv.blit(top, (0, 0), focus=True, main=True)
- else:
- rv.blit(top, (0, 0), focus=True, main=True)
- rv.blit(bottom, (0, 0), focus=False, main=False)
-
- return rv
-
-
-class CropMove(Transition):
- """
- :doc: transition function
- :args: (time, mode="slideright", startcrop=(0.0, 0.0, 0.0, 1.0), startpos=(0.0, 0.0), endcrop=(0.0, 0.0, 1.0, 1.0), endpos=(0.0, 0.0), topnew=True)
- :name: CropMove
-
- Returns a transition that works by cropping a scene and positioning it on the
- screen. This can be used to implement a variety of effects, all of which
- involved changing rectangular slices of scenes.
-
- `time`
- The time the transition takes.
-
- `mode`
- The name of the mode of the transition. There are three groups
- of modes: wipes, slides, and other. This can also be "custom",
- to allow a custom mode to be defined.
-
- In a wipe, the image stays fixed, and more of it is revealed as
- the transition progresses. For example, in "wiperight", a wipe from left to right, first the left edge of the image is
- revealed at the left edge of the screen, then the center of the image,
- and finally the right side of the image at the right of the screen.
- Other supported wipes are "wipeleft", "wipedown", and "wipeup".
-
- In a slide, the image moves. So in a "slideright", the right edge of the
- image starts at the left edge of the screen, and moves to the right
- as the transition progresses. Other slides are "slideleft", "slidedown",
- and "slideup".
-
- There are also slideaways, in which the old image moves on top of
- the new image. Slideaways include "slideawayright", "slideawayleft",
- "slideawayup", and "slideawaydown".
-
- We also support a rectangular iris in with "irisin" and a
- rectangular iris out with "irisout".
-
- The following parameters are only respected if the mode is "custom". Positions
- are relative to the size of the screen, while the crops are relative to the
- size of the image. So a crop of (0.25, 0.0, 0.5, 1.0) takes the middle
- half of an image.
-
- `startcrop`
- The starting rectangle that is cropped out of the
- top image. A 4-element tuple containing x, y, width, and height.
-
- `startpos`
- The starting place that the top image is drawn
- to the screen at, a 2-element tuple containing x and y.
-
- `endcrop`
- The ending rectangle that is cropped out of the
- top image. A 4-element tuple containing x, y, width, and height.
-
- `endpos`
- The ending place that the top image is drawn
- to the screen at, a 2-element tuple containing x and y.
-
- `topnew`
- If true, the scene that is cropped and moved (and is on top of
- the other scene) is the new scene. If false, it is the old scene.
-
- ::
-
- define wiperight = CropMove(1.0, "wiperight")
- define wipeleft = CropMove(1.0, "wipeleft")
- define wipeup = CropMove(1.0, "wipeup")
- define wipedown = CropMove(1.0, "wipedown")
-
- define slideright = CropMove(1.0, "slideright")
- define slideleft = CropMove(1.0, "slideleft")
- define slideup = CropMove(1.0, "slideup")
- define slidedown = CropMove(1.0, "slidedown")
-
- define slideawayright = CropMove(1.0, "slideawayright")
- define slideawayleft = CropMove(1.0, "slideawayleft")
- define slideawayup = CropMove(1.0, "slideawayup")
- define slideawaydown = CropMove(1.0, "slideawaydown")
-
- define irisout = CropMove(1.0, "irisout")
- define irisin = CropMove(1.0, "irisin")
- """
-
- def __init__(self, time,
- mode="slideright",
- startcrop=(0.0, 0.0, 0.0, 1.0),
- startpos=(0.0, 0.0),
- endcrop=(0.0, 0.0, 1.0, 1.0),
- endpos=(0.0, 0.0),
- topnew=True,
- old_widget=None,
- new_widget=None,
- **properties):
-
- super(CropMove, self).__init__(time, **properties)
- self.time = time
-
- if mode == "wiperight":
- startpos = (0.0, 0.0)
- startcrop = (0.0, 0.0, 0.0, 1.0)
- endpos = (0.0, 0.0)
- endcrop = (0.0, 0.0, 1.0, 1.0)
- topnew = True
-
- elif mode == "wipeleft":
- startpos = (1.0, 0.0)
- startcrop = (1.0, 0.0, 0.0, 1.0)
- endpos = (0.0, 0.0)
- endcrop = (0.0, 0.0, 1.0, 1.0)
- topnew = True
-
- elif mode == "wipedown":
- startpos = (0.0, 0.0)
- startcrop = (0.0, 0.0, 1.0, 0.0)
- endpos = (0.0, 0.0)
- endcrop = (0.0, 0.0, 1.0, 1.0)
- topnew = True
-
- elif mode == "wipeup":
- startpos = (0.0, 1.0)
- startcrop = (0.0, 1.0, 1.0, 0.0)
- endpos = (0.0, 0.0)
- endcrop = (0.0, 0.0, 1.0, 1.0)
- topnew = True
-
- elif mode == "slideright":
- startpos = (0.0, 0.0)
- startcrop = (1.0, 0.0, 0.0, 1.0)
- endpos = (0.0, 0.0)
- endcrop = (0.0, 0.0, 1.0, 1.0)
- topnew = True
-
- elif mode == "slideleft":
- startpos = (1.0, 0.0)
- startcrop = (0.0, 0.0, 0.0, 1.0)
- endpos = (0.0, 0.0)
- endcrop = (0.0, 0.0, 1.0, 1.0)
- topnew = True
-
- elif mode == "slideup":
- startpos = (0.0, 1.0)
- startcrop = (0.0, 0.0, 1.0, 0.0)
- endpos = (0.0, 0.0)
- endcrop = (0.0, 0.0, 1.0, 1.0)
- topnew = True
-
- elif mode == "slidedown":
- startpos = (0.0, 0.0)
- startcrop = (0.0, 1.0, 1.0, 0.0)
- endpos = (0.0, 0.0)
- endcrop = (0.0, 0.0, 1.0, 1.0)
- topnew = True
-
- elif mode == "slideawayleft":
- endpos = (0.0, 0.0)
- endcrop = (1.0, 0.0, 0.0, 1.0)
- startpos = (0.0, 0.0)
- startcrop = (0.0, 0.0, 1.0, 1.0)
- topnew = False
-
- elif mode == "slideawayright":
- endpos = (1.0, 0.0)
- endcrop = (0.0, 0.0, 0.0, 1.0)
- startpos = (0.0, 0.0)
- startcrop = (0.0, 0.0, 1.0, 1.0)
- topnew = False
-
- elif mode == "slideawaydown":
- endpos = (0.0, 1.0)
- endcrop = (0.0, 0.0, 1.0, 0.0)
- startpos = (0.0, 0.0)
- startcrop = (0.0, 0.0, 1.0, 1.0)
- topnew = False
-
- elif mode == "slideawayup":
- endpos = (0.0, 0.0)
- endcrop = (0.0, 1.0, 1.0, 0.0)
- startpos = (0.0, 0.0)
- startcrop = (0.0, 0.0, 1.0, 1.0)
- topnew = False
-
- elif mode == "irisout":
- startpos = (0.5, 0.5)
- startcrop = (0.5, 0.5, 0.0, 0.0)
- endpos = (0.0, 0.0)
- endcrop = (0.0, 0.0, 1.0, 1.0)
- topnew = True
-
- elif mode == "irisin":
- startpos = (0.0, 0.0)
- startcrop = (0.0, 0.0, 1.0, 1.0)
- endpos = (0.5, 0.5)
- endcrop = (0.5, 0.5, 0.0, 0.0)
- topnew = False
-
-
- elif mode == "custom":
- pass
- else:
- raise Exception("Invalid mode %s passed into CropMove." % mode)
-
- self.delay = time
- self.time = time
-
- self.startpos = startpos
- self.endpos = endpos
-
- self.startcrop = startcrop
- self.endcrop = endcrop
-
- self.topnew = topnew
-
- self.old_widget = old_widget
- self.new_widget = new_widget
-
- self.events = False
-
- if topnew:
- self.bottom = old_widget
- self.top = new_widget
- else:
- self.bottom = new_widget
- self.top = old_widget
-
- def render(self, width, height, st, at):
-
- if renpy.game.less_updates:
- return null_render(self, width, height, st, at)
-
- time = 1.0 * st / self.time
-
- # Done rendering.
- if time >= 1.0:
- self.events = True
- return render(self.new_widget, width, height, st, at)
-
- # How we scale each element of a tuple.
- scales = (width, height, width, height)
-
- def interpolate_tuple(t0, t1):
- return tuple([ int(s * (a * (1.0 - time) + b * time))
- for a, b, s in zip(t0, t1, scales) ])
-
- crop = interpolate_tuple(self.startcrop, self.endcrop)
- pos = interpolate_tuple(self.startpos, self.endpos)
-
-
- top = render(self.top, width, height, st, at)
- bottom = render(self.bottom, width, height, st, at)
-
- width = min(bottom.width, width)
- height = min(bottom.height, height)
- rv = renpy.display.render.Render(width, height)
-
- rv.blit(bottom, (0, 0), focus=not self.topnew)
-
- ss = top.subsurface(crop, focus=self.topnew)
- rv.blit(ss, pos, focus=self.topnew)
-
- renpy.display.render.redraw(self, 0)
- return rv
-
-
-def ComposeTransition(trans, before=None, after=None, new_widget=None, old_widget=None):
- """
- :doc: transition function
- :args: (trans, before, after)
-
- Returns a transition that composes up to three transitions. If not None,
- the `before` and `after` transitions are applied to the old and new
- scenes, respectively. These updated old and new scenes are then supplied
- to the `trans` transition.
-
- ::
-
- # Move the images in and out while dissolving. (This is a fairly expensive transition.)
- define moveinoutdissolve = ComposeTransition(dissolve, before=moveoutleft, after=moveinright)
- """
-
- if before is not None:
- old = before(new_widget=new_widget, old_widget=old_widget)
- else:
- old = old_widget
-
- if after is not None:
- new = after(new_widget=new_widget, old_widget=old_widget)
- else:
- new = new_widget
-
- return trans(new_widget=new, old_widget=old)
-
-
-def SubTransition(rect, trans, old_widget=None, new_widget=None, **properties):
- """
- Applies a transition to a subset of the screen. Not documented.
- """
-
- x, y, _w, _h = rect
-
- old = renpy.display.layout.LiveCrop(rect, old_widget)
- new = renpy.display.layout.LiveCrop(rect, new_widget)
-
- inner = trans(old_widget=old, new_widget=new)
- delay = inner.delay
- inner = renpy.display.layout.Position(inner, xpos=x, ypos=y, xanchor=0, yanchor=0)
-
- f = renpy.display.layout.MultiBox(layout='fixed')
- f.add(new_widget)
- f.add(inner)
-
- return NoTransition(delay, old_widget=f, new_widget=f)
-
diff --git a/unrpyc/renpy/display/video.py b/unrpyc/renpy/display/video.py
deleted file mode 100644
index 5451e89..0000000
--- a/unrpyc/renpy/display/video.py
+++ /dev/null
@@ -1,234 +0,0 @@
-# Copyright 2004-2013 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 renpy.display
-import renpy.audio
-
-# The movie displayable that's currently being shown on the screen.
-current_movie = None
-
-# True if the movie that is currently displaying is in fullscreen mode,
-# False if it's a smaller size.
-fullscreen = False
-
-# The size of a Movie object that hasn't had an explicit size set.
-default_size = (400, 300)
-
-# The file we allocated the surface for.
-surface_file = None
-
-# The surface to display the movie on, if not fullscreen.
-surface = None
-
-def movie_stop(clear=True, only_fullscreen=False):
- """
- Stops the currently playing movie.
- """
-
- if (not fullscreen) and only_fullscreen:
- return
-
- renpy.audio.music.stop(channel='movie')
-
-
-def movie_start(filename, size=None, loops=0):
- """
- This starts a movie playing.
- """
-
- if renpy.game.less_updates:
- return
-
- global default_size
-
- if size is not None:
- default_size = size
-
- filename = [ filename ]
-
- if loops == -1:
- loop = True
- else:
- loop = False
- filename = filename * (loops + 1)
-
- renpy.audio.music.play(filename, channel='movie', loop=loop)
-
-movie_start_fullscreen = movie_start
-movie_start_displayable = movie_start
-
-def early_interact():
- """
- Called early in the interact process, to clear out the fullscreen
- flag.
- """
-
- global fullscreen
- global current_movie
-
- fullscreen = True
- current_movie = None
-
-
-def interact():
- """
- This is called each time the screen is redrawn. It helps us decide if
- the movie should be displayed fullscreen or not.
- """
-
- global surface
- global surface_file
-
- if not renpy.audio.music.get_playing("movie"):
- surface = None
- surface_file = None
- return False
-
- if fullscreen:
- return True
- else:
- return False
-
-def get_movie_texture():
- """
- Gets a movie texture we can draw to the screen.
- """
-
- global surface
- global surface_file
-
- playing = renpy.audio.music.get_playing("movie")
-
- pss = renpy.audio.audio.pss
-
- if pss:
- size = pss.movie_size()
- else:
- size = (64, 64)
-
- if (surface is None) or (surface.get_size() != size) or (surface_file != playing):
- surface = renpy.display.pgrender.surface(size, False)
- surface_file = playing
- surface.fill((0, 0, 0, 255))
-
- tex = None
-
- if playing is not None:
- renpy.display.render.mutated_surface(surface)
- tex = renpy.display.draw.load_texture(surface, True)
-
- return tex
-
-
-def render_movie(width, height):
- tex = get_movie_texture()
-
- if tex is None:
- return None
-
- sw, sh = tex.get_size()
-
- scale = min(1.0 * width / sw, 1.0 * height / sh)
-
- dw = scale * sw
- dh = scale * sh
-
- rv = renpy.display.render.Render(width, height, opaque=True)
- rv.forward = renpy.display.render.Matrix2D(1.0 / scale, 0.0, 0.0, 1.0 / scale)
- rv.reverse = renpy.display.render.Matrix2D(scale, 0.0, 0.0, scale)
- rv.blit(tex, (int((width - dw) / 2), int((height - dh) / 2)))
-
- return rv
-
-class Movie(renpy.display.core.Displayable):
- """
- This is a displayable that shows the current movie.
- """
-
- fullscreen = False
-
- def __init__(self, fps=24, size=None, **properties):
- """
- @param fps: The framerate that the movie should be shown at.
- """
- super(Movie, self).__init__(**properties)
- self.size = size
-
- def render(self, width, height, st, at):
-
- size = self.size
-
- if size is None:
- size = default_size
-
- width, height = size
-
- rv = render_movie(width, height)
-
- if rv is None:
- rv = renpy.display.render.Render(0, 0)
-
- # Usually we get redrawn when the frame is ready - but we want
- # the movie to disappear if it's ended, or if it hasn't started
- # yet.
- renpy.display.render.redraw(self, 0.1)
-
- return rv
-
-
- def per_interact(self):
- global fullscreen
- fullscreen = False
-
- global current_movie
- current_movie = self
-
-
-def playing():
- return renpy.audio.music.get_playing("movie")
-
-def frequent():
- """
- Called to update the video playback. Returns true if a video refresh is
- needed, false otherwise.
- """
-
- if not playing():
- return 0
-
- pss = renpy.audio.audio.pss
-
- if pss.needs_alloc():
-
- if renpy.display.video.fullscreen and renpy.display.draw.fullscreen_surface:
- surf = renpy.display.draw.fullscreen_surface
- else:
- get_movie_texture()
- surf = renpy.display.scale.real(surface)
-
- pss.alloc_event(surf)
-
- rv = pss.refresh_event()
-
- if rv and current_movie is not None:
- renpy.display.render.redraw(current_movie, 0)
-
- return rv
diff --git a/unrpyc/unrpyc.py b/unrpyc/unrpyc.py
deleted file mode 100644
index 54ca818..0000000
--- a/unrpyc/unrpyc.py
+++ /dev/null
@@ -1,97 +0,0 @@
-import optparse
-import os.path
-import sys
-import pickle as pickle
-import codecs
-import glob
-import itertools
-import zlib
-
-class Dummy:
- def record_pycode(self,*args,**kwargs):
- return
- all_pycode = []
-
-def import_renpy(basedir=None):
- #import renpy from another location.
- if basedir:
- sys.path.append(basedir)
- global renpy
- global decompiler
-
- # Needed for pickle to read the AST
- try:
- import renpy
- except ImportError:
- print("\nFailed at importing renpy. Are you sure that the renpy directory can be found in sys.path or the current working directory?\n")
- raise
- # try to import as much renpy modules as possible, but some modules might not exist
- # in older ren'py versions.
- try: import renpy.object
- except: pass
- try:
- import renpy.game
- renpy.game.script = Dummy()
- except: pass
- try: import renpy.ast
- except: pass
- try: import renpy.atl
- except: pass
-
- import decompiler
- if basedir:
- sys.path.remove(basedir)
-
-
-def read_ast_from_file(in_file):
- raw_contents = zlib.decompress(in_file.read())
- data, stmts = pickle.loads(raw_contents)
- return stmts
-
-def decompile_rpyc(input_filename, out_filename, overwrite=False, ignore_python=False):
- # Output filename is input filename but with .rpy extension
- path, ext = os.path.splitext(input_filename)
-
- print(("Decompiling %s to %s..." % (input_filename, out_filename)))
-
- if not overwrite and os.path.exists(out_filename):
- print("Output file already exists. Pass --clobber to overwrite.")
- return False # Don't stop decompiling if one file already exists
-
- with open(input_filename, 'rb') as in_file:
- ast = read_ast_from_file(in_file)
-
- with codecs.open(out_filename, 'w', encoding='utf-8') as out_file:
- decompiler.pretty_print_ast(out_file, ast, ignore_python)
- return True
-
-if __name__ == "__main__":
- parser = optparse.OptionParser(
- usage="usage: %prog [options] input output",
- version="%prog 0.1")
-
- parser.add_option('-c', '--clobber', action='store_true', dest='clobber',
- default=False, help="overwrites existing output files")
-
- parser.add_option('-b', '--basedir', action='store', dest='basedir',
- help="specify the game base directory in which the 'renpy' directory is located")
-
- parser.add_option('-p', '--ignore-python', action='store_true', dest='ignore_python',
- default=False, help="ignore python blocks")
-
- options, args = parser.parse_args()
-
-
- if options.basedir:
- import_renpy(options.basedir)
- else:
- import_renpy()
-
- if len(args) != 2:
- parser.print_help();
- parser.error("Incorrect number of arguments: expected 2, got %d." % (len(args)))
-
- decompile_rpyc(args[0], args[1], options.clobber, options.ignore_python)
-else:
- import_renpy()
-