Restore merges.

This commit is contained in:
Tom Rothamel
2011-03-31 18:47:17 -04:00
parent 9483a7daa5
commit 14f24424ea
75 changed files with 1907 additions and 714 deletions
+1
View File
@@ -104,3 +104,4 @@ module/glrtt_fbo.c
module/glrtt_fbo.html
tutorial/game/cache
renpy.exe.log
renpy/vc_version.py
+5 -2
View File
@@ -48,9 +48,12 @@ init -1210 python:
config.imagereference_respects_position = True
config.predict_screens = False
config.choice_screen_chosen = False
if version <= (6, 12, 0):
config.keep_running_transform = False
config.image_attributes = False
config.new_character_image_argument = False
init 1210 python hide::
# This returns true if the script_version is <= the
+19 -1
View File
@@ -1315,7 +1315,25 @@ init -1140 python:
def __call__(self):
renpy.take_screenshot()
##########################################################################
# Side Images
def SideImage(tag="side"):
"""
:doc: side_image_function
Returns the side image associated with the currently speaking character,
or a Null displayable if no such side image exists.
"""
name = renpy.get_side_image(tag)
if name is None:
return Null()
else:
return ImageReference(name)
##########################################################################
# Preferences Constructor
+31 -9
View File
@@ -115,23 +115,45 @@ def tree(root):
def main():
# Revision updating is done early, so we can do it even if the rest
# of the program fails.
# Determine the version. We grab the current revision, and if any
# file has changed, bump it by 1.
p = subprocess.Popen(["bzr", "revno"], stdout=subprocess.PIPE)
revno = p.stdout.read().strip()
revno = int(revno)
p.wait()
p = subprocess.Popen(["bzr", "status", "-V"], stdout=subprocess.PIPE)
status = p.stdout.read().strip()
p.wait()
if status:
revno += 1
# Write the revno to the necessary files.
f = file("lib/update-version.txt", "w")
f.write("{revno}-{now} base\n".format(revno=revno, now=time.time()))
f.close()
f = file("renpy/vc_version.py", "w")
f.write("""\
# The version of Ren'Py reported by the version control software.
vc_version = {revno}
""".format(revno=revno))
f.close()
# Check to be sure we have the right arguments.
if len(sys.argv) != 2:
print "Usage: %s <prefix>" % sys.argv[0]
return
prefix = sys.argv[1]
# Copy over the screens, to keep them up to date.
shutil.copy("tutorial/game/screens.rpy", "template/game/screens.rpy")
# Update the update-version.txt file.
p = subprocess.Popen(["bzr", "revno"], stdout=subprocess.PIPE)
revno = p.stdout.read().strip() + "-" + str(time.time())
p.wait()
f = file("lib/update-version.txt", "w")
f.write(revno + " base\n")
f.close()
# Compile all the python files.
compileall.compile_dir("renpy/", ddir=prefix + "/renpy/", force=1)
+1 -1
View File
@@ -1,3 +1,3 @@
init -999:
$ config.script_version = (6, 12, 0)
$ config.script_version = (6, 12, 1)
+4 -4
View File
@@ -3,11 +3,11 @@
A="$RENPY_ANDROID"
CFLAGS="$CFLAGS -DANDROID"
CFLAGS="$CFLAGS -I$A/android-sdl/sdl/sdl-1.2/include"
CFLAGS="$CFLAGS -I$A/android-sdl/jni/png"
CFLAGS="$CFLAGS -I$A/android-sdl/jni/freetype/include"
CFLAGS="$CFLAGS -I$A/sdl/sdl-1.2/include"
CFLAGS="$CFLAGS -I$A/jni/png"
CFLAGS="$CFLAGS -I$A/jni/freetype/include"
LDFLAGS="$LDFLAGS -L$A/android-sdl/libs/armeabi -L$A/android-sdl/obj/local/armeabi"
LDFLAGS="$LDFLAGS -L$A/libs/armeabi -L$A/obj/local/armeabi"
export CFLAGS
export LDFLAGS
+2 -1
View File
@@ -4,7 +4,6 @@ import os
import os.path
import platform
import sys
import subprocess
# These control the level of optimization versus debugging.
extra_compile_args = [ "-O3", "-Wno-unused-function" ]
@@ -128,6 +127,7 @@ def cython(fn):
print pyx, "is not newer than", c
return
import subprocess
subprocess.call(["cython", pyx])
@@ -283,6 +283,7 @@ display("glrtt_copy", glew_libs)
display("glrtt_fbo", glew_libs)
sys.path.append('..')
import renpy
distutils.core.setup(
+80 -70
View File
@@ -29,8 +29,18 @@ import os
# ***** ***** ***** ***** ***** ***** **** ***** ***** ***** *****
# Be sure to change script_version in launcher/script_version.rpy, too!
# Be sure to change _renpy.pyx and module.py, if necessary.
version = "Ren'Py 6.12.0f"
try:
from renpy.vc_version import vc_version; vc_version
except ImportError:
vc_version = 0
# The tuple giving the version. This needs to be updated when
# we bump the version.
version_tuple = (6, 12, 1, vc_version)
# A verbose string computed from that version.
version = "Ren'Py " + ".".join(str(i) for i in version_tuple)
# Other versions.
script_version = 5003000
@@ -53,105 +63,106 @@ def import_cython():
grab the various cython modules.
"""
import renpy.display.accelerator
import renpy.display.gldraw
import renpy.display.glenviron
import renpy.display.glenviron_fixed
import renpy.display.glenviron_limited
import renpy.display.glenviron_shader
import renpy.display.glrtt_copy
import renpy.display.glrtt_fbo
import renpy.display.glshader
import renpy.display.gltexture
import renpy.display.render
import renpy.display.accelerator #@UnresolvedImport
import renpy.display.gldraw #@UnresolvedImport
import renpy.display.glenviron #@UnresolvedImport
import renpy.display.glenviron_fixed #@UnresolvedImport
import renpy.display.glenviron_limited #@UnresolvedImport
import renpy.display.glenviron_shader #@UnresolvedImport
import renpy.display.glrtt_copy #@UnresolvedImport
import renpy.display.glrtt_fbo #@UnresolvedImport
import renpy.display.glshader #@UnresolvedImport
import renpy.display.gltexture #@UnresolvedImport
import renpy.display.render #@UnresolvedImport
# Prevent a pyflakes warning.
renpy
def import_all():
import renpy.display #@UnresolvedImport
# Should probably be early, as we will add it as a base to serialized things.
import renpy.object
import renpy.object #@UnresolvedImport
import renpy.game
import renpy.game #@UnresolvedImport
# Adds in the Ren'Py loader.
import renpy.loader
import renpy.loader #@UnresolvedImport
import renpy.ast
import renpy.atl
import renpy.curry
import renpy.easy
import renpy.execution
import renpy.loadsave
import renpy.parser
import renpy.python # object
import renpy.remote
import renpy.script
import renpy.statements
import renpy.style
import renpy.ast #@UnresolvedImport
import renpy.atl #@UnresolvedImport
import renpy.curry #@UnresolvedImport
import renpy.easy #@UnresolvedImport
import renpy.execution #@UnresolvedImport
import renpy.loadsave #@UnresolvedImport
import renpy.parser #@UnresolvedImport
import renpy.python #@UnresolvedImport
import renpy.remote #@UnresolvedImport
import renpy.script #@UnresolvedImport
import renpy.statements #@UnresolvedImport
import renpy.style #@UnresolvedImport
import renpy.display
import renpy.display.presplash
import renpy.display.iliad # Must be before scale and pgrender.
import renpy.display.pgrender
import renpy.display.scale # Must be before module.
import renpy.display.module
import renpy.display.presplash #@UnresolvedImport
import renpy.display.iliad # Must be before scale and pgrender. @UnresolvedImport
import renpy.display.pgrender #@UnresolvedImport
import renpy.display.scale # Must be before module. @UnresolvedImport
import renpy.display.module #@UnresolvedImport
# Now that render is pre-compiled, we want to use the
# location of renpy.display.module to find it.
import _renpy
libexec = os.path.dirname(_renpy.__file__)
renpy.display.__path__.insert(0, os.path.join(libexec, "renpy", "display"))
renpy.display.__path__.insert(0, os.path.join(libexec, "renpy", "display")) #@UndefinedVariable
# Also find encodings, to deal with the way py2exe lays things out.
import encodings
libexec = os.path.dirname(encodings.__path__[0])
renpy.display.__path__.insert(1, os.path.join(libexec, "renpy", "display"))
renpy.display.__path__.insert(1, os.path.join(libexec, "renpy", "display")) #@UndefinedVariable
import renpy.display.render # Most display stuff depends on this.
import renpy.display.render # Most display stuff depends on this. @UnresolvedImport
import renpy.display.core # object
import renpy.display.font
import renpy.display.text # core, font
import renpy.display.layout # core
import renpy.display.motion # layout
import renpy.display.behavior # layout
import renpy.display.transition # core, layout
import renpy.display.im
import renpy.display.image # core, behavior, im
import renpy.display.video
import renpy.display.focus
import renpy.display.anim
import renpy.display.particle
import renpy.display.joystick
import renpy.display.minigame
import renpy.display.screen
import renpy.display.dragdrop
import renpy.display.imagemap
import renpy.display.predict
import renpy.display.core # object @UnresolvedImport
import renpy.display.font #@UnresolvedImport
import renpy.display.text # core, font @UnresolvedImport
import renpy.display.layout # core @UnresolvedImport
import renpy.display.motion # layout @UnresolvedImport
import renpy.display.behavior # layout @UnresolvedImport
import renpy.display.transition # core, layout @UnresolvedImport
import renpy.display.im #@UnresolvedImport
import renpy.display.imagelike #@UnresolvedImport
import renpy.display.image # core, behavior, im, imagelike @UnresolvedImport
import renpy.display.video #@UnresolvedImport
import renpy.display.focus #@UnresolvedImport
import renpy.display.anim #@UnresolvedImport
import renpy.display.particle #@UnresolvedImport
import renpy.display.joystick #@UnresolvedImport
import renpy.display.minigame #@UnresolvedImport
import renpy.display.screen #@UnresolvedImport
import renpy.display.dragdrop #@UnresolvedImport
import renpy.display.imagemap #@UnresolvedImport
import renpy.display.predict #@UnresolvedImport
import renpy.display.error
import renpy.display.error #@UnresolvedImport
# Note: For windows to work, renpy.audio.audio needs to be after
# renpy.display.module.
import renpy.audio.audio
import renpy.audio.music
import renpy.audio.sound
import renpy.audio.audio #@UnresolvedImport
import renpy.audio.music #@UnresolvedImport
import renpy.audio.sound #@UnresolvedImport
import renpy.ui
import renpy.screenlang
import renpy.ui #@UnresolvedImport
import renpy.screenlang #@UnresolvedImport
import renpy.lint
import renpy.warp
import renpy.lint #@UnresolvedImport
import renpy.warp #@UnresolvedImport
import renpy.exports
import renpy.character # depends on exports.
import renpy.exports #@UnresolvedImport
import renpy.character # depends on exports. @UnresolvedImport
import renpy.config # depends on lots.
import renpy.store # depends on everything.
import renpy.main
import renpy.config # depends on lots. @UnresolvedImport
import renpy.store # depends on everything. @UnresolvedImport
import renpy.main #@UnresolvedImport
# Import everything into renpy.exports, provided it isn't
# already there.
@@ -161,7 +172,7 @@ def import_all():
# This reloads all modules.
def reload_all():
import renpy
import renpy #@UnresolvedImport
renpy.log.info("Reloading.")
# Shut down the cache thread.
@@ -189,4 +200,3 @@ def reload_all():
renpy.display.draw = None
import_all()
+27 -8
View File
@@ -27,6 +27,8 @@
# updating.
import renpy
import renpy.display
import re
import time
import md5
@@ -144,8 +146,8 @@ class PyCode(object):
pass
code = self.source
if isinstance(code, renpy.python.ast.AST):
code = renpy.python.ast.dump(code)
if isinstance(code, renpy.python.ast.AST): #@UndefinedVariable
code = renpy.python.ast.dump(code) #@UndefinedVariable
self.hash = chr(renpy.bytecode_version) + md5.md5(code.encode("utf-8")).digest()
return self.hash
@@ -312,12 +314,18 @@ class Say(Node):
'what',
'with_',
'interact',
'attributes',
]
def diff_info(self):
return (Say, self.who, self.what)
def __init__(self, loc, who, what, with_, interact=True):
def __setstate__(self, state):
self.attributes = None
self.interact = True
setstate(self, state)
def __init__(self, loc, who, what, with_, interact=True, attributes=None):
super(Say, self).__init__(loc)
@@ -336,8 +344,14 @@ class Say(Node):
self.with_ = with_
self.interact = interact
# A tuple of attributes that are applied to the character that's
# speaking, or None to disable this behavior.
self.attributes = attributes
def execute(self):
renpy.exports.say_attributes = self.attributes
if self.who is not None:
if self.who_fast:
who = getattr(renpy.store, self.who, None)
@@ -364,12 +378,15 @@ class Say(Node):
renpy.store._last_say_what = what
say_menu_with(self.with_, renpy.game.interface.set_transition)
renpy.exports.say(who, what, interact=getattr(self, 'interact', True))
renpy.exports.say(who, what, interact=self.interact)
return self.next
def predict(self):
old_attributes = renpy.exports.say_attributes
renpy.exports.say_attributes = self.attributes
if self.who is not None:
if self.who_fast:
who = getattr(renpy.store, self.who)
@@ -389,6 +406,8 @@ class Say(Node):
renpy.exports.predict_say(who, what)
renpy.exports.say_attributes = old_attributes
return [ self.next ]
def scry(self):
@@ -733,7 +752,7 @@ def predict_imspec(imspec, scene=False):
return
else:
img = renpy.exports.images.get(name, None)
img = renpy.display.image.images.get(name, None)
if img is None:
return
@@ -742,9 +761,9 @@ def predict_imspec(imspec, scene=False):
full_name = (tag,) + full_name[1:]
if scene:
renpy.game.context().predict_info.images.predict_scene(layer)
renpy.game.context().images.predict_scene(layer)
renpy.game.context().predict_info.images.predict_show(tag or name, layer)
renpy.game.context().images.predict_show(tag or name, layer)
renpy.display.predict.displayable(img)
@@ -900,7 +919,7 @@ class Hide(Node):
if tag is None:
tag = name[0]
renpy.game.context().predict_info.images.predict_hide(tag, layer)
renpy.game.context().images.predict_hide(tag, layer)
return [ ]
+2
View File
@@ -87,6 +87,8 @@ PROPERTIES = {
"xoffset" : int,
"yoffset" : int,
"offset" : (int, int),
"xcenter" : position,
"ycenter" : position,
}
+5 -3
View File
@@ -25,6 +25,8 @@
# at least pcm_ok, we have no sound whatsoever.
import renpy
import renpy.audio
import renpy.display
import time
import pygame
@@ -48,7 +50,7 @@ if 'pss' not in disable:
if pss is None:
try:
import android_sound as pss
import android_sound as pss #@UnresolvedImport
print "Imported android_sound."
except:
pass
@@ -56,10 +58,10 @@ if 'pss' not in disable:
if 'mix' not in disable:
try:
import winmixer as mix; mix
import winmixer as mix; mix #@UnresolvedImport
except:
try:
import linmixer as mix; mix
import linmixer as mix; mix #@UnresolvedImport
except:
pass
+1 -1
View File
@@ -21,7 +21,7 @@
# The public API for music in games.
import renpy
import renpy.audio
# A list of music channels.
music_channels = [ ]
+1 -1
View File
@@ -24,7 +24,7 @@
# TODO: Check to see if SFX are enabled before playing sounds with play or
# queue.
import renpy
import renpy.audio
# This is basically a thin wrapper around music, with the default
# channel set to "sound".
+1 -1
View File
@@ -58,7 +58,7 @@ def extra_imports():
import posixpath; posixpath # W0403
import ctypes
import ctypes.wintypes; ctypes.wintypes
import EasyDialogs; EasyDialogs
import EasyDialogs; EasyDialogs #@UnresolvedImport
import argparse; argparse
import compiler; compiler
import textwrap; textwrap
+131 -50
View File
@@ -22,6 +22,8 @@
# The Character object (and friends).
import renpy
import renpy.display
import re
# This matches the dialogue-relevant text tags.
@@ -558,6 +560,14 @@ class ADVCharacter(object):
self.dynamic = v('dynamic')
self.screen = v('screen')
self.mode = v('mode')
if renpy.config.new_character_image_argument:
if "image" in properties:
self.image_tag = properties.pop("image")
else:
self.image_tag = kind.image_tag
else:
self.image_tag = None
self.display_args = dict(
interact = d('interact'),
@@ -587,8 +597,9 @@ class ADVCharacter(object):
self.show_args = { }
self.cb_args = { }
if "image" in properties:
self.show_args["image"] = properties.pop("image")
if not renpy.config.new_character_image_argument:
if "image" in properties:
self.show_args["image"] = properties.pop("image")
if "slow_abortable" in properties:
self.what_args["slow_abortable"] = properties.pop("slow_abortable")
@@ -662,57 +673,126 @@ class ADVCharacter(object):
window_args=self.window_args,
screen=self.screen,
**self.show_args)
def resolve_say_attributes(self, predict):
"""
Deals with image attributes associated with the current say
statement.
"""
attrs = renpy.exports.get_say_attributes()
if not attrs:
return
if not self.image_tag:
if not predict:
raise Exception("Say has image attributes, but there's no image tag associated with the speaking character.")
else:
return
tagged_attrs = (self.image_tag,) + attrs
images = renpy.game.context().images
# If image is showing already, resolve it, then show or predict it.
if images.showing("master", (self.image_tag,)):
new_image = images.apply_attributes("master", self.image_tag, tagged_attrs)
if new_image is None:
new_image = tagged_attrs
if predict:
images.predict_show(new_image)
else:
renpy.exports.show(new_image)
else:
# Otherwise, just record the attributes of the image.
images.predict_show("master", tagged_attrs)
def __call__(self, what, interact=True, **kwargs):
# Check self.condition to see if we should show this line at all.
if not (self.condition is None or renpy.python.py_eval(self.condition)):
return True
if interact:
renpy.exports.mode(self.mode)
# Figure out the arguments to display.
display_args = self.display_args.copy()
display_args.update(kwargs)
display_args["interact"] = display_args["interact"] and interact
self.resolve_say_attributes(False)
who = self.name
old_side_image_attributes = renpy.exports.side_image_attributes
if self.image_tag:
attrs = (self.image_tag,) + renpy.game.context().images.get_attributes("master", self.image_tag)
else:
attrs = None
renpy.exports.side_image_attributes = attrs
try:
if interact:
renpy.exports.mode(self.mode)
# Figure out the arguments to display.
display_args = self.display_args.copy()
display_args.update(kwargs)
display_args["interact"] = display_args["interact"] and interact
who = self.name
# If dynamic is set, evaluate the name expression.
if self.dynamic:
who = renpy.python.py_eval(who)
if who is not None:
who = self.who_prefix + who + self.who_suffix
what = self.what_prefix + what + self.what_suffix
# Run the add_function, to add this character to the
# things like NVL-mode.
self.do_add(who, what)
# Now, display the damned thing.
self.do_display(who, what, cb_args=self.cb_args, **display_args)
# Indicate that we're done.
self.do_done(who, what)
# Finally, log this line of dialogue.
if who and isinstance(who, (str, unicode)):
renpy.exports.log(who)
renpy.exports.log(what)
renpy.exports.log("")
# If dynamic is set, evaluate the name expression.
if self.dynamic:
who = renpy.python.py_eval(who)
if who is not None:
who = self.who_prefix + who + self.who_suffix
what = self.what_prefix + what + self.what_suffix
# Run the add_function, to add this character to the
# things like NVL-mode.
self.do_add(who, what)
# Now, display the damned thing.
self.do_display(who, what, cb_args=self.cb_args, **display_args)
# Indicate that we're done.
self.do_done(who, what)
# Finally, log this line of dialogue.
if who and isinstance(who, (str, unicode)):
renpy.exports.log(who)
renpy.exports.log(what)
renpy.exports.log("")
finally:
renpy.exports.side_image_attributes = old_side_image_attributes
def predict(self, what):
if self.dynamic:
who = "<Dynamic>"
self.resolve_say_attributes(True)
old_side_image_attributes = renpy.exports.side_image_attributes
if self.image_tag:
attrs = self.image_tag + renpy.game.context().images.get_attributes("master", self.image_tag)
else:
who = self.name
attrs = None
renpy.exports.side_image_attributes = attrs
return self.do_predict(who, what)
try:
if self.dynamic:
who = "<Dynamic>"
else:
who = self.name
return self.do_predict(who, what)
finally:
renpy.exports.side_image_attributes = None
def will_interact(self):
@@ -745,6 +825,17 @@ def Character(name=NotSet, kind=None, **properties):
be used to define a template character, and then copy that
character with changes.
**Linked Image**
An image tag may be associated with a Character. This allows a
say statement involving this character to display an image with
the tag, and also allows Ren'Py to automatically select a side
image to show when this character speaks.
`image`
A string giving the image tag that is linked with this
character.
**Prefixes and Suffixes.**
These allow a prefix and suffix to be applied to the name of the
character, and to the text being shown. This can be used, for
@@ -779,12 +870,6 @@ def Character(name=NotSet, kind=None, **properties):
expression. That string will be evaluated before each line
of dialogue, and the result used as the name of the character.
`image`
If true, then `name` is expected to name an image file. That
image is used as the name of the character.
**Controlling Interactions.**
These options control if the dialogue is displayed, if an
interaction occurs, and the mode that is entered upon display.
@@ -888,10 +973,6 @@ def Character(name=NotSet, kind=None, **properties):
`window_style` arguments, respectively.
"""
if kind is None:
kind = renpy.store.adv
+12 -2
View File
@@ -24,7 +24,7 @@
# methods that perform standard tasks, like the say and menu methods.
# This will be deleted by the end of this file.
import renpy
import renpy.display
import os
# Can we add more config variables?
@@ -434,6 +434,7 @@ screenshot_crop = None
gamedir = None
basedir = None
renpy_base = None
commondir = None
# Should we enable OpenGL mode?
gl_enable = True
@@ -472,10 +473,19 @@ variants = [ None ]
# A function from (auto_parameter, variant) -> displayable.
imagemap_auto_function = None
# Should we keep the running transform when we merely change the image?
keep_running_transform = True
# Should we use image attributes?
image_attributes = True
# Should we use the new version of the character image argument?
new_character_image_argument = True
del renpy
del os
def init():
import renpy # W0404
import renpy # W0404 @Reimport
global style_properties
style_properties = renpy.style.style_properties
+1 -1
View File
@@ -21,7 +21,7 @@
# This file contains support for state-machine controlled animations.
import renpy
import renpy.display
import random
class State(object):
+54 -3
View File
@@ -22,7 +22,9 @@
# This contains various Displayables that handle events.
import renpy
import renpy.display
import renpy.audio
from renpy.display.render import render, Render
import pygame
@@ -628,6 +630,57 @@ def TextButton(text, style='button', text_style='button_text',
text = renpy.display.text.Text(text, style=text_style)
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 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):
@@ -1360,6 +1413,4 @@ class MouseArea(renpy.display.core.Displayable):
run(self.unhovered)
+41 -62
View File
@@ -22,7 +22,8 @@
# This file contains code for initializing and managing the display
# window.
import renpy
import renpy.display
import renpy.audio
import pygame
import sys
@@ -39,7 +40,7 @@ except:
pass
try:
import android
import android #@UnresolvedImport
except:
android = None
@@ -378,53 +379,16 @@ class Displayable(renpy.object.Object):
"""
return self
class ImagePredictInfo(renpy.object.Object):
"""
This stores information involved in image prediction.
"""
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.
"""
def after_setstate(self):
for i in renpy.config.layers + renpy.config.top_layers:
self.images.setdefault(i, {})
def __init__(self, ipi=None):
return child
super(ImagePredictInfo, self).__init__()
# layer -> (tag -> image name)
self.images = { }
if ipi is None:
for i in renpy.config.layers + renpy.config.top_layers:
self.images[i] = { }
else:
for i in renpy.config.layers + renpy.config.top_layers:
self.images[i] = ipi.images[i].copy()
def showing(self, layer, name):
shown = self.images[layer].get(name[0], None)
if shown is None or len(shown) < len(name):
return False
for a, b in zip(name, shown):
if a != b:
return False
return True
def predict_scene(self, layer):
self.images[layer].clear()
def predict_show(self, name, layer):
self.images[layer][name[0]] = name
def predict_hide(self, tag, layer):
self.images[layer].pop(tag, None)
class SceneListEntry(renpy.object.Object):
@@ -477,7 +441,7 @@ class SceneLists(renpy.object.Object):
things to the user.
"""
__version__ = 5
__version__ = 6
def after_setstate(self):
for i in renpy.config.layers + renpy.config.top_layers:
@@ -508,8 +472,11 @@ class SceneLists(renpy.object.Object):
if version < 5:
self.drag_group = None
if version < 6:
self.shown = self.image_predict_info
def __init__(self, oldsl, ipi):
def __init__(self, oldsl, shown):
super(SceneLists, self).__init__()
@@ -526,8 +493,8 @@ class SceneLists(renpy.object.Object):
# been applied to the layer as a whole.
self.layer_at_list = { }
# Represents the current stare of image_prediction.
self.image_predict_info = ipi
# The current shown images,
self.shown = shown
# A list of (layer, tag) pairs that are considered to be
# transient.
@@ -709,31 +676,43 @@ class SceneLists(renpy.object.Object):
self.at_list[layer][key] = at_list
if key and name:
self.image_predict_info.images[layer][key] = 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)
if not isinstance(thing, renpy.display.motion.Transform):
thing = self.transform_state(default_transform, thing)
add_index, remove_index = self.find_index(layer, key, zorder, behind)
at = None
st = None
if remove_index is not None:
at = l[remove_index].animation_time
thing = self.transform_state(l[remove_index].displayable, thing)
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()
@@ -823,7 +802,7 @@ class SceneLists(renpy.object.Object):
tag = self.layers[layer][remove_index].tag
if tag:
self.image_predict_info.images[layer].pop(tag, None)
self.shown.predict_hide(layer, (tag,))
self.at_list[layer].pop(tag, None)
self.hide_or_replace(layer, remove_index, "hide")
@@ -847,7 +826,7 @@ class SceneLists(renpy.object.Object):
self.hide_or_replace(layer, i, hide)
self.at_list[layer].clear()
self.image_predict_info.images[layer].clear()
self.shown.predict_scene(layer)
self.layer_at_list[layer] = (None, [ ])
def set_layer_at_list(self, layer, at_list):
@@ -871,7 +850,7 @@ class SceneLists(renpy.object.Object):
is found in the scene list.
"""
return self.image_predict_info.showing(layer, name)
return self.shown.showing(layer, name)
def make_layer(self, layer, properties):
"""
@@ -1230,7 +1209,7 @@ class Interface(object):
draws = { }
try:
import renpy.display.gldraw as gldraw
import renpy.display.gldraw as gldraw #@UnresolvedImport
draws["gl"] = gldraw.GLDraw
except:
renpy.log.info("Couldn't import gl renderer.")
@@ -1693,7 +1672,7 @@ class Interface(object):
if android.check_pause():
import android_sound
import android_sound #@UnresolvedImport
android_sound.pause_all()
pygame.time.set_timer(PERIODIC, 0)
+3 -2
View File
@@ -22,7 +22,7 @@
# TODO: Use overlap (rather than simple pointer location) to determine
# drag and drop.
import renpy
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
@@ -50,7 +50,8 @@ def default_drag_joined(drag):
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.
+1 -1
View File
@@ -22,7 +22,7 @@
# This file contains code to manage focus on the display.
import pygame
import renpy
import renpy.display
class Focus(object):
+1 -1
View File
@@ -29,7 +29,7 @@ except:
import _renpy_font
pygame.font = _renpy_font
import renpy
import renpy.display
# This contains a map from (fn, size, bold, italics, underline) to the
# unloaded font object corresponding to that specification.
+2 -2
View File
@@ -167,8 +167,8 @@ cdef class GLDraw:
# Should we use the fast (but incorrect) dissolve mode?
self.fast_dissolve = False # renpy.android
# Should we use clipping planes or scissors?
self.use_clipping_planes = False
# Should we use clipping planes or stencils?
self.use_clipping_planes = True
# Should we always report pixels as being always opaque?
self.always_opaque = renpy.android
+2 -1
View File
@@ -23,7 +23,8 @@
# size-based caching and constructing images from operations (like
# cropping and scaling).
import renpy
import renpy.display
import math
import zipfile
import cStringIO
+211 -376
View File
@@ -24,9 +24,31 @@
# of the stuff thar uses images remaining.
import renpy
from renpy.display.render import render, Render, Matrix2D
import renpy.display
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)
Image = renpy.display.im.image
def wrap_render(child, w, h, st, at):
rend = render(child, w, h, st, at)
@@ -88,8 +110,8 @@ class ImageReference(renpy.display.core.Displayable):
# Scan through, searching for an image (defined with an
# input statement) that is a prefix of the given name.
while name:
if name in renpy.exports.images:
target = renpy.exports.images[name]
if name in images:
target = images[name]
try:
self.target = target.parameterize(name, parameters)
@@ -164,392 +186,205 @@ class ImageReference(renpy.display.core.Displayable):
self.find_target()
return [ self.target ]
class Solid(renpy.display.core.Displayable):
class ShownImageInfo(renpy.object.Object):
"""
:doc: disp_imagelike
A displayable that fills the area its assigned with `color`.
::
image white = Solid("#fff")
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.)
"""
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):
if self.color:
return [ renpy.display.im.SolidImage(self.color, 4, 4) ]
else:
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
si = renpy.display.im.SolidImage(color, SIZE, SIZE)
sr = render(si, SIZE, SIZE, st, at)
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(sr, (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.
__version__ = 2
.. 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.
`xborder`
The width of the border on the left and right sides of the
image.
`yborder`
The height of the border on the top and bottom sides of the
image.
`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)
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.
"""
__version__ = 1
if old is None:
def __init__(self, image, xborder, yborder, bilinear=True, tile=False, **properties):
super(Frame, self).__init__(**properties)
# 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 = { }
self.image = renpy.easy.displayable(image)
self.xborder = xborder
self.yborder = yborder
self.tile = tile
def render(self, width, height, st, at):
crend = render(self.image, width, height, st, at)
if isinstance(renpy.display.draw, renpy.display.swdraw.SWDraw):
return self.sw_render(crend, width, height)
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 xrange(0, cdw, csw):
for y in xrange(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
sw, sh = crend.get_size()
dw = int(width)
dh = int(height)
xb = min(self.xborder, sw / 2 - 1, width / 2 - 1)
yb = min(self.yborder, sh / 2 - 1, height / 2 - 1)
rv = Render(dw, dh)
self.draw_pattern(draw, xb, yb)
return rv
def draw_pattern(self, draw, xb, yb):
# Top row.
if yb:
if xb:
draw(0, xb, 0, yb)
draw(xb, -xb, 0, yb)
if xb:
draw(-xb, 0, 0, yb)
# Middle row.
if xb:
draw(0, xb, yb, -yb)
draw(xb, -xb, yb, -yb)
if xb:
draw(-xb, 0, yb, -yb)
# Bottom row.
if yb:
if xb:
draw(0, xb, -yb, 0)
draw(xb, -xb, -yb, 0)
if xb:
draw(-xb, 0, -yb, 0)
# 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 sw_render(self, crend, width, height):
source = crend.render_to_texture(True)
dw = int(width)
dh = int(height)
dest = renpy.display.pgrender.surface((dw, dh), True)
rv = dest
dest = renpy.display.scale.real(dest)
source = renpy.display.scale.real(source)
xb = renpy.display.scale.scale(self.xborder)
yb = renpy.display.scale.scale(self.yborder)
sw, sh = source.get_size()
dw, dh = dest.get_size()
xb = min(xb, sw / 2 - 1, dw / 2 - 1)
yb = min(yb, sh / 2 - 1, dh / 2 - 1)
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))
def after_upgrade(self, version):
if version < 2:
self.attributes = { }
self.shown = { }
# 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, xb, yb)
rrv = renpy.display.render.Render(width, height)
rrv.blit(rv, (0, 0))
rrv.depends_on(crend)
# And, finish up.
return rrv
def visit(self):
return [ self.image ]
class ImageButton(renpy.display.behavior.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)
for layer in self.images:
for tag in layer:
self.attributes[layer, tag] = 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.
"""
def visit(self):
return self.state_children.values()
return self.attributes.get((layer, tag), ())
def get_child(self):
return self.style.child or self.state_children[self.style.prefix]
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(name, 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 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):
"""
Predicts name being shown on layer.
"""
tag = name[0]
rest = name[1:]
self.attributes[layer, tag] = rest
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):
"""
Choose a single unique image for
"""
# 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
+361
View File
@@ -0,0 +1,361 @@
# Copyright 2004-2011 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):
if self.color:
return [ renpy.display.im.SolidImage(self.color, 4, 4) ]
else:
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
si = renpy.display.im.SolidImage(color, SIZE, SIZE)
sr = render(si, SIZE, SIZE, st, at)
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(sr, (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.
`xborder`
The width of the border on the left and right sides of the
image.
`yborder`
The height of the border on the top and bottom sides of the
image.
`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 __init__(self, image, xborder, yborder, bilinear=True, tile=False, **properties):
super(Frame, self).__init__(**properties)
self.image = renpy.easy.displayable(image)
self.xborder = xborder
self.yborder = yborder
self.tile = tile
def render(self, width, height, st, at):
crend = render(self.image, width, height, st, at)
if isinstance(renpy.display.draw, renpy.display.swdraw.SWDraw):
return self.sw_render(crend, width, height)
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 xrange(0, cdw, csw):
for y in xrange(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
sw, sh = crend.get_size()
dw = int(width)
dh = int(height)
xb = min(self.xborder, sw / 2 - 1, width / 2 - 1)
yb = min(self.yborder, sh / 2 - 1, height / 2 - 1)
rv = Render(dw, dh)
self.draw_pattern(draw, xb, yb)
return rv
def draw_pattern(self, draw, xb, yb):
# Top row.
if yb:
if xb:
draw(0, xb, 0, yb)
draw(xb, -xb, 0, yb)
if xb:
draw(-xb, 0, 0, yb)
# Middle row.
if xb:
draw(0, xb, yb, -yb)
draw(xb, -xb, yb, -yb)
if xb:
draw(-xb, 0, yb, -yb)
# Bottom row.
if yb:
if xb:
draw(0, xb, -yb, 0)
draw(xb, -xb, -yb, 0)
if xb:
draw(-xb, 0, -yb, 0)
def sw_render(self, crend, width, height):
source = crend.render_to_texture(True)
dw = int(width)
dh = int(height)
dest = renpy.display.pgrender.surface((dw, dh), True)
rv = dest
dest = renpy.display.scale.real(dest)
source = renpy.display.scale.real(source)
xb = renpy.display.scale.scale(self.xborder)
yb = renpy.display.scale.scale(self.yborder)
sw, sh = source.get_size()
dw, dh = dest.get_size()
xb = min(xb, sw / 2 - 1, dw / 2 - 1)
yb = min(yb, sh / 2 - 1, dh / 2 - 1)
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, xb, yb)
rrv = renpy.display.render.Render(width, height)
rrv.blit(rv, (0, 0))
rrv.depends_on(crend)
# And, finish up.
return rrv
def visit(self):
return [ self.image ]
+1 -1
View File
@@ -22,7 +22,7 @@
# This file handles imagemap caching.
import pygame
import renpy
import renpy.display
from renpy.display.render import render
+1 -1
View File
@@ -24,7 +24,7 @@
import os
import pygame
import renpy
import renpy.display
# Do we have a joystick enabled?
enabled = False
+1 -1
View File
@@ -22,7 +22,7 @@
# This file contains classes that handle layout of displayables on
# the screen.
import renpy
import renpy.display
from renpy.display.render import render, Render
+1 -1
View File
@@ -21,7 +21,7 @@
# The minigame API.
import renpy
import renpy.display
# A map from (text, font, size, color, bold, italics, underline) -> surface.
# This one is for the old frame.
+1 -4
View File
@@ -24,7 +24,7 @@
VERSION = (6, 12, 0)
import renpy
import renpy.display
import pygame; pygame # prevents pyflakes warning.
import sys
@@ -94,9 +94,6 @@ def scale(s, size):
Always works, but may not be high quality.
"""
width, height = s.get_size()
dx, dy = size
d = renpy.display.pgrender.surface(size, True)
bilinear_scale(s, d)
+40 -7
View File
@@ -25,7 +25,7 @@
import math
import types
import renpy
import renpy.display
from renpy.display.render import render
from renpy.display.layout import Container
@@ -325,6 +325,22 @@ class TransformState(renpy.object.Object):
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):
"""
@@ -388,6 +404,9 @@ class Transform(Container):
offset = Proxy("offset")
subpixel = Proxy("subpixel")
xcenter = Proxy("xcenter")
ycenter = Proxy("ycenter")
def after_upgrade(self, version):
@@ -568,14 +587,30 @@ class Transform(Container):
return
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()
rv.set_child(self.child._change_transform_child(child))
return rv
def _hide(self, st, at, kind):
if not (self.hide_request or self.replaced_request):
d = self()
d.kwargs = { }
d.take_state(self)
d.take_execution_state(self)
d = self.copy()
else:
d = self
@@ -1117,8 +1152,6 @@ def zoom_render(crend, x, y, w, h, zw, zh, bilinear):
rv.blit(crend, rv.reverse.transform(-x, -y))
# TODO: Bilinear?
return rv
+1 -1
View File
@@ -23,7 +23,7 @@
from renpy.display.render import render, BLIT
import renpy
import renpy.display
import random
+1 -1
View File
@@ -23,7 +23,7 @@
import sys
import pygame
import renpy
import renpy.display
# This class is used to make a copy of a pygame module's functions. We
# can then access those functions, and be relatively sure that those
+1 -2
View File
@@ -21,8 +21,7 @@
# This file contains the routines that manage image prediction.
import traceback
import renpy
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,
-1
View File
@@ -31,7 +31,6 @@ proc = None
# user. If it decides to show something to the user, uses subprocess
# to actually handle the showing.
def start(gamedir):
import os
import os.path
if "RENPY_LESS_UPDATES" in os.environ:
+1 -1
View File
@@ -24,7 +24,7 @@
import os
import math
import pygame
import renpy
import renpy.display
import renpy.display.pgrender as pgrender
# These need to be here before we mess with Pygame.
+2 -2
View File
@@ -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
import renpy.display
class Screen(renpy.object.Object):
"""
@@ -553,7 +553,7 @@ def get_widget(screen, id, layer='screens'):
screen = get_screen(screen, layer)
if not isinstance(screen, ScreenDisplayable):
return None
return None
if screen.child is None:
screen.update()
+1 -1
View File
@@ -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
import renpy.display
import pygame
import math
import weakref
+1 -1
View File
@@ -20,7 +20,7 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import re
import renpy
import renpy.display
import sys
try:
+2 -2
View File
@@ -24,7 +24,7 @@
# are None, at least to the point of making it through __init__. This is
# so that prediction of images works.
import renpy
import renpy.display
from renpy.display.render import render
@@ -211,7 +211,7 @@ def Fade(out_time,
if color:
widget = renpy.display.image.Solid(color)
if not widget:
widget = renpy.display.image.Solid((0, 0, 0, 255))
+1 -1
View File
@@ -21,7 +21,7 @@
# Functions that make the user's life easier.
import renpy
import renpy.display
def color(c):
if isinstance(c, tuple) and len(c) == 4:
+18 -25
View File
@@ -28,22 +28,11 @@ import renpy
class Delete(object):
pass
class PredictInfo(renpy.object.Object):
"""
This stores information involved in prediction.
Not used anymore, but needed for backwards compatibility.
"""
def __init__(self, pi=None):
super(PredictInfo, self).__init__()
if pi:
self.images = renpy.display.core.ImagePredictInfo(pi.images)
else:
self.images = renpy.display.core.ImagePredictInfo(None)
class Context(renpy.object.Object):
"""
This is the context object which stores the current context
@@ -67,11 +56,10 @@ class Context(renpy.object.Object):
does participates in rollback.
"""
__version__ = 5
__version__ = 6
def after_upgrade(self, version):
if version < 1:
self.predict_info = PredictInfo()
self.scene_lists.image_predict_info = self.predict_info.images
if version < 2:
@@ -87,6 +75,9 @@ class Context(renpy.object.Object):
if version < 5:
self.modes = renpy.python.RevertableList([ "start" ])
self.use_modes = True
if version < 6:
self.images = self.predict_info.images
def __init__(self, rollback, context=None, clear=False):
"""
@@ -138,13 +129,14 @@ class Context(renpy.object.Object):
for k, v in context.music.iteritems():
self.music[k] = v.copy()
self.predict_info = PredictInfo(context.predict_info)
self.images = renpy.display.image.ShownImageInfo(context.images)
else:
oldsl = None
self.predict_info = PredictInfo()
self.images = renpy.display.image.ShownImageInfo(None)
self.scene_lists = renpy.display.core.SceneLists(oldsl, self.predict_info.images)
self.scene_lists = renpy.display.core.SceneLists(oldsl, self.images)
self.make_dynamic([ "_return", "_args", "_kwargs", "mouse_visible", "suppress_overlay" ])
self.dynamic_stack.append({ })
@@ -342,9 +334,9 @@ class Context(renpy.object.Object):
if not self.current:
return
old_predict_info = self.predict_info
old_images = self.images
nodes = [ (renpy.game.script.lookup(self.current), self.predict_info) ]
nodes = [ (renpy.game.script.lookup(self.current), self.images) ]
node_set = set()
for i in range(0, renpy.config.predict_statements):
@@ -352,8 +344,9 @@ class Context(renpy.object.Object):
if i >= len(nodes):
break
node, predict_info = nodes[i]
self.predict_info = PredictInfo(predict_info)
node, images = nodes[i]
self.images = renpy.display.image.ShownImageInfo(images)
# Ignore exceptions in prediction, so long as
# prediction is not needed.
@@ -364,7 +357,7 @@ class Context(renpy.object.Object):
continue
if n not in node_set:
nodes.append((n, self.predict_info))
nodes.append((n, self.images))
node_set.add(n)
except:
@@ -376,8 +369,8 @@ class Context(renpy.object.Object):
print "While predicting images."
# We accept that sometimes prediction won't work.
self.predict_info = old_predict_info
self.images = old_images
def seen_current(self, ever):
+70 -13
View File
@@ -28,6 +28,9 @@
_file = file
import renpy
import renpy.display
import renpy.audio
from renpy.display.text import ParameterizedText
from renpy.display.font import register_sfont, register_mudgefont, register_bmfont
from renpy.display.behavior import Keymap
@@ -81,10 +84,6 @@ del public_api
import collections
# This is a map from image name to a Displayable object corresponding
# to that image name.
images = { }
def roll_forward_info():
return renpy.game.log.forward_info()
@@ -147,8 +146,7 @@ def image(name, img):
name = tuple(name.split())
img = renpy.easy.displayable(img)
images[name] = img
renpy.display.image.register_image(name, img)
def copy_images(old, new):
if not isinstance(old, tuple):
@@ -159,12 +157,12 @@ def copy_images(old, new):
lenold = len(old)
for k in list(images.keys()):
for k, v in renpy.display.image.images.items():
if len(k) < lenold:
continue
if k[:lenold] == old:
images[new + k[lenold:]] = images[k]
renpy.display.image.register_image(new + k[lenold:], v)
def showing(name, layer='master'):
"""
@@ -181,7 +179,7 @@ def showing(name, layer='master'):
if not isinstance(name, tuple):
name = tuple(name.split())
return renpy.game.context().predict_info.images.showing(layer, name)
return renpy.game.context().images.showing(layer, name)
def show(name, at_list=[ ], layer='master', what=None, zorder=0, tag=None, behind=[ ], atl=None, transient=False, munge_name=True):
"Documented in wiki as renpy.show."
@@ -206,7 +204,15 @@ def show(name, at_list=[ ], layer='master', what=None, zorder=0, tag=None, behin
if isinstance(what, renpy.display.core.Displayable):
base = img = what
else:
if renpy.config.image_attributes:
new_what = renpy.game.context().images.apply_attributes(layer, key, name)
if new_what is not None:
what = new_what
name = (key,) + new_what[1:]
base = img = renpy.display.image.ImageReference(what, style='image_placement')
if not base.find_target() and renpy.config.missing_show:
@@ -594,7 +600,7 @@ def predict_say(who, what):
"""
if who is None:
who = renpy.store.narrator # E1101
who = renpy.store.narrator # E1101 @UndefinedVariable
if isinstance(who, (str, unicode)):
return renpy.store.predict_say(who, what)
@@ -625,7 +631,7 @@ def say(who, what, interact=True):
what = what % tag_quoting_dict
if who is None:
who = renpy.store.narrator # E1101
who = renpy.store.narrator # E1101 @UndefinedVariable
if isinstance(who, (str, unicode)):
renpy.store.say(who, what, interact=interact)
@@ -1131,7 +1137,7 @@ def launch_editor(filenames, line=1, transient=0):
cmd = cmd.replace('""', '')
try:
subprocess.Popen(split_args(cmd)) # E1101
subprocess.Popen(split_args(cmd)) # E1101 @UndefinedVariable
return True
except:
if renpy.config.debug:
@@ -1258,7 +1264,7 @@ def free_memory():
force_full_redraw()
renpy.display.im.free_memory()
renpy.display.font.free_memory()
renpy.display.render.free_memory()
renpy.display.render.free_memory() # @UndefinedVariable
def easy_displayable(d, none=False):
if none:
@@ -1562,3 +1568,54 @@ def variant(name):
return name in renpy.config.variants
def vibrate(duration):
"""
:doc: other
Causes the device to vibrate for `duration` seconds. Currently, this
is only supported on Android.
"""
try:
import android #@UnresolvedImport
android.vibrate(duration)
except:
pass
# The attributes that are applied to the current say statement.
say_attributes = None
def get_say_attributes():
"""
:doc: other
Gets the attributes associated with the current say statement, or
None if no attributes are associated with this statement.
This is only valid when executing or predicting a say statement.
"""
return say_attributes
side_image_attributes = None
def get_side_image(tag):
"""
This attempts to find an image to show as the side image. It attempts to
find an image that begins with tag, and matches side_image_attributes. It
returns the name of the image (as a tuple of strings) if possible, or
None if that's not possible.
"""
if side_image_attributes is None:
return
images = renpy.game.context().images
required = set()
optional = set(side_image_attributes)
return images.choose_image(tag, required, optional, None)
+14 -3
View File
@@ -31,6 +31,7 @@
# in this module as fields on game.
import renpy
import renpy.display
# The basepath.
basepath = None
@@ -95,6 +96,9 @@ less_updates = False
# Should we never show the mouse?
less_mouse = False
# Should we not imagedissiolve?
less_imagedissolve = False
# The class that's used to hold the persistent data.
class Persistent(object):
@@ -109,7 +113,7 @@ class Persistent(object):
return None
# The persistent data that's kept from session to session
persistent = None
persistent = Persistent()
class Preferences(renpy.object.Object):
"""
@@ -171,7 +175,7 @@ class Preferences(renpy.object.Object):
return self.mute[mixer]
# The current preferences.
preferences = None
preferences = Preferences()
class RestartException(Exception):
"""
@@ -312,4 +316,11 @@ def call_in_new_context(label, *args, **kwargs):
if interface.restart_interaction:
contexts[-1].scene_lists.focused = None
# Type information.
if False:
script = renpy.script.Script()
interface = renpy.display.core.Interface()
log = renpy.python.RollbackLog()
+86 -13
View File
@@ -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
import renpy.display
import codecs
import time
import re
@@ -97,9 +97,51 @@ def try_compile(where, expr, additional=None):
add(additional)
# The sets of names + attributes that
imprecise_cache = set()
def image_exists_imprecise(name):
if name in imprecise_cache:
return True
nametag = name[0]
required = set()
banned = set()
for i in name[1:]:
if i[0] == "-":
banned.add(i[1:])
else:
required.add(i)
for im in renpy.display.image.images:
if im[0] != nametag:
continue
attrs = set(im[1:])
if [ i for i in required if i not in attrs ]:
continue
if [ i for i in banned if i in attrs ]:
continue
imprecise_cache.add(name)
return True
return False
# This reports an error if we're sure that the image with the given name
# does not exist.
def image_exists(name, expression, tag):
def image_exists(name, expression, tag, precise=True):
"""
Checks a scene or show statement for image existence.
"""
# Add the tag to the set of known tags.
tag = tag or name[0]
@@ -108,17 +150,26 @@ def image_exists(name, expression, tag):
if expression:
return
name = list(name)
names = " ".join(name)
namelist = list(name)
names = " ".join(namelist)
while name:
if tuple(name) in renpy.exports.images:
# Look for the precise name.
while namelist:
if tuple(namelist) in renpy.display.image.images:
return
name.pop()
namelist.pop()
# If we're not precise, then we have to start looking for images
# that we can possibly match.
if not precise and image_exists_imprecise(name):
return
report("The image named '%s' was not declared.", names)
# Only check each file once.
check_file_cache = { }
@@ -166,7 +217,7 @@ def check_image(node):
name = " ".join(node.imgname)
check_displayable('image %s' % name, renpy.exports.images[node.imgname])
check_displayable('image %s' % name, renpy.display.image.images[node.imgname])
def imspec(t):
if len(t) == 3:
@@ -178,7 +229,7 @@ def imspec(t):
# Lints ast.Show and ast.Scene nodets.
def check_show(node):
def check_show(node, precise):
# A Scene may have an empty imspec.
if not node.imspec:
@@ -189,7 +240,7 @@ def check_show(node):
if layer not in renpy.config.layers and layer not in renpy.config.top_layers:
report("Uses layer '%s', which is not in config.layers.", layer)
image_exists(name, expression, tag)
image_exists(name, expression, tag, precise=precise)
for i in at_list:
try_eval("the at list of a scene or show statment", i, "Perhaps you forgot to declare, or misspelled, a position?")
@@ -284,6 +335,28 @@ def check_say(node):
try_eval("the with clause of a say statement", node.with_, "Perhaps you forgot to declare, or misspelled, a transition?")
text_checks(node.what)
# Code to check image attributes. (If we're lucky.)
if node.who is None:
return
char = getattr(renpy.store, node.who, None)
if not isinstance(char, renpy.character.ADVCharacter):
return
if node.attributes is None:
return
if char.image_tag is None:
return
name = (char.image_tag,) + node.attributes
if not image_exists_imprecise(name):
report("Could not find image (%s) corresponding to attributes on say statement.", " ".join(name))
def check_menu(node):
@@ -406,7 +479,7 @@ def lint():
global image_prefixes
image_prefixes = { }
for k in renpy.exports.images:
for k in renpy.display.image.images:
image_prefixes[k[0]] = True
# Iterate through every statement in the program, processing
@@ -429,10 +502,10 @@ def lint():
check_image(node)
elif isinstance(node, renpy.ast.Show):
check_show(node)
check_show(node, True)
elif isinstance(node, renpy.ast.Scene):
check_show(node)
check_show(node, False)
elif isinstance(node, renpy.ast.Hide):
check_hide(node)
+1 -1
View File
@@ -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
import renpy.display
import renpy.game as game
import os
import sys
+21 -2
View File
@@ -1774,6 +1774,25 @@ def say_statement(l, loc):
# Try for a two-argument say statement.
who = l.simple_expression()
attributes = [ ]
while True:
prefix = l.match(r'-')
if not prefix:
prefix = ""
component = l.word()
if component is None:
break
attributes.append(prefix + component)
if attributes:
attributes = tuple(attributes)
else:
attributes = None
what = l.string()
if l.keyword('with'):
@@ -1785,7 +1804,7 @@ def say_statement(l, loc):
l.expect_eol()
l.expect_noblock('say statement')
l.advance()
return ast.Say(loc, who, what, with_)
return ast.Say(loc, who, what, with_, attributes=attributes)
# This reports a parse error for any bad statement.
l.error('expected statement.')
@@ -1898,7 +1917,7 @@ def report_parse_errors():
if renpy.config.editor:
renpy.exports.launch_editor([ 'errors.txt' ], 1, transient=1)
else:
os.startfile('errors.txt') # E1101
os.startfile('errors.txt') # E1101 @UndefinedVariable
except:
pass
+4 -4
View File
@@ -267,7 +267,7 @@ def mutator(method):
global mutate_flag
mutated = renpy.game.log.mutated
mutated = renpy.game.log.mutated #@UndefinedVariable
if id(self) not in mutated:
mutated[id(self)] = ( weakref.ref(self), self.get_rollback())
@@ -292,7 +292,7 @@ class RevertableList(list):
reverse = mutator(list.reverse)
sort = mutator(list.sort)
def wrapper(method): # E0213
def wrapper(method): # E0213 @NoSelf
def newmethod(*args, **kwargs):
return RevertableList(method(*args, **kwargs)) # E1102
@@ -325,7 +325,7 @@ class RevertableDict(dict):
popitem = mutator(dict.popitem)
setdefault = mutator(dict.setdefault)
def list_wrapper(method): # E0213
def list_wrapper(method): # E0213 @NoSelf
def newmethod(*args, **kwargs):
return RevertableList(method(*args, **kwargs)) # E1102
@@ -368,7 +368,7 @@ class RevertableSet(sets.Set):
union_update = mutator(sets.Set.union_update)
update = mutator(sets.Set.update)
def wrapper(method): # E0213
def wrapper(method): # E0213 @NoSelf
def newmethod(*args, **kwargs):
rv = method(*args, **kwargs) # E1102
if isinstance(rv, sets.Set):
+3
View File
@@ -478,6 +478,9 @@ position_properties = [ Style(i) for i in [
"clipping",
"xfill",
"yfill",
# no center, since it can conflict with the center transform.
"xcenter",
"ycenter",
] ]
text_properties = [ Style(i) for i in [
+1 -1
View File
@@ -103,7 +103,7 @@ class Script(object):
# A key that's used to lock the script file, should that
# prove necessary.
self.key = renpy.game.options.lock
self.key = renpy.game.options.lock #@UndefinedVariable
if self.key is None:
if os.path.exists(renpy.config.renpy_base + "/lock.txt"):
+6 -4
View File
@@ -28,6 +28,7 @@
# But please note that this will not be available in the body
# of user code, unless we re-import it.
import renpy
import renpy.display
import renpy.ui as ui
import renpy.display.im as im
@@ -121,10 +122,11 @@ Bar = renpy.display.behavior.Bar
Button = renpy.display.behavior.Button
Input = renpy.display.behavior.Input
Frame = renpy.display.image.Frame
Image = renpy.display.image.Image
ImageReference = renpy.display.image.ImageReference
Solid = renpy.display.image.Solid
Image = renpy.display.im.image
Frame = renpy.display.imagelike.Frame
Solid = renpy.display.imagelike.Solid
LiveComposite = renpy.display.layout.LiveComposite
LiveCrop = renpy.display.layout.LiveCrop
@@ -318,7 +320,7 @@ adv = ADVCharacter(None,
condition=None,
dynamic=False,
image=False,
image=None,
interact=True,
slow=True,
+14 -1
View File
@@ -201,6 +201,8 @@ def always_true(a):
return True
def always_0(a):
return 0
def always_half(a):
return 0.5
substitutes = dict(
xmargin = [
@@ -292,7 +294,18 @@ substitutes = dict(
('ymaximum', index_3),
('xminimum', index_2),
('yminimum', index_3),
],
],
xcenter = [
('xpos', None),
('xanchor', always_half),
],
ycenter = [
('ypos', None),
('yanchor', always_half),
],
)
# Map from property to number.
+1 -1
View File
@@ -695,7 +695,7 @@ def _imagebutton(idle_image = None,
selected_hover = choice(selected_hover, selected_hover_image, "selected_hover")
selected_insensitive = choice(selected_insensitive, selected_insensitive_image, "selected_insensitive")
return renpy.display.image.ImageButton(
return renpy.display.behavior.ImageButton(
idle,
hover,
insensitive_image = insensitive,
+16
View File
@@ -672,6 +672,22 @@ positions.
Equivalent to setting ypos and yanchor to this value.
.. transform-property:: xcenter
:type: float
:default: 0.0
Equivalent to setting xpos to the value of this property, and
xanchor to 0.5.
.. transform-property:: ycenter
:type: float
:default: 0.0
Equivalent to setting ypos to the value of this property, and
yanchor to 0.5.
.. transform-property:: rotate
:type: float or None
+86
View File
@@ -2,6 +2,92 @@
Full Changelog
==============
Ren'Py 6.12.1
=============
Image Attributes
----------------
The process of showing images is now attribute-based. Image names now
consist of a tag, and zero or more attributes. When showing an image,
the order of attributes is no longer important - it's now possible to
define an image using one set of attributes, and show it using those
attributes in a different order.
Attributes are also "sticky". This means that we attempt to preserve
as many attributes as possible when showing a new image.
For example, say we had the following images::
image eileen beach happy = "eileen_beach_happy.png"
image eileen beach woozy = "eileen_beach_woozy.png"
We can now show the first image using the command::
show eileen happy beach
Since the order of attributes no longer matters, this will show the
"eileen beach happy" image. If we follow this with the show statement::
show eileen woozy
the image "eileen beach woozy" will be shown. (Assuming no other
images exist. If the image "eileen happy woozy" existed, an ambiguity
error would occur.)
When an image tag is shown without any attributes, then the current
attributes are retained. Now, one can write::
show eileen at right
to display Eileen on the right side of the screen, without changing
the attributes supplied to an image.
** Say Attributes. **
Image attributes can be updated as part of a say statement. A
character can be given an `image` argument, giving the name of an
image that character is linked to. As part of the say statement, image
attributes can be given before the dialogue string. These attributes
are given to the linked image.
For example, if we define a character using the code::
define e = Character('Eileen', image="eileen")
the code::
e woozy "I think I'm getting too much sun."
is equivalent to::
show eileen woozy
e "I think I'm getting too much sun."
whenever an image with the tag eileen is being shown.
** Side Image. **
This release features a new implementation of :ref:`Side Images`, which
allows side images to be defined like other images, and allows side
images to be integrated with screens easily.
** Sticky Transforms. **
Finally, showing an image without providing a transform or ATL block
will now continue the previous transform that an image with that tag
was using. Previously, it caused those transforms to stop.
Other
-----
Added the :propref:`xcenter` and :propref:`ycenter` position and
transform properties. These set the position of the center of a
displayable. [Test]
The :func:`renpy.vibrate` function allows Ren'Py to ask Android devices
to vibrate.
Ren'Py 6.12.0
=============
+7
View File
@@ -684,6 +684,13 @@ Rarely or Internally Used
If True, joystic support is enabled.
.. var:: config.keep_running_transform = True
If true, showing an image without supplying a transform or ATL
block will cause the image to continue the previous transform
an image with that tag was using, if any. If false, the transform
is stopped.
.. var:: config.keymap = dict(...)
This variable contains a keymap giving the keys and mouse buttons
+1 -1
View File
@@ -190,7 +190,7 @@ code::
first desaturates the image, and then tints it blue. When the
intermediate image is not needed, multiplying matrices is far
more efficent, in both time and image cache space, than using
more efficient, in both time and image cache space, than using
two im.MatrixColors.
.. include:: inc/im_matrixcolor
+11 -6
View File
@@ -19,6 +19,17 @@
be used to define a template character, and then copy that
character with changes.
**Linked Image**
An image tag may be associated with a Character. This allows a
say statement involving this character to display an image with
the tag, and also allows Ren'Py to automatically select a side
image to show when this character speaks.
`image`
A string giving the image tag that is linked with this
character.
**Prefixes and Suffixes.**
These allow a prefix and suffix to be applied to the name of the
character, and to the text being shown. This can be used, for
@@ -53,12 +64,6 @@
expression. That string will be evaluated before each line
of dialogue, and the result used as the name of the character.
`image`
If true, then `name` is expected to name an image file. That
image is used as the name of the character.
**Controlling Interactions.**
These options control if the dialogue is displayed, if an
interaction occurs, and the mode that is entered upon display.
+1 -1
View File
@@ -1,6 +1,6 @@
.. Automatically generated file - do not modify.
.. class:: Drag(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=<function default_drag_joined at 0x1de72a8>, clicked=None, hovered=None, unhovered=None, replaces=None, **properties)
.. class:: Drag(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
+12
View File
@@ -1011,6 +1011,13 @@
be treated as immutable. This should only be called once the display
has been started (that is, after the init code is finished).
.. function:: renpy.get_say_attributes()
Gets the attributes associated with the current say statement, or
None if no attributes are associated with this statement.
This is only valid when executing or predicting a say statement.
.. function:: renpy.image_size(im)
Given an image manipulator, loads it and returns a (``width``,
@@ -1040,6 +1047,11 @@
Only one notification is displayed at a time. If a second notification
is displayed, the first notification is replaced.
.. function:: renpy.vibrate(duration)
Causes the device to vibrate for `duration` seconds. Currently, this
is only supported on Android.
.. function:: renpy.music.register_channel(name, mixer=None, loop=None, stop_on_mute=True, tight=False, file_prefix='', file_suffix='', buffer_queue=True)
This registers a new audio channel named `name`. Audio can then be
+21
View File
@@ -9,6 +9,27 @@ Note that setting :var:`config.script_version` will cause many of
these changes to be reverted, at the cost of losing access to recent
features.
.. _incompatible-6.12.1:
6.12.1
------
Image names have changed from being static names to being
attribute-based. This can lead to image names that were previously
distinct becoming ambiguous. To disable attribute-based image names,
set :var:`config.image_attributes` to False.
Showing an image without providing a transform or ATL block will now
continue the previous transform that the image was using. This means
that a moving image may continue moving once it has changed. To revert
to the old behavior, set :var:`config.keep_running_transform` to False.
The `image` argument to :func:`Character` has changed meaning. While
the old meaning was unsupported in the screens-based environment, it
can be restored for compatibility purposes by setting
:var:`new_character_image_argument` to False.
.. _incompatible-6.12.0:
6.12.0
+1
View File
@@ -50,6 +50,7 @@ Customizing Ren'Py
screens
screen_actions
screen_special
side_image
config
Advanced Displayables
File diff suppressed because one or more lines are too long
+5
View File
@@ -105,4 +105,9 @@ same default page as the file actions.
.. include:: inc/file_action_function
Side Image Functions
--------------------
This function returns the side image to use.
.. include:: inc/side_image_function
+98
View File
@@ -0,0 +1,98 @@
Side Images
===========
Many visual novels include a picture of the character that is speaking as
part of their interface. Ren'Py calls this image a side image, and has
support for automatically selecting and displaying a side image as part
of the dialogue.
The side image support assumes that a :func:`Character` is declared with
a linked image tag::
define e = Character("Eileen", image="eileen")
When a character with a linked image tag speaks, Ren'Py creates a pool of
image attributes. The linked image tag is added to this pool, as are the
current image attributes that are associated with that tag.
To determine the side image associated with a tag, Ren'Py tries to find
an image with the tag "side", and the largest number of attributes from
the pool. If no image can be found, or more than one image has the same
number of attributes, an :class:`Null` is shown instead.
For example, say we have the following script::
define e = Character("Eileen", image="eileen")
image eileen happy = "eileen_happy.png"
image eileen concerned = "eileen_concerned.png"
image side eileen happy = "side_eileen_happy.png"
image side eileen = "side_eileen.png"
label start:
show eileen happy
e "Let's call this line Point A."
e concerned "And this one is point B."
At point A, the character ``e`` is speaking, which is linked to the image
tag "eileen". The "eileen happy" image is showing, so the pool of attributes
is "eileen" and "happy". We look for an image with the "side" tag, and as
many of those attributes as possible - and we match "side eileen happy",
which is the side image Ren'Py will display.
At point B, the "eileen concerned" image is showing. The pool of attributes
is now "eileen" and "concerned". The only matching image is "side eileen",
so that's what Ren'Py selects. If the was a "side concerned" image, there
would be ambiguity, and Ren'Py wouldn't display an image.
Invisible Characters
--------------------
Another use of the side image is to show an image of the player character,
when that character has dialogue. The way to do this is to link an image to
the character, and then use the say with attributes construct to select
the side image to show.
For example::
define p = Character("Player", image="player")
image side player happy = "side_player_happy.png"
image side player concerned = "side_player_concerned.png"
label start:
p happy "This is shown with the 'side player happy' image."
p "This is also shown with 'side player happy'."
p concerned "This is shown with 'side player concerned'."
Leaving Room / Customization
----------------------------
By default, the entire width of the screen is taken up by the text. If one
tries to display a side image, it will be displayed on top of the text. To
fix this, one should include margin or padding on the appropriate side of
the text window, using code like::
init python:
style.window.padding_left = 150
The position of the side image can be changed by customizing the ``say``
or ``nvl`` screens. Both include the line::
add SideImage() xalign 0.0 yalign 1.0
By changing the xalign and yalign properties, you can control the positioning
of the side image on the screen.
Finally, the :func:`SideImage` function returns, as a displayable, the
current side image. This can be used as part of more advanced screen
customization.
+10
View File
@@ -238,6 +238,16 @@ layout.
Equivalent to setting xalign to the first component of the tuple,
and yalign to the second.
.. style-property:: xcenter position
Equivalent to setting xpos to the value of this property, and
xanchor to 0.5.
.. style-property:: ycenter position
Equivalent to setting ypos to the value of tihis property, and
yanchor to 0.5.
.. style-property:: xoffset int
Gives a number of pixels that are added to the horizontal position
+1 -1
View File
@@ -1,3 +1,3 @@
#!/bin/sh
rsync -a /home/tom/ab/renpy-mainline/doc/ tom@onegeek.org:/home/tom/WWW.renpyorg/doc/html/
rsync -a ../doc/ tom@onegeek.org:/home/tom/WWW.renpyorg/doc/html/
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
rsync -a ../doc/ tom@onegeek.org:/home/tom/WWW.renpyorg/dev-doc/html/
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -271,7 +271,7 @@ image main_menu:
size (800, 509)
contains:
Text("Ren'Py 6.12.0 \"Positronic Brain\"", size=18)
Text("Ren'Py 6.12.1 \"No Name Yet\"", size=18)
yalign .98
xalign .02
+4 -3
View File
@@ -48,11 +48,12 @@ screen say:
style "say_vbox"
text what id "what"
# If there's a side image, display it above the text.
if side_image:
add side_image
else:
add SideImage() xalign 0.0 yalign 1.0
##############################################################################
@@ -158,7 +159,7 @@ screen nvl:
text caption style "nvl_dialogue"
add SideImage() xalign 0.0 yalign 1.0
##############################################################################
# Main Menu
View File
View File
+332
View File
@@ -0,0 +1,332 @@
# Version of renpy.display.render for use in python-only type inferencing.
raise NotImplemented()
import renpy.display
def free_memory():
"""
Frees memory used by the render system.
"""
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.
"""
def render(d, widtho, heighto, st, at):
"""
Causes the displayable `d` to be rendered in an area of size
width, height. st and at are the times of this render, but once
rendered the Render will remain cached until the displayable needs
to be redrawn.
"""
return Render(0, 0)
def invalidate(d):
"""
Removes d from the render cache. If we're not in a redraw, triggers
a redraw to start.
"""
def process_redraws():
"""
Called to determine if any redraws are pending. Returns true if we
need to redraw the screen now, false otherwise.
"""
return True
def redraw_time():
"""
Returns the time at which the next redraw is scheduled.
"""
return 0.0
def redraw(d, when):
"""
Called to cause `d` to be redrawn in `when` seconds.
"""
class Matrix2D:
"""
This represents a 2d matrix that can be used to transform
points and things like that.
"""
def __init__(self, xdx, xdy, ydx, ydy):
self.xdx = xdx
self.xdy = xdy
self.ydx = ydx
self.ydy = ydy
def transform(self, x, y):
return (x * self.xdx + y * self.xdy), (x * self.ydx + y * self.ydy)
def __mul__(self, 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.
"""
# 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.
"""
return renpy.display.focus.Focus(None, None, None, None, None, None)
def mutated_surface(surf):
"""
Called to indicate that the given surface has changed.
"""
def render_screen(root, width, height):
"""
Renders `root` (a displayable) as the root of a screen with the given
`width` and `height`.
"""
return Render(0, 0)
def mark_sweep():
"""
This performs mark-and-sweep garbage collection on the live_renders
list.
"""
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)
"""
# Possible operations that can be done as part of a render.
BLIT = 0
DISSOLVE = 1
IMAGEDISSOLVE = 2
PIXELLATE = 3
class Render:
def __init__(self, width, height, draw_func=None, layer_name=None, opaque=None):
"""
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.
"""
def blit(self, source, pos, focus=True, main=True, 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.
"""
def subpixel_blit(self, source, pos, focus=True, main=True, 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.
"""
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 0, 0
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.
"""
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.
"""
return Render(0, 0)
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.
"""
def kill_cache(self):
"""
Removes this render and its transitive parents from the cache.
"""
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.
"""
def take_focuses(self, cminx, cminy, cmaxx, cmaxy, reverse, x, y, focuses):
"""
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.
"""
def focus_at_point(self, x, y):
"""
This returns the focus of this object at the given point.
"""
return renpy.display.layout.Null()
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`.
"""
return [ ]
def is_opaque(self):
"""
Returns true if this displayable is opaque, or False otherwise.
Also sets self.visible_children.
"""
return True
def is_pixel_opaque(self, x, y):
"""
Determine if the pixel at x and y is opaque or not.
"""
return True
def fill(self, color):
"""
Fills this Render with the given color.
"""
def canvas(self):
"""
Returns a canvas object that draws to this Render.
"""
return Canvas(None)
class Canvas(object):
def __init__(self, surf):
self.surf = surf
def rect(self, color, rect, width=0):
return
def polygon(self, color, pointlist, width=0):
return
def circle(self, color, pos, radius, width=0):
return
def ellipse(self, color, rect, width=0):
return
def arc(self, color, rect, start_angle, stop_angle, width=1):
return
def line(self, color, start_pos, end_pos, width=1):
return
def lines(self, color, closed, pointlist, width=1):
return
def aaline(self, color, startpos, endpos, blend=1):
return
def aalines(self, color, closed, pointlist, blend=1):
return