Ejemplo n.º 1
0
def display_say(
        who,
        what,
        show_function,
        interact,
        slow,
        afm,
        ctc,
        ctc_pause,
        ctc_position,
        all_at_once,
        cb_args,
        with_none,
        callback,
        type, # @ReservedAssignment
        checkpoint=True,
        ctc_timedpause=None,
        ctc_force=False,
        advance=True,
        multiple=None,
        dtt=None):

    # Final is true if this statement should perform an interaction.

    if multiple is None:
        final = interact
    else:
        step, total = multiple

        if step == total:
            final = interact
        else:
            final = False

    if not final:
        advance = False

    if final and (not renpy.game.preferences.skip_unseen) and (not renpy.game.context().seen_current(True)) and renpy.config.skipping == "fast": # type: ignore
        renpy.config.skipping = None

    # If we're in fast skipping mode, don't bother with say
    # statements at all.
    if advance and renpy.config.skipping == "fast":

        for i in renpy.config.fast_skipping_callbacks:
            i()

        # Clears out transients.
        renpy.exports.with_statement(None)
        return

    # Figure out the callback(s) we want to use.
    if callback is None:
        if renpy.config.character_callback:
            callback = [ renpy.config.character_callback ]
        else:
            callback = [ ]

    if not isinstance(callback, list):
        callback = [ callback ]

    callback = renpy.config.all_character_callbacks + callback

    # Call the begin callback.
    for c in callback:
        c("begin", interact=interact, type=type, **cb_args)

    roll_forward = renpy.exports.roll_forward_info()

    if roll_forward is True:
        roll_forward = False

    # If we're just after a rollback or roll_forward, disable slow.
    after_rollback = renpy.game.after_rollback
    if after_rollback:
        slow = False
        all_at_once = True

    # If we're committed to skipping this statement, disable slow.
    elif (renpy.config.skipping and
          advance and
          (renpy.game.preferences.skip_unseen or # type: ignore
           renpy.game.context().seen_current(True))):
        slow = False
        all_at_once = True

    # Figure out which pause we're on. (Or set the pause to None in
    # order to put us in all-at-once mode.)
    if not interact or renpy.game.preferences.self_voicing: # type: ignore
        all_at_once = True

    if dtt is None:
        dtt = DialogueTextTags(what)

    if all_at_once:
        pause_start = [ dtt.pause_start[0] ]
        pause_end = [ dtt.pause_end[-1] ]
        pause_delay = [ dtt.pause_delay[-1] ]
    else:
        pause_start = dtt.pause_start
        pause_end = dtt.pause_end
        pause_delay = dtt.pause_delay

    exception = None

    if dtt.fast:
        for i in renpy.config.say_sustain_callbacks:
            i()

    try:

        for i, (start, end, delay) in enumerate(zip(pause_start, pause_end, pause_delay)):

            # True if the is the last pause in a line of dialogue.
            last_pause = (i == len(pause_start) - 1)

            if dtt.no_wait:
                last_pause = False

            # If we're going to do an interaction, then saybehavior needs
            # to be here.
            if advance:
                behavior = renpy.ui.saybehavior(allow_dismiss=renpy.config.say_allow_dismiss)
            else:
                behavior = None

            # The string to show.
            what_string = dtt.text

            # Figure out the CTC to use, if any.
            if last_pause:
                what_ctc = ctc
                ctc_kind = "last"
            else:
                if delay is not None:
                    what_ctc = ctc_timedpause or ctc_pause
                    ctc_kind = "timedpause"
                else:
                    what_ctc = ctc_pause
                    ctc_kind = "pause"

            ctc_kwargs = {
                "ctc_kind" : ctc_kind,
                "ctc_last" : ctc,
                "ctc_pause" : ctc_pause,
                "ctc_timedpause" : ctc_timedpause,
            }

            if not (interact or ctc_force):
                what_ctc = None

            what_ctc = renpy.easy.displayable_or_none(what_ctc)

            if (what_ctc is not None) and what_ctc._duplicatable:
                what_ctc = what_ctc._duplicate(None)
                what_ctc._unique()

            if ctc is not what_ctc:
                if (ctc is not None) and ctc._duplicatable:
                    ctc = ctc._duplicate(None)
                    ctc._unique()

            if delay == 0:
                what_ctc = None
                ctc = None

            # Run the show callback.
            for c in callback:
                c("show", interact=interact, type=type, **cb_args)

            # Create the callback that is called when the slow text is done.
            slow_done = SlowDone(what_ctc, ctc_position, callback, interact, type, cb_args, delay, ctc_kwargs, last_pause)

            # Show the text.
            if multiple:
                what_text = show_function(who, what_string, multiple=multiple)
            else:
                what_text = show_function(who, what_string)

            if interact or what_string or (what_ctc is not None) or (behavior and afm):

                if isinstance(what_text, tuple):
                    what_text = renpy.display.screen.get_widget(what_text[0], what_text[1], what_text[2])

                if not isinstance(what_text, renpy.text.text.Text): # @UndefinedVariable
                    raise Exception("The say screen (or show_function) must return a Text object.")

                if what_ctc:

                    if ctc_position == "nestled":
                        what_text.set_ctc(what_ctc)
                    elif ctc_position == "nestled-close":
                        what_text.set_ctc([ u"\ufeff", what_ctc, ])

                if (not last_pause) and ctc:
                    if ctc_position == "nestled":
                        what_text.set_last_ctc(ctc)
                    elif ctc_position == "nestled-close":
                        what_text.set_last_ctc([ u"\ufeff", ctc, ])

                if what_text.text[0] == what_string:

                    # Update the properties of the what_text widget.
                    what_text.start = start
                    what_text.end = end
                    what_text.slow = slow
                    what_text.slow_done = slow_done

                    what_text.update()

                elif renpy.config.developer:
                    raise Exception("The displayable with id 'what' was not given the exact contents of the what variable given to the say screen.")

                if behavior and afm:
                    behavior.set_text(what_text)

            else:

                slow = False

            for c in callback:
                c("show_done", interact=interact, type=type, **cb_args)

            if not slow:
                slow_done()

            if final:
                rv = renpy.ui.interact(mouse='say', type=type, roll_forward=roll_forward)

                # This is only the case if the user has rolled forward, {nw} happens, or
                # maybe in some other obscure cases.
                if rv is False:
                    break

                if isinstance(rv, (renpy.game.JumpException, renpy.game.CallException)):
                    raise rv

                if not last_pause:
                    for i in renpy.config.say_sustain_callbacks:
                        i()

    except (renpy.game.JumpException, renpy.game.CallException) as e:

        exception = e

    # Do the checkpoint and with None.
    if final:

        if not dtt.no_wait:
            if checkpoint:
                if exception is None:
                    renpy.exports.checkpoint(True)
                else:
                    renpy.exports.checkpoint(exception)

        else:
            renpy.game.after_rollback = after_rollback

        if with_none is None:
            with_none = renpy.config.implicit_with_none

        renpy.plog(1, "before with none")

        if with_none:
            renpy.game.interface.do_with(None, None)

        renpy.plog(1, "after with none")

    for c in callback:
        c("end", interact=interact, type=type, **cb_args)

    if exception is not None:
        raise exception
