summaryrefslogtreecommitdiff
path: root/ast2json/renpy/ast.py
diff options
context:
space:
mode:
Diffstat (limited to 'ast2json/renpy/ast.py')
-rw-r--r--ast2json/renpy/ast.py1757
1 files changed, 1757 insertions, 0 deletions
diff --git a/ast2json/renpy/ast.py b/ast2json/renpy/ast.py
new file mode 100644
index 0000000..6fbd58f
--- /dev/null
+++ b/ast2json/renpy/ast.py
@@ -0,0 +1,1757 @@
+# 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 AST for the Ren'Py script language. Each class
+# here corresponds to a statement in the script language.
+
+# NOTE:
+# When updating this file, consider if lint.py or warp.py also need
+# updating.
+
+import renpy
+
+import re
+import time
+import hashlib
+import collections
+
+def next_node(n):
+ """
+ Indicates the next node that should be executed. When a statement
+ can crash, this should be set as early as possible, so that ignore
+ can bring us there.
+ """
+
+ renpy.game.context().next_node = n
+
+class ParameterInfo(object):
+ """
+ This class is used to store information about parameters to a
+ label.
+ """
+ def __init__(self, parameters, positional, extrapos, extrakw):
+
+ # A list of parameter name, default value pairs.
+ self.parameters = parameters
+
+ # A list, giving the positional parameters to this function,
+ # in order.
+ self.positional = positional
+
+ # A variable that takes the extra positional arguments, if
+ # any. None if no such variable exists.
+ self.extrapos = extrapos
+
+ # A variable that takes the extra keyword arguments, if
+ # any. None if no such variable exists.
+ self.extrakw = extrakw
+
+class ArgumentInfo(object):
+
+ def __init__(self, arguments, extrapos, extrakw):
+
+ # A list of (keyword, expression) pairs. If an argument doesn't
+ # have a keyword, it's thought of as positional.
+ self.arguments = arguments
+
+ # An expression giving extra positional arguments being
+ # supplied to this function.
+ self.extrapos = extrapos
+
+ # An expression giving extra keyword arguments that need
+ # to be supplied to this function.
+ self.extrakw = extrakw
+
+
+def __newobj__(cls, *args):
+ return cls.__new__(cls, *args)
+
+# This represents a string containing python code.
+class PyExpr(str):
+
+ __slots__ = [
+ 'filename',
+ 'linenumber',
+ ]
+
+ def __new__(cls, s, filename, linenumber):
+ self = str.__new__(cls, s)
+ self.filename = filename
+ self.linenumber = linenumber
+ return self
+
+ def __getnewargs__(self):
+ return (str(self), self.filename, self.linenumber) # E1101
+
+class PyCode(object):
+
+ __slots__ = [
+ 'source',
+ 'location',
+ 'mode',
+ 'bytecode',
+ 'hash',
+ ]
+
+ def __getstate__(self):
+ return (1, self.source, self.location, self.mode)
+
+ def __setstate__(self, state):
+ (_, self.source, self.location, self.mode) = state
+ self.bytecode = None
+
+ if renpy.game.script.record_pycode:
+ renpy.game.script.all_pycode.append(self)
+
+ def __init__(self, source, loc=('<none>', 1), mode='exec'):
+
+ if isinstance(source, PyExpr):
+ loc = (source.filename, source.linenumber, source)
+
+ # The source code.
+ self.source = source
+
+ # The time is necessary so we can disambiguate between Python
+ # blocks on the same line in different script versions.
+ self.location = loc + ( int(time.time()), )
+ self.mode = mode
+
+ # This will be initialized later on, after we are serialized.
+ self.bytecode = None
+
+ if renpy.game.script.record_pycode:
+ renpy.game.script.all_pycode.append(self)
+
+ self.hash = None
+
+ def get_hash(self):
+ try:
+ if self.hash is not None:
+ return self.hash
+ except:
+ pass
+
+ code = self.source
+ if isinstance(code, renpy.python.ast.AST): #@UndefinedVariable
+ code = renpy.python.ast.dump(code) #@UndefinedVariable
+
+ self.hash = chr(renpy.bytecode_version) + hashlib.md5(repr(self.location) + code.encode("utf-8")).digest()
+ return self.hash
+
+
+def chain_block(block, next): #@ReservedAssignment
+ """
+ This is called to chain together all of the nodes in a block. Node
+ n is chained with node n+1, while the last node is chained with
+ next.
+ """
+
+ if not block:
+ return
+
+ for a, b in zip(block, block[1:]):
+ a.chain(b)
+
+ block[-1].chain(next)
+
+
+class Scry(object):
+ """
+ This is used to store information about the future, if we know it. Unlike
+ predict, this tries to only get things we _know_ will happen.
+ """
+
+ # By default, all attributes are None.
+ def __getattr__(self, name):
+ return None
+
+ def __next__(self): #@ReservedAssignment
+ if self._next is None:
+ return None
+ else:
+ return self._next.scry()
+
+
+class Node(object):
+ """
+ A node in the abstract syntax tree of the program.
+
+ @ivar name: The name of this node.
+
+ @ivar filename: The filename where this node comes from.
+ @ivar linenumber: The line number of the line on which this node is defined.
+ """
+
+ __slots__ = [
+ 'name',
+ 'filename',
+ 'linenumber',
+ 'next',
+ ]
+
+ # True if this node is translatable, false otherwise. (This can be set on
+ # the class or the instance.)
+ translatable = False
+
+ # Called to set the state of a Node, when necessary.
+ def __setstate__(self, state):
+ for k, v in state[1].items():
+ try:
+ setattr(self, k, v)
+ except AttributeError:
+ pass
+
+
+ def __init__(self, loc):
+ """
+ Initializes this Node object.
+
+ @param loc: A (filename, physical line number) tuple giving the
+ logical line on which this Node node starts.
+ """
+
+ self.filename, self.linenumber = loc
+ self.name = None
+ self.next = None
+
+ def diff_info(self):
+ """
+ Returns a tuple of diff info about ourself. This is used to
+ compare Nodes to see if they should be considered the same node. The
+ tuple returned must be hashable.
+ """
+
+ return ( id(self), )
+
+ def get_children(self):
+ """
+ Returns a list of all of the nodes that are children of this
+ node. (That is, all of the nodes in any block associated with
+ this node.)
+ """
+
+ return [ ]
+
+ def get_init(self):
+ """
+ Returns a node that should be run at init time (that is, before
+ the normal start of the script.), or None if this node doesn't
+ care to suggest one.
+
+ (The only class that needs to override this is Init.)
+ """
+
+ return None
+
+ def chain(self, next): #@ReservedAssignment
+ """
+ This is called with the Node node that should be followed after
+ executing this node, and all nodes that this node
+ executes. (For example, if this node is a block label, the
+ next is the node that should be executed after all nodes in
+ the block.)
+ """
+
+ self.next = next
+
+ def execute(self):
+ """
+ Causes this node to execute, and any action it entails to be
+ performed. The node should call next_node with the node to
+ be executed after this one.
+ """
+
+ assert False, "Node subclass forgot to define execute."
+
+ def early_execute(self):
+ """
+ Called when the module is loaded.
+ """
+
+ def predict(self):
+ """
+ This is called to predictively load images from this node. It
+ should cause renpy.display.predict.image and
+ renpy.display.predict.screen to be called as necessary.
+ """
+
+ if self.__next__:
+ return [ self.__next__ ]
+ else:
+ return [ ]
+
+ def scry(self):
+ """
+ Called to return an object with some general, user-definable information
+ about the future.
+ """
+
+ rv = Scry()
+ rv._next = self.__next__ # W0201
+ return rv
+
+ def restructure(self, callback):
+ """
+ Called to restructure the AST.
+
+ When this method is called, callback is called once for each child
+ block of the node. The block, a list, can be updated by the callback
+ using slice assignment to the list.
+ """
+
+ # Does nothing for nodes that do not contain child blocks.
+ return
+
+ def get_code(self, dialogue_filter=None):
+ """
+ Returns the canonical form of the code corresponding to this statement.
+ This only needs to be defined if the statement is translatable.
+
+ `filter`
+ If present, a filter that should be applied to human-readable
+ text in the statement.
+ """
+
+ raise Exception("Not Implemented")
+
+def say_menu_with(expression, callback):
+ """
+ This handles the with clause of a say or menu statement.
+ """
+
+ if expression is not None:
+ what = renpy.python.py_eval(expression)
+ elif renpy.store.default_transition and renpy.game.preferences.transitions == 2:
+ what = renpy.store.default_transition
+ else:
+ return
+
+ if not what:
+ return
+
+ if renpy.game.preferences.transitions:
+ # renpy.game.interface.set_transition(what)
+ callback(what)
+
+class Say(Node):
+
+ __slots__ = [
+ 'who',
+ 'who_fast',
+ 'what',
+ 'with_',
+ 'interact',
+ 'attributes',
+ ]
+
+ def diff_info(self):
+ return (Say, self.who, self.what)
+
+ def __setstate__(self, state):
+ self.attributes = None
+ self.interact = True
+ Node.__setstate__(self, state)
+
+ def __init__(self, loc, who, what, with_, interact=True, attributes=None):
+
+ super(Say, self).__init__(loc)
+
+ if who is not None:
+ self.who = who.strip()
+
+ if re.match(r'[a-zA-Z_]\w*$', self.who):
+ self.who_fast = True
+ else:
+ self.who_fast = False
+ else:
+ self.who = None
+ self.who_fast = False
+
+ self.what = what
+ 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 get_code(self, dialogue_filter=None):
+ rv = [ ]
+
+ if self.who:
+ rv.append(self.who)
+
+ if self.attributes is not None:
+ rv.extend(self.attributes)
+
+ what = self.what
+ if dialogue_filter is not None:
+ what = dialogue_filter(what)
+
+ rv.append(renpy.translation.encode_say_string(what))
+
+ if not self.interact:
+ rv.append("nointeract")
+
+ if self.with_:
+ rv.append("with")
+ rv.append(self.with_)
+
+ return " ".join(rv)
+
+ def execute(self):
+
+ next_node(self.__next__)
+
+ try:
+
+ renpy.exports.say_attributes = self.attributes
+
+ if self.who is not None:
+ if self.who_fast:
+ who = getattr(renpy.store, self.who, None)
+ if who is None:
+ raise Exception("Sayer '%s' is not defined." % self.who.encode("utf-8"))
+ else:
+ who = renpy.python.py_eval(self.who)
+ else:
+ who = None
+
+ if not (
+ (who is None) or
+ isinstance(who, collections.Callable) or
+ isinstance(who, str) ):
+
+ raise Exception("Sayer %s is not a function or string." % self.who.encode("utf-8"))
+
+ what = self.what
+ if renpy.config.say_menu_text_filter:
+ what = renpy.config.say_menu_text_filter(what) # E1102
+
+ if getattr(who, "record_say", True):
+ renpy.store._last_say_who = self.who
+ renpy.store._last_say_what = what
+
+ say_menu_with(self.with_, renpy.game.interface.set_transition)
+ renpy.exports.say(who, what, interact=self.interact)
+
+ finally:
+ renpy.exports.say_attributes = None
+
+
+ def predict(self):
+
+ old_attributes = renpy.exports.say_attributes
+
+ try:
+
+ renpy.exports.say_attributes = self.attributes
+
+ if self.who is not None:
+ if self.who_fast:
+ who = getattr(renpy.store, self.who)
+ else:
+ who = renpy.python.py_eval(self.who)
+ else:
+ who = None
+
+ def predict_with(trans):
+ renpy.display.predict.displayable(trans(old_widget=None, new_widget=None))
+
+ say_menu_with(self.with_, predict_with)
+
+ what = self.what
+ if renpy.config.say_menu_text_filter:
+ what = renpy.config.say_menu_text_filter(what)
+
+ renpy.exports.predict_say(who, what)
+
+ finally:
+ renpy.exports.say_attributes = old_attributes
+
+ return [ self.__next__ ]
+
+ def scry(self):
+ rv = Node.scry(self)
+
+ if self.who is not None:
+ if self.who_fast:
+ who = getattr(renpy.store, self.who)
+ else:
+ who = renpy.python.py_eval(self.who)
+ else:
+ who = None
+
+ if self.interact:
+ renpy.exports.scry_say(who, rv)
+ else:
+ rv.interacts = False
+
+ return rv
+
+# Copy the descriptor.
+setattr(Say, "with", Say.with_) # E1101
+
+class Init(Node):
+
+ __slots__ = [
+ 'block',
+ 'priority',
+ ]
+
+ def __init__(self, loc, block, priority):
+ super(Init, self).__init__(loc)
+
+ self.block = block
+ self.priority = priority
+
+
+ def get_children(self):
+ return self.block
+
+ def get_init(self):
+ return self.priority, self.block[0]
+
+ # We handle chaining specially. We want to chain together the nodes in
+ # the block, but we want that chain to end in None, and we also want
+ # this node to just continue on to the next node in normal execution.
+ def chain(self, next): #@ReservedAssignment
+ self.next = next
+
+ chain_block(self.block, None)
+
+ def execute(self):
+ next_node(self.__next__)
+
+ def restructure(self, callback):
+ callback(self.block)
+
+def apply_arguments(params, args, kwargs):
+ """
+ Applies arguments to parameters to update scope.
+
+ `scope`
+ A dict.
+
+ `params`
+ The parameters object.
+
+ `args`, `kwargs`
+ Positional and keyword arguments.
+ """
+
+ values = { }
+ rv = { }
+
+ if args is None:
+ args = ()
+
+ if kwargs is None:
+ kwargs = { }
+
+ if params is None:
+ if args or kwargs:
+ raise Exception("Arguments supplied, but parameter list not present")
+ else:
+ return rv
+
+ for name, value in zip(params.positional, args):
+ if name in values:
+ raise Exception("Parameter %s has two values." % name)
+
+ values[name] = value
+
+ extrapos = tuple(args[len(params.positional):])
+
+ for name, value in kwargs.items():
+ if name in values:
+ raise Exception("Parameter %s has two values." % name)
+
+ values[name] = value
+
+ for name, default in params.parameters:
+
+ if name not in values:
+ if default is None:
+ raise Exception("Required parameter %s has no value." % name)
+ else:
+ rv[name] = renpy.python.py_eval(default)
+
+ else:
+ rv[name] = values[name]
+ del values[name]
+
+ # Now, values has the left-over keyword arguments, and extrapos
+ # has the left-over positional arguments.
+
+ if params.extrapos:
+ rv[params.extrapos] = extrapos
+ elif extrapos:
+ raise Exception("Too many arguments in call (expected %d, got %d)." % (len(params.positional), len(args)))
+
+ if params.extrakw:
+ rv[params.extrakw] = values
+ else:
+ if values:
+ raise Exception("Unknown keyword arguments: %s" % ( ", ".join(list(values.keys()))))
+
+ return rv
+
+class Label(Node):
+
+ __slots__ = [
+ 'name',
+ 'parameters',
+ 'block',
+ 'hide',
+ ]
+
+ def __setstate__(self, state):
+ self.parameters = None
+ self.hide = False
+ Node.__setstate__(self, state)
+
+ def __init__(self, loc, name, block, parameters, hide=False):
+ """
+ Constructs a new Label node.
+
+ @param name: The name of this label.
+ @param block: A (potentially empty) list of nodes making up the
+ block associated with this label.
+ """
+
+ super(Label, self).__init__(loc)
+
+ self.name = name
+ self.block = block
+ self.parameters = parameters
+ self.hide = hide
+
+ def diff_info(self):
+ return (Label, self.name)
+
+ def get_children(self):
+ return self.block
+
+ def chain(self, next): #@ReservedAssignment
+
+ if self.block:
+ self.next = self.block[0]
+ chain_block(self.block, next)
+ else:
+ self.next = next
+
+ def execute(self):
+ next_node(self.__next__)
+
+ renpy.game.context().mark_seen()
+
+ values = apply_arguments(self.parameters, renpy.store._args, renpy.store._kwargs)
+
+ for k, v in values.items():
+ renpy.exports.dynamic(k)
+ setattr(renpy.store, k, v)
+
+ renpy.store._args = None
+ renpy.store._kwargs = None
+
+ if renpy.config.label_callback:
+ renpy.config.label_callback(self.name, renpy.game.context().last_abnormal)
+
+ def restructure(self, callback):
+ callback(self.block)
+
+
+class Python(Node):
+
+ __slots__ = [
+ 'hide',
+ 'code',
+ 'store',
+ ]
+
+ def __setstate__(self, state):
+ self.store = "store"
+ super(Python, self).__setstate__(state)
+
+ def __init__(self, loc, python_code, hide=False, store="store"):
+ """
+ @param code: A PyCode object.
+
+ @param hide: If True, the code will be executed with its
+ own local dictionary.
+ """
+
+ super(Python, self).__init__(loc)
+
+ self.hide = hide
+ self.code = PyCode(python_code, loc=loc, mode='exec')
+ self.store = store
+
+ def diff_info(self):
+ return (Python, self.code.source)
+
+ def early_execute(self):
+ renpy.python.create_store(self.store)
+
+ def execute(self):
+ next_node(self.__next__)
+
+ try:
+ renpy.python.py_exec_bytecode(self.code.bytecode, self.hide, store=self.store)
+ finally:
+
+ if not renpy.game.context().init_phase:
+ for i in renpy.config.python_callbacks:
+ i()
+
+ def scry(self):
+ rv = Node.scry(self)
+ rv.interacts = True
+ return rv
+
+class EarlyPython(Node):
+
+ __slots__ = [
+ 'hide',
+ 'code',
+ 'store',
+ ]
+
+ def __setstate__(self, state):
+ self.store = "store"
+ super(EarlyPython, self).__setstate__(state)
+
+ def __init__(self, loc, python_code, hide=False, store="store"):
+ """
+ @param code: A PyCode object.
+
+ @param hide: If True, the code will be executed with its
+ own local dictionary.
+ """
+
+ super(EarlyPython, self).__init__(loc)
+
+ self.hide = hide
+ self.code = PyCode(python_code, loc=loc, mode='exec')
+ self.store = store
+
+ def diff_info(self):
+ return (EarlyPython, self.code.source)
+
+ def execute(self):
+ next_node(self.__next__)
+
+ def early_execute(self):
+ renpy.python.create_store(self.store)
+
+ if self.code.bytecode:
+ renpy.python.py_exec_bytecode(self.code.bytecode, self.hide, store=self.store)
+
+class Image(Node):
+
+ __slots__ = [
+ 'imgname',
+ 'code',
+ 'atl',
+ ]
+
+ def __init__(self, loc, name, expr=None, atl=None):
+ """
+ @param name: The name of the image being defined.
+
+ @param expr: An expression yielding a Displayable that is
+ assigned to the image.
+ """
+
+ super(Image, self).__init__(loc)
+
+ self.imgname = name
+
+ if expr:
+ self.code = PyCode(expr, loc=loc, mode='eval')
+ self.atl = None
+ else:
+ self.code = None
+ self.atl = atl
+
+ def diff_info(self):
+ return (Image, tuple(self.imgname))
+
+ def execute(self):
+
+ # Note: We should always check that self.code is None before
+ # accessing self.atl, as self.atl may not always exist.
+
+ next_node(self.__next__)
+
+ if self.code is not None:
+ img = renpy.python.py_eval_bytecode(self.code.bytecode)
+ else:
+ img = renpy.display.motion.ATLTransform(self.atl)
+
+ renpy.exports.image(self.imgname, img)
+
+
+
+class Transform(Node):
+
+ __slots__ = [
+
+ # The name of the transform.
+ 'varname',
+
+ # The block of ATL associated with the transform.
+ 'atl',
+
+ # The parameters associated with the transform, if any.
+ 'parameters',
+ ]
+
+ default_parameters = ParameterInfo([ ], [ ], None, None)
+
+ def __init__(self, loc, name, atl=None, parameters=default_parameters):
+
+ super(Transform, self).__init__(loc)
+
+ self.varname = name
+ self.atl = atl
+ self.parameters = parameters
+
+ def diff_info(self):
+ return (Transform, self.varname)
+
+ def execute(self):
+
+ next_node(self.__next__)
+
+ parameters = getattr(self, "parameters", None)
+
+ if parameters is None:
+ parameters = Transform.default_parameters
+
+ trans = renpy.display.motion.ATLTransform(self.atl, parameters=parameters)
+ renpy.dump.transforms.append((self.varname, self.filename, self.linenumber))
+ setattr(renpy.store, self.varname, trans)
+
+
+def predict_imspec(imspec, scene=False, atl=None):
+ """
+ Call this to use the given callback to predict the image named
+ in imspec.
+ """
+
+ if len(imspec) == 7:
+ name, expression, tag, _at_list, layer, _zorder, _behind = imspec
+
+ elif len(imspec) == 6:
+ name, expression, tag, _at_list, layer, _zorder = imspec
+
+ elif len(imspec) == 3:
+ name, _at_list, layer = imspec
+
+
+ if expression:
+ try:
+ img = renpy.python.py_eval(expression)
+ img = renpy.easy.displayable(img)
+ except:
+ return
+
+ else:
+ img = renpy.display.image.images.get(name, None)
+ if img is None:
+ return
+
+ full_name = name
+ if tag:
+ full_name = (tag,) + full_name[1:]
+
+ if scene:
+ renpy.game.context().images.predict_scene(layer)
+
+ renpy.game.context().images.predict_show(tag or name, layer)
+
+ if atl is not None:
+ try:
+ img = renpy.display.motion.ATLTransform(atl, child=img)
+ except:
+ import traceback
+ traceback.print_exc()
+
+ renpy.display.predict.displayable(img)
+
+
+
+def show_imspec(imspec, atl=None):
+
+ if len(imspec) == 7:
+ name, expression, tag, at_list, layer, zorder, behind = imspec
+
+ elif len(imspec) == 6:
+ name, expression, tag, at_list, layer, zorder = imspec
+ behind = [ ]
+
+ elif len(imspec) == 3:
+ name, at_list, layer = imspec
+ expression = None
+ tag = None
+ zorder = None
+ behind = [ ]
+
+ if zorder is not None:
+ zorder = renpy.python.py_eval(zorder)
+ else:
+ zorder = 0
+
+ if expression is not None:
+ expression = renpy.python.py_eval(expression)
+ expression = renpy.easy.displayable(expression)
+
+ at_list = [ renpy.python.py_eval(i) for i in at_list ]
+
+ renpy.config.show(name,
+ at_list=at_list,
+ layer=layer,
+ what=expression,
+ zorder=zorder,
+ tag=tag,
+ behind=behind,
+ atl=atl)
+
+class Show(Node):
+
+ __slots__ = [
+ 'imspec',
+ 'atl',
+ ]
+
+ def __init__(self, loc, imspec, atl=None):
+ """
+ @param imspec: A triple consisting of an image name (itself a
+ tuple of strings), a list of at expressions, and a layer.
+ """
+
+ super(Show, self).__init__(loc)
+
+ self.imspec = imspec
+ self.atl = atl
+
+ def diff_info(self):
+ return (Show, tuple(self.imspec[0]))
+
+ def execute(self):
+ next_node(self.__next__)
+
+ show_imspec(self.imspec, atl=getattr(self, "atl", None))
+
+ def predict(self):
+ predict_imspec(self.imspec, atl=getattr(self, "atl", None))
+ return [ self.__next__ ]
+
+
+class Scene(Node):
+
+ __slots__ = [
+ 'imspec',
+ 'layer',
+ 'atl',
+ ]
+
+ def __init__(self, loc, imgspec, layer, atl=None):
+ """
+ @param imspec: A triple consisting of an image name (itself a
+ tuple of strings), a list of at expressions, and a layer, or
+ None to not have this scene statement also display an image.
+ """
+
+ super(Scene, self).__init__(loc)
+
+ self.imspec = imgspec
+ self.layer = layer
+ self.atl = atl
+
+ def diff_info(self):
+
+ if self.imspec:
+ data = tuple(self.imspec[0])
+ else:
+ data = None
+
+ return (Scene, data)
+
+ def execute(self):
+
+ next_node(self.__next__)
+
+ renpy.config.scene(self.layer)
+
+ if self.imspec:
+ show_imspec(self.imspec, atl=getattr(self, "atl", None))
+
+ def predict(self):
+
+ if self.imspec:
+ predict_imspec(self.imspec, atl=getattr(self, "atl", None), scene=True)
+
+ return [ self.__next__ ]
+
+
+class Hide(Node):
+
+ __slots__ = [
+ 'imspec',
+ ]
+
+ def __init__(self, loc, imgspec):
+ """
+ @param imspec: A triple consisting of an image name (itself a
+ tuple of strings), a list of at expressions, and a list of
+ with expressions.
+ """
+
+ super(Hide, self).__init__(loc)
+
+ self.imspec = imgspec
+
+ def diff_info(self):
+ return (Hide, tuple(self.imspec[0]))
+
+ def predict(self):
+
+ if len(self.imspec) == 3:
+ name, _at_list, layer = self.imspec
+ tag = None
+ _expression = None
+ _zorder = None
+ _behind = None
+ elif len(self.imspec) == 6:
+ name, _expression, tag, _at_list, layer, _zorder = self.imspec
+ _behind = None
+ elif len(self.imspec) == 7:
+ name, _expression, tag, _at_list, layer, _zorder, _behind = self.imspec
+
+
+ if tag is None:
+ tag = name[0]
+
+ renpy.game.context().images.predict_hide(tag, layer)
+
+ return [ self.__next__ ]
+
+ def execute(self):
+
+ next_node(self.__next__)
+
+ if len(self.imspec) == 3:
+ name, _at_list, layer = self.imspec
+ _expression = None
+ tag = None
+ _zorder = 0
+ elif len(self.imspec) == 6:
+ name, _expression, tag, _at_list, layer, _zorder = self.imspec
+ elif len(self.imspec) == 7:
+ name, _expression, tag, _at_list, layer, _zorder, _behind = self.imspec
+
+ renpy.config.hide(tag or name, layer)
+
+
+class With(Node):
+
+ __slots__ = [
+ 'expr',
+ 'paired',
+ ]
+
+ def __setstate__(self, state):
+ self.paired = None
+ Node.__setstate__(self, state)
+
+ def __init__(self, loc, expr, paired=None):
+ """
+ @param expr: An expression giving a transition or None.
+ """
+
+ super(With, self).__init__(loc)
+ self.expr = expr
+ self.paired = paired
+
+ def diff_info(self):
+ return (With, self.expr)
+
+ def execute(self):
+
+ next_node(self.__next__)
+
+ trans = renpy.python.py_eval(self.expr)
+
+ if self.paired is not None:
+ paired = renpy.python.py_eval(self.paired)
+ else:
+ paired = None
+
+ renpy.exports.with_statement(trans, paired)
+
+ def predict(self):
+
+ try:
+ trans = renpy.python.py_eval(self.expr)
+
+ if trans:
+ renpy.display.predict.displayable(trans(old_widget=None, new_widget=None))
+
+ except:
+ pass
+
+
+ return [ self.__next__ ]
+
+
+class Call(Node):
+
+ __slots__ = [
+ 'label',
+ 'arguments',
+ 'expression',
+ ]
+
+ def __setstate__(self, state):
+ self.arguments = None
+ Node.__setstate__(self, state)
+
+ def __init__(self, loc, label, expression, arguments):
+
+ super(Call, self).__init__(loc)
+ self.label = label
+ self.expression = expression
+ self.arguments = arguments
+
+ def diff_info(self):
+ return (Call, self.label, self.expression)
+
+ def execute(self):
+
+ label = self.label
+ if self.expression:
+ label = renpy.python.py_eval(label)
+
+ rv = renpy.game.context().call(label, return_site=self.next.name)
+ next_node(rv)
+ renpy.game.context().abnormal = True
+
+ if self.arguments:
+
+ args = [ ]
+ kwargs = renpy.python.RevertableDict()
+
+ for name, expr in self.arguments.arguments:
+
+ value = renpy.python.py_eval(expr)
+
+ if name is None:
+ args.append(value)
+ else:
+ if name in kwargs:
+ raise Exception("The argument named %s appears twice." % name)
+
+ kwargs[name] = value
+
+ if self.arguments.extrapos:
+ args.extend(renpy.python.py_eval(self.arguments.extrapos))
+
+ if self.arguments.extrakw:
+ for name, value in renpy.python.py_eval(self.arguments.extrakw).items():
+ if name in kwargs:
+ raise Exception("The argument named %s appears twice." % name)
+
+ kwargs[name] = value
+
+
+ renpy.store._args = tuple(args)
+ renpy.store._kwargs = kwargs
+
+ def predict(self):
+ if self.expression:
+ return [ ]
+ else:
+ return [ renpy.game.script.lookup(self.label) ]
+
+ def scry(self):
+ rv = Node.scry(self)
+ rv._next = None
+ return rv
+
+
+class Return(Node):
+
+ __slots__ = [ 'expression']
+
+ def __setstate__(self, state):
+ self.expression = None
+ Node.__setstate__(self, state)
+
+ def __init__(self, loc, expression):
+ super(Return, self).__init__(loc)
+ self.expression = expression
+
+ def diff_info(self):
+ return (Return, )
+
+ # We don't care what the next node is.
+ def chain(self, next): #@ReservedAssignment
+ self.next = None
+ return
+
+ def execute(self):
+
+ if self.expression:
+ renpy.store._return = renpy.python.py_eval(self.expression)
+ else:
+ renpy.store._return = None
+
+ renpy.game.context().pop_dynamic()
+
+ next_node(renpy.game.context().lookup_return(pop=True))
+
+ def predict(self):
+ site = renpy.game.context().lookup_return(pop=False)
+ if site:
+ return [ site ]
+ else:
+ return [ ]
+
+ def scry(self):
+ rv = Node.scry(self)
+ rv._next = None
+ return rv
+
+
+class Menu(Node):
+
+ __slots__ = [
+ 'items',
+ 'set',
+ 'with_',
+ ]
+
+ def __init__(self, loc, items, set, with_): #@ReservedAssignment
+ super(Menu, self).__init__(loc)
+
+ self.items = items
+ self.set = set
+ self.with_ = with_
+
+ def diff_info(self):
+ return (Menu,)
+
+ def get_children(self):
+ rv = [ ]
+
+ for _label, _condition, block in self.items:
+ if block:
+ rv.extend(block)
+
+ return rv
+
+ # Blocks of statements in a choice continue after the menu.
+ def chain(self, next): #@ReservedAssignment
+
+ self.next = next
+
+ for (_label, _condition, block) in self.items:
+ if block:
+ chain_block(block, next)
+
+ def execute(self):
+
+ next_node(self.__next__)
+
+ choices = [ ]
+ narration = [ ]
+
+ for i, (label, condition, block) in enumerate(self.items):
+
+ if renpy.config.say_menu_text_filter:
+ label = renpy.config.say_menu_text_filter(label)
+
+ if block is None:
+ if renpy.config.narrator_menu and label:
+ narration.append(label)
+ else:
+ choices.append((label, condition, None))
+ else:
+ choices.append((label, condition, i))
+ next_node(block[0])
+
+ if narration:
+ renpy.exports.say(None, "\n".join(narration), interact=False)
+
+ say_menu_with(self.with_, renpy.game.interface.set_transition)
+ choice = renpy.exports.menu(choices, self.set)
+
+ if choice is not None:
+ next_node(self.items[choice][2][0])
+ else:
+ next_node(self.__next__)
+
+
+ def predict(self):
+ rv = [ ]
+
+ def predict_with(trans):
+ renpy.display.predict.displayable(trans(old_widget=None, new_widget=None))
+
+ say_menu_with(self.with_, predict_with)
+
+ renpy.store.predict_menu()
+
+ for _label, _condition, block in self.items:
+ if block:
+ rv.append(block[0])
+
+ return rv
+
+ def scry(self):
+ rv = Node.scry(self)
+ rv._next = None
+ rv.interacts = True
+ return rv
+
+ def restructure(self, callback):
+ for _label, _condition, block in self.items:
+ if block is not None:
+ callback(block)
+
+setattr(Menu, "with", Menu.with_) # E1101
+
+
+# Goto is considered harmful. So we decided to name it "jump"
+# instead.
+class Jump(Node):
+
+ __slots__ = [
+ 'target',
+ 'expression',
+ ]
+
+ def __init__(self, loc, target, expression):
+ super(Jump, self).__init__(loc)
+
+ self.target = target
+ self.expression = expression
+
+ def diff_info(self):
+ return (Jump, self.target, self.expression)
+
+ # We don't care what our next node is.
+ def chain(self, next): #@ReservedAssignment
+ self.next = None
+ return
+
+ def execute(self):
+
+ target = self.target
+ if self.expression:
+ target = renpy.python.py_eval(target)
+
+ rv = renpy.game.script.lookup(target)
+ renpy.game.context().abnormal = True
+
+ next_node(rv)
+
+ def predict(self):
+
+ if self.expression:
+ return [ ]
+ else:
+ return [ renpy.game.script.lookup(self.target) ]
+
+ def scry(self):
+ rv = Node.scry(self)
+ if self.expression:
+ rv._next = None
+ else:
+ rv._next = renpy.game.script.lookup(self.target)
+
+ return rv
+
+
+# GNDN
+class Pass(Node):
+
+ __slots__ = [ ]
+
+ def diff_info(self):
+ return (Pass,)
+
+ def execute(self):
+ next_node(self.__next__)
+
+
+class While(Node):
+
+ __slots__ = [
+ 'condition',
+ 'block',
+ ]
+
+ def __init__(self, loc, condition, block):
+ super(While, self).__init__(loc)
+
+ self.condition = condition
+ self.block = block
+
+ def diff_info(self):
+ return (While, self.condition)
+
+ def get_children(self):
+ return self.block
+
+ def chain(self, next): #@ReservedAssignment
+ self.next = next
+ chain_block(self.block, self)
+
+ def execute(self):
+
+ next_node(self.__next__)
+
+ if renpy.python.py_eval(self.condition):
+ next_node(self.block[0])
+
+ def predict(self):
+ return [ self.block[0], self.__next__ ]
+
+ def scry(self):
+ rv = Node.scry(self)
+ rv._next = None
+ return rv
+
+ def restructure(self, callback):
+ callback(self.block)
+
+class If(Node):
+
+ __slots__ = [ 'entries' ]
+
+ def __init__(self, loc, entries):
+ """
+ @param entries: A list of (condition, block) tuples.
+ """
+
+ super(If, self).__init__(loc)
+
+ self.entries = entries
+
+ def diff_info(self):
+ return (If,)
+
+ def get_children(self):
+ rv = [ ]
+
+ for _condition, block in self.entries:
+ rv.extend(block)
+
+ return rv
+
+ def chain(self, next): #@ReservedAssignment
+ self.next = next
+
+ for _condition, block in self.entries:
+ chain_block(block, next)
+
+ def execute(self):
+
+ next_node(self.__next__)
+
+ for condition, block in self.entries:
+ if renpy.python.py_eval(condition):
+ next_node(block[0])
+ return
+
+ def predict(self):
+
+ return [ block[0] for _condition, block in self.entries ] + \
+ [ self.__next__ ]
+
+ def scry(self):
+ rv = Node.scry(self)
+ rv._next = None
+ return rv
+
+ def restructure(self, callback):
+ for _condition, block in self.entries:
+ callback(block)
+
+class UserStatement(Node):
+
+ __slots__ = [
+ 'line',
+ 'parsed',
+ 'block',
+ 'translatable' ]
+
+ def __setstate__(self, state):
+ self.block = [ ]
+ self.translatable = False
+ Node.__setstate__(self, state)
+
+ def __init__(self, loc, line, block):
+
+ super(UserStatement, self).__init__(loc)
+ self.line = line
+ self.block = block
+ self.parsed = None
+
+ # Do not store the parse quite yet.
+ _parse_info = renpy.statements.parse(self, self.line, self.block)
+
+ def diff_info(self):
+ return (UserStatement, self.line)
+
+ def execute(self):
+ next_node(self.get_next())
+
+ self.call("execute")
+
+ def predict(self):
+ self.call("predict")
+ return [ self.get_next() ]
+
+ def call(self, method, *args, **kwargs):
+
+ parsed = self.parsed
+ if parsed is None:
+ parsed = renpy.statements.parse(self, self.line, self.block)
+ self.parsed = parsed
+
+ renpy.statements.call(method, parsed, *args, **kwargs)
+
+ def get_next(self):
+ rv = self.call("next")
+ if rv is not None:
+ return renpy.game.script.lookup(rv)
+ else:
+ return self.__next__
+
+ def scry(self):
+ rv = Node.scry(self)
+ rv._next = self.get_next()
+ self.call("scry", rv)
+ return rv
+
+ def get_code(self, dialogue_filter=None):
+ return self.line
+
+
+class Define(Node):
+
+ __slots__ = [
+ 'varname',
+ 'code',
+ ]
+
+ def __init__(self, loc, name, expr):
+ """
+ @param name: The name of the image being defined.
+
+ @param expr: An expression yielding a Displayable that is
+ assigned to the image.
+ """
+
+ super(Define, self).__init__(loc)
+
+ self.varname = name
+ self.code = PyCode(expr, loc=loc, mode='eval')
+
+ def diff_info(self):
+ return (Define, tuple(self.varname))
+
+ def execute(self):
+
+ next_node(self.__next__)
+
+ value = renpy.python.py_eval_bytecode(self.code.bytecode)
+ renpy.dump.definitions.append((self.varname, self.filename, self.linenumber))
+ setattr(renpy.store, self.varname, value)
+
+
+class Screen(Node):
+
+ __slots__ = [
+ 'screen',
+ ]
+
+ def __init__(self, loc, screen):
+ """
+ @param name: The name of the image being defined.
+
+ @param expr: An expression yielding a Displayable that is
+ assigned to the image.
+ """
+
+ super(Screen, self).__init__(loc)
+
+ self.screen = screen
+
+ def diff_info(self):
+ return (Screen, self.screen.name)
+
+ def execute(self):
+ next_node(self.__next__)
+ self.screen.define()
+ renpy.dump.screens.append((self.screen.name, self.filename, self.linenumber))
+
+
+################################################################################
+# Translations
+################################################################################
+
+class Translate(Node):
+ """
+ A translation block, produced either by explicit translation statements
+ or implicit translation blocs.
+
+ If language is None, when executed this transfers control to the translate
+ statement in the current language, if any, and otherwise runs the block.
+ If language is not None, causes an error to occur if control reaches this
+ statement.
+
+ When control normally leaves a translate statement, in any language, it
+ goes to the end of the translate statement in the None language.
+ """
+
+ __slots__ = [
+ "identifier",
+ "language",
+ "block",
+ ]
+
+ def __init__(self, loc, identifier, language, block):
+ super(Translate, self).__init__(loc)
+
+ self.identifier = identifier
+ self.language = language
+ self.block = block
+
+ def diff_info(self):
+ return (Translate, self.identifier, self.language)
+
+ def chain(self, next): #@ReservedAssignment
+ self.next = next
+ chain_block(self.block, next)
+
+ def execute(self):
+
+ if self.language is not None:
+ next_node(self.__next__)
+ raise Exception("Translation nodes cannot be run directly.")
+
+ next_node(renpy.game.script.translator.lookup_translate(self.identifier))
+ renpy.game.context().translate_identifier = self.identifier
+
+ def predict(self):
+ node = renpy.game.script.translator.lookup_translate(self.identifier)
+ return [ node ]
+
+ def scry(self):
+ rv = Scry()
+ rv._next = renpy.game.script.translator.lookup_translate(self.identifier)
+ return rv
+
+ def get_children(self):
+ return self.block
+
+ def restructure(self, callback):
+ return callback(self.block)
+
+
+class EndTranslate(Node):
+ """
+ A node added implicitly after each translate block. It's responsible for
+ resetting the translation identifier.
+ """
+
+ def __init__(self, loc):
+ super(EndTranslate, self).__init__(loc)
+
+ def diff_info(self):
+ return (EndTranslate,)
+
+ def execute(self):
+ next_node(self.__next__)
+ renpy.game.context().translate_identifier = None
+
+
+class TranslateString(Node):
+ """
+ A node used for translated strings.
+ """
+
+ __slots__ = [
+ "language",
+ "old",
+ "new"
+ ]
+
+ def __init__(self, loc, language, old, new):
+ super(TranslateString, self).__init__(loc)
+ self.language = language
+ self.old = old
+ self.new = new
+
+ def diff_info(self):
+ return (TranslateString,)
+
+ def execute(self):
+ next_node(self.__next__)
+ renpy.translation.add_string_translation(self.language, self.old, self.new)
+
+class TranslatePython(Node):
+
+ __slots__ = [
+ 'language',
+ 'code',
+ ]
+
+ def __init__(self, loc, language, python_code):
+ """
+ @param code: A PyCode object.
+
+ @param hide: If True, the code will be executed with its
+ own local dictionary.
+ """
+
+ super(TranslatePython, self).__init__(loc)
+
+ self.language = language
+ self.code = PyCode(python_code, loc=loc, mode='exec')
+
+ def diff_info(self):
+ return (TranslatePython, self.code.source)
+
+ def execute(self):
+ next_node(self.__next__)
+
+ # def early_execute(self):
+ # renpy.python.create_store(self.store)
+ # renpy.python.py_exec_bytecode(self.code.bytecode, self.hide, store=self.store)
+
+