diff options
51 files changed, 1236 insertions, 19229 deletions
@@ -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 @@ -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 @@ -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() - |