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
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()