Ejemplo n.º 2
0
    def run(self, node=None):
        """
        Executes as many nodes as possible in the current context. If the
        node argument is given, starts executing from that node. Otherwise,
        looks up the node given in self.current, and executes from there.
        """

        self.exception_handler = None

        self.abnormal = True

        if node is None:
            node = renpy.game.script.lookup(self.current)

        developer = renpy.config.developer
        tracing = sys.gettrace() is not None

        # Is this the first time through the loop?
        first = True

        while node:

            if node.name == self.come_from_name:
                self.come_from_name = None
                node = self.call(self.come_from_label, return_site=node.name)
                self.make_dynamic(["_return", "_begin_rollback"])
                renpy.store._begin_rollback = False

            this_node = node
            type_node_name = type(node).__name__

            renpy.plog(1, "--- start {} ({}:{})", type_node_name,
                       node.filename, node.linenumber)

            self.current = node.name
            self.last_abnormal = self.abnormal
            self.abnormal = False
            self.defer_rollback = None

            if renpy.config.line_log:
                ll_entry = LineLogEntry(node.filename, node.linenumber, node,
                                        self.last_abnormal)

                if ll_entry not in self.line_log:
                    self.line_log.append(ll_entry)

            if not renpy.store._begin_rollback:
                update_rollback = False
                force_rollback = False
            elif first or self.force_checkpoint or (node.rollback == "force"):
                update_rollback = True
                force_rollback = True
            elif not renpy.config.all_nodes_rollback and (node.rollback
                                                          == "never"):
                update_rollback = False
                force_rollback = False
            else:
                update_rollback = True
                force_rollback = False

            # Force a new rollback to start to match things in the forward log.
            if renpy.game.log.forward and renpy.game.log.forward[0][
                    0] == node.name:
                update_rollback = True
                force_rollback = True

            first = False

            if update_rollback:

                if self.rollback and renpy.game.log:
                    renpy.game.log.begin(force=force_rollback)

                if self.rollback and self.force_checkpoint:
                    renpy.game.log.force_checkpoint = True
                    self.force_checkpoint = False

            self.seen = False

            renpy.test.testexecution.take_name(self.current)

            try:
                try:
                    check_infinite_loop()

                    if tracing:
                        self.report_coverage(node)

                    renpy.game.exception_info = "While running game code:"

                    self.next_node = None

                    renpy.plog(2, "    before execute {} ({}:{})",
                               type_node_name, node.filename, node.linenumber)

                    node.execute()

                    renpy.plog(2, "    after execute {} ({}:{})",
                               type_node_name, node.filename, node.linenumber)

                    if developer and self.next_node:
                        self.check_stacks()

                except renpy.game.CONTROL_EXCEPTIONS as e:

                    # An exception ends the current translation.
                    self.translate_interaction = None

                    raise

                except Exception as e:
                    self.translate_interaction = None

                    exc_info = sys.exc_info()
                    short, full, traceback_fn = renpy.error.report_exception(
                        e, editor=False)

                    try:
                        handled = False

                        if self.exception_handler is not None:
                            self.exception_handler(short, full, traceback_fn)
                            handled = True
                        elif renpy.config.exception_handler is not None:
                            handled = renpy.config.exception_handler(
                                short, full, traceback_fn)

                        if not handled:
                            if renpy.display.error.report_exception(
                                    short, full, traceback_fn):
                                raise
                    except renpy.game.CONTROL_EXCEPTIONS as ce:
                        raise ce
                    except Exception as ce:
                        reraise(exc_info[0], exc_info[1], exc_info[2])

                node = self.next_node

            except renpy.game.JumpException as e:
                node = renpy.game.script.lookup(e.args[0])
                self.abnormal = True

            except renpy.game.CallException as e:

                if e.from_current:
                    return_site = getattr(node, "statement_start", node).name
                else:
                    if self.next_node is None:
                        raise Exception(
                            "renpy.call can't be used when the next node is undefined."
                        )
                    return_site = self.next_node.name

                node = self.call(e.label, return_site=return_site)
                self.abnormal = True
                renpy.store._args = e.args
                renpy.store._kwargs = e.kwargs

            if self.seen:
                renpy.game.persistent._seen_ever[
                    self.current] = True  # type: ignore
                renpy.game.seen_session[self.current] = True

            renpy.plog(2, "    end {} ({}:{})", type_node_name,
                       this_node.filename, this_node.linenumber)

        if self.rollback and renpy.game.log:
            renpy.game.log.complete()