1# Copyright 2004-2021 Tom Rothamel <pytom@bishoujo.us>
2#
3# Permission is hereby granted, free of charge, to any person
4# obtaining a copy of this software and associated documentation files
5# (the "Software"), to deal in the Software without restriction,
6# including without limitation the rights to use, copy, modify, merge,
7# publish, distribute, sublicense, and/or sell copies of the Software,
8# and to permit persons to whom the Software is furnished to do so,
9# subject to the following conditions:
10#
11# The above copyright notice and this permission notice shall be
12# included in all copies or substantial portions of the Software.
13#
14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22# The Character object (and friends).
23
24from __future__ import division, absolute_import, with_statement, print_function, unicode_literals
25from renpy.compat import *
26
27import renpy.display
28
29import re
30import os
31import collections
32import renpy.six as six
33
34# This matches the dialogue-relevant text tags.
35TAG_RE = re.compile(r'(\{\{)|(\{(p|w|nw|fast|done)(?:\=([^}]*))?\})', re.S)
36
37less_pauses = ("RENPY_LESS_PAUSES" in os.environ)
38
39
40class DialogueTextTags(object):
41    """
42    This object parses the text tags that only make sense in dialogue,
43    like {fast}, {p}, {w}, and {nw}.
44    """
45
46    def __init__(self, s):
47
48        # The text that we've accumulated, not including any tags.
49        self.text = ""
50
51        # The index in the produced string where each pause starts.
52        self.pause_start = [ 0 ]
53
54        # The index in the produced string where each pause ends.
55        self.pause_end = [ ]
56
57        # The time to delay for each pause. None to delay forever.
58        self.pause_delay = [ ]
59
60        # True if we've encountered the no-wait tag.
61        self.no_wait = False
62
63        # Does this statement have a done tag?
64        self.has_done = False
65
66        # Does this statement have a fast tag?
67        self.fast = False
68
69        i = iter(TAG_RE.split(s))
70
71        while True:
72
73            try:
74                self.text += next(i)
75
76                quoted = next(i)
77                full_tag = next(i)
78                tag = next(i)
79                value = next(i)
80
81                if value is not None:
82                    value = float(value)
83
84                if quoted is not None:
85                    self.text += quoted
86                    continue
87
88                if tag == "p" or tag == "w":
89                    if not less_pauses:
90                        self.pause_start.append(len(self.text))
91                        self.pause_end.append(len(self.text))
92                        self.pause_delay.append(value)
93
94                elif tag == "nw":
95                    self.no_wait = True
96
97                elif tag == "fast":
98                    self.pause_start = [ len(self.text) ]
99                    self.pause_end = [ ]
100                    self.pause_delay = [ ]
101                    self.no_wait = False
102                    self.fast = True
103
104                elif tag == "done":
105                    self.has_done = True
106                    self.text += full_tag
107                    break
108
109                self.text += full_tag
110
111            except StopIteration:
112                break
113
114        self.pause_end.append(len(self.text))
115
116        while True:
117
118            try:
119                self.text += next(i)
120
121                quoted = next(i)
122                full_tag = next(i)
123                tag = next(i)
124                value = next(i)
125
126                if quoted is not None:
127                    self.text += quoted
128                    continue
129
130                self.text += full_tag
131
132            except StopIteration:
133                break
134
135        if self.no_wait:
136            self.pause_delay.append(0)
137        else:
138            self.pause_delay.append(None)
139
140
141def predict_show_display_say(who, what, who_args, what_args, window_args, image=False, two_window=False, side_image=None, screen=None, properties=None, **kwargs):
142    """
143    This is the default function used by Character to predict images that
144    will be used by show_display_say. It's called with more-or-less the
145    same parameters as show_display_say, and it's expected to return a
146    list of images used by show_display_say.
147    """
148
149    if side_image:
150        renpy.easy.predict(side_image)
151
152    if renpy.store._side_image_attributes:
153        renpy.easy.predict(renpy.display.image.ImageReference(("side",) + renpy.store._side_image_attributes))
154
155    if image:
156        if image != "<Dynamic>":
157            renpy.easy.predict(who)
158
159        kwargs["image"] = image
160
161    if screen:
162        props = compute_widget_properties(who_args, what_args, window_args, properties)
163
164        renpy.display.predict.screen(
165            screen,
166            _widget_properties=props,
167            who=who,
168            what=what,
169            two_window=two_window,
170            side_image=side_image,
171            **kwargs)
172
173        return
174
175
176def compute_widget_properties(who_args, what_args, window_args, properties, variant=None, multiple=None):
177    """
178    Computes and returns the widget properties.
179    """
180
181    def style_args(d, name):
182
183        style = d.get("style", None)
184
185        if style is None:
186            if multiple is None:
187                return d
188            else:
189                style = name
190
191        in_rollback = renpy.exports.in_rollback()
192
193        if (not in_rollback) and (not variant) and (not multiple):
194            return d
195
196        d = d.copy()
197
198        if isinstance(style, basestring):
199
200            if multiple is not None:
201                style = "block{}_multiple{}_{}".format(multiple[0], multiple[1], style)
202
203            style = getattr(renpy.store.style, style)
204
205            if variant is not None:
206                style = style[variant]
207
208            if in_rollback:
209                style = style["rollback"]
210
211        d["style"] = style
212
213        return d
214
215    who_args = style_args(who_args, "who")
216    what_args = style_args(what_args, "what")
217    window_args = style_args(window_args, "window")
218
219    rv = dict(properties)
220
221    for prefix in renpy.config.character_id_prefixes:
222        rv[prefix] = style_args(properties.get(prefix, {}), prefix)
223
224    rv["window"] = window_args
225    rv["what"] = what_args
226    rv["who"] = who_args
227
228    return rv
229
230
231def show_display_say(who, what, who_args={}, what_args={}, window_args={},
232                     image=False, side_image=None, two_window=False,
233                     two_window_vbox_properties={},
234                     who_window_properties={},
235                     say_vbox_properties={},
236                     transform=None,
237                     variant=None,
238                     screen=None,
239                     layer=None,
240                     properties={},
241                     multiple=None,
242                     **kwargs):
243    """
244    This is called (by default) by renpy.display_say to add the
245    widgets corresponding to a screen of dialogue to the user. It is
246    not expected to be called by the user, but instead to be called by
247    display_say, or by a function passed as the show_function argument
248    to Character or display_say.
249
250    @param who: The name of the character that is speaking, or None to
251    not show this name to the user.
252
253    @param what: What that character is saying. Please not that this
254    may not be a string, as it can also be a list containing both text
255    and displayables, suitable for use as the first argument of ui.text().
256
257    @param who_args: Additional keyword arguments intended to be
258    supplied to the ui.text that creates the who widget of this dialogue.
259
260    @param what_args: Additional keyword arguments intended to be
261    supplied to the ui.text that creates the what widget of this dialogue.
262
263    @param window_args: Additional keyword arguments intended to be
264    supplied to the ui.window that creates the who widget of this
265    dialogue.
266
267    @param image: If True, then who should be interpreted as an image
268    or displayable rather than a text string.
269
270    @param kwargs: Additional keyword arguments should be ignored.
271
272    This function is required to return the ui.text() widget
273    displaying the what text.
274    """
275
276    props = compute_widget_properties(who_args, what_args, window_args, properties, variant=variant, multiple=multiple)
277
278    def handle_who():
279        if who:
280            if image:
281                renpy.ui.add(renpy.display.im.image(who, loose=True, **props["who"]))
282            else:
283                renpy.ui.text(who, **who_args)
284
285    def merge_style(style, properties):
286
287        if isinstance(style, basestring):
288            style = getattr(renpy.store.style, style)
289
290        if variant is not None:
291            style = style[variant]
292
293        if renpy.exports.in_rollback():
294            style = style["rollback"]
295
296        rv = dict(style=style)
297        rv.update(properties)
298        return rv
299
300    if screen and renpy.display.screen.has_screen(screen):
301
302        if layer is None:
303            layer = renpy.config.say_layer
304
305        tag = screen
306        index = 0
307
308        if multiple:
309
310            if renpy.display.screen.has_screen("multiple_" + screen):
311                screen = "multiple_" + screen
312                kwargs["multiple"] = multiple
313
314            tag = "block{}_multiple{}_{}".format(multiple[0], multiple[1], tag)
315
316        if image:
317            kwargs["image"] = image
318
319        if (side_image is not None) or renpy.config.old_say_args:
320            kwargs["side_image"] = side_image
321
322        if two_window or renpy.config.old_say_args:
323            kwargs["two_window"] = two_window
324
325        renpy.display.screen.show_screen(
326            screen,
327            _widget_properties=props,
328            _transient=True,
329            _tag=tag,
330            who=who,
331            what=what,
332            _layer=layer,
333            **kwargs)
334
335        renpy.exports.shown_window()
336
337        return (tag, "what", layer)
338
339    # Apply the transform.
340    if transform:
341        renpy.ui.at(transform)
342
343    if two_window:
344
345        # Opens say_two_window_vbox.
346        renpy.ui.vbox(**merge_style('say_two_window_vbox', two_window_vbox_properties))
347
348        renpy.ui.window(**merge_style('say_who_window', who_window_properties))
349        handle_who()
350
351    renpy.ui.window(**props["window"])
352    # Opens the say_vbox.
353    renpy.ui.vbox(**merge_style('say_vbox', say_vbox_properties))
354
355    if not two_window:
356        handle_who()
357
358    rv = renpy.ui.text(what, **props["what"])
359
360    # Closes the say_vbox.
361    renpy.ui.close()
362
363    if two_window:
364        # Closes the say_two_window_vbox.
365        renpy.ui.close()
366
367    if side_image:
368        renpy.ui.image(side_image)
369
370    renpy.exports.shown_window()
371
372    return rv
373
374
375class SlowDone(object):
376    delay = None
377    ctc_kwargs = { }
378    last_pause = True
379
380    def __init__(self, ctc, ctc_position, callback, interact, type, cb_args, delay, ctc_kwargs, last_pause): # @ReservedAssignment
381        self.ctc = ctc
382        self.ctc_position = ctc_position
383        self.callback = callback
384        self.interact = interact
385        self.type = type
386        self.cb_args = cb_args
387        self.delay = delay
388        self.ctc_kwargs = ctc_kwargs
389        self.last_pause = last_pause
390
391    def __call__(self):
392
393        if self.interact and self.delay != 0:
394
395            if renpy.display.screen.has_screen("ctc"):
396
397                if self.ctc:
398                    args = [ self.ctc ]
399                else:
400                    args = [ ]
401
402                renpy.display.screen.show_screen("ctc", *args, _transient=True, _ignore_extra_kwargs=True, **self.ctc_kwargs)
403                renpy.exports.restart_interaction()
404
405            elif self.ctc and self.ctc_position == "fixed":
406                renpy.display.screen.show_screen("_ctc", _transient=True, ctc=self.ctc)
407                renpy.exports.restart_interaction()
408
409        if self.delay is not None:
410            renpy.ui.pausebehavior(self.delay, True, voice=self.last_pause)
411            renpy.exports.restart_interaction()
412
413        for c in self.callback:
414            c("slow_done", interact=self.interact, type=self.type, **self.cb_args)
415
416# This function takes care of repeatably showing the screen as part of
417# an interaction.
418
419
420def display_say(
421        who,
422        what,
423        show_function,
424        interact,
425        slow,
426        afm,
427        ctc,
428        ctc_pause,
429        ctc_position,
430        all_at_once,
431        cb_args,
432        with_none,
433        callback,
434        type, # @ReservedAssignment
435        checkpoint=True,
436        ctc_timedpause=None,
437        ctc_force=False,
438        advance=True,
439        multiple=None,
440        dtt=None):
441
442    # Final is true if this statement should perform an interaction.
443
444    if multiple is None:
445        final = interact
446    else:
447        step, total = multiple
448
449        if step == total:
450            final = interact
451        else:
452            final = False
453
454    if not final:
455        advance = False
456
457    if final and (not renpy.game.preferences.skip_unseen) and (not renpy.game.context().seen_current(True)) and renpy.config.skipping == "fast":
458        renpy.config.skipping = None
459
460    # If we're in fast skipping mode, don't bother with say
461    # statements at all.
462    if advance and renpy.config.skipping == "fast":
463
464        for i in renpy.config.fast_skipping_callbacks:
465            i()
466
467        # Clears out transients.
468        renpy.exports.with_statement(None)
469        return
470
471    # Figure out the callback(s) we want to use.
472    if callback is None:
473        if renpy.config.character_callback:
474            callback = [ renpy.config.character_callback ]
475        else:
476            callback = [ ]
477
478    if not isinstance(callback, list):
479        callback = [ callback ]
480
481    callback = renpy.config.all_character_callbacks + callback
482
483    # Call the begin callback.
484    for c in callback:
485        c("begin", interact=interact, type=type, **cb_args)
486
487    roll_forward = renpy.exports.roll_forward_info()
488
489    if roll_forward is True:
490        roll_forward = False
491
492    # If we're just after a rollback or roll_forward, disable slow.
493    after_rollback = renpy.game.after_rollback
494    if after_rollback:
495        slow = False
496        all_at_once = True
497
498    # If we're committed to skipping this statement, disable slow.
499    elif (renpy.config.skipping and
500          advance and
501          (renpy.game.preferences.skip_unseen or
502           renpy.game.context().seen_current(True))):
503        slow = False
504        all_at_once = True
505
506    # Figure out which pause we're on. (Or set the pause to None in
507    # order to put us in all-at-once mode.)
508    if not interact or renpy.game.preferences.self_voicing:
509        all_at_once = True
510
511    if dtt is None:
512        dtt = DialogueTextTags(what)
513
514    if all_at_once:
515        pause_start = [ dtt.pause_start[0] ]
516        pause_end = [ dtt.pause_end[-1] ]
517        pause_delay = [ dtt.pause_delay[-1] ]
518    else:
519        pause_start = dtt.pause_start
520        pause_end = dtt.pause_end
521        pause_delay = dtt.pause_delay
522
523    exception = None
524
525    if dtt.fast:
526        for i in renpy.config.say_sustain_callbacks:
527            i()
528
529    try:
530
531        for i, (start, end, delay) in enumerate(zip(pause_start, pause_end, pause_delay)):
532
533            # True if the is the last pause in a line of dialogue.
534            last_pause = (i == len(pause_start) - 1)
535
536            if dtt.no_wait:
537                last_pause = False
538
539            # If we're going to do an interaction, then saybehavior needs
540            # to be here.
541            if advance:
542                behavior = renpy.ui.saybehavior(allow_dismiss=renpy.config.say_allow_dismiss)
543            else:
544                behavior = None
545
546            # The string to show.
547            what_string = dtt.text
548
549            # Figure out the CTC to use, if any.
550            if last_pause:
551                what_ctc = ctc
552                ctc_kind = "last"
553            else:
554                if delay is not None:
555                    what_ctc = ctc_timedpause or ctc_pause
556                    ctc_kind = "timedpause"
557                else:
558                    what_ctc = ctc_pause
559                    ctc_kind = "pause"
560
561            ctc_kwargs = {
562                "ctc_kind" : ctc_kind,
563                "ctc_last" : ctc,
564                "ctc_pause" : ctc_pause,
565                "ctc_timedpause" : ctc_timedpause,
566            }
567
568            if not (interact or ctc_force):
569                what_ctc = None
570
571            what_ctc = renpy.easy.displayable_or_none(what_ctc)
572
573            if (what_ctc is not None) and what_ctc._duplicatable:
574                what_ctc = what_ctc._duplicate(None)
575                what_ctc._unique()
576
577            if ctc is not what_ctc:
578                if (ctc is not None) and ctc._duplicatable:
579                    ctc = ctc._duplicate(None)
580                    ctc._unique()
581
582            if delay == 0:
583                what_ctc = None
584                ctc = None
585
586            # Run the show callback.
587            for c in callback:
588                c("show", interact=interact, type=type, **cb_args)
589
590            # Create the callback that is called when the slow text is done.
591            slow_done = SlowDone(what_ctc, ctc_position, callback, interact, type, cb_args, delay, ctc_kwargs, last_pause)
592
593            # Show the text.
594            if multiple:
595                what_text = show_function(who, what_string, multiple=multiple)
596            else:
597                what_text = show_function(who, what_string)
598
599            if interact or what_string or (what_ctc is not None) or (behavior and afm):
600
601                if isinstance(what_text, tuple):
602                    what_text = renpy.display.screen.get_widget(what_text[0], what_text[1], what_text[2])
603
604                if not isinstance(what_text, renpy.text.text.Text): # @UndefinedVariable
605                    raise Exception("The say screen (or show_function) must return a Text object.")
606
607                if what_ctc:
608
609                    if ctc_position == "nestled":
610                        what_text.set_ctc(what_ctc)
611                    elif ctc_position == "nestled-close":
612                        what_text.set_ctc([ u"\ufeff", what_ctc, ])
613
614                if (not last_pause) and ctc:
615                    if ctc_position == "nestled":
616                        what_text.set_last_ctc(ctc)
617                    elif ctc_position == "nestled-close":
618                        what_text.set_last_ctc([ u"\ufeff", ctc, ])
619
620                if what_text.text[0] == what_string:
621
622                    # Update the properties of the what_text widget.
623                    what_text.start = start
624                    what_text.end = end
625                    what_text.slow = slow
626                    what_text.slow_done = slow_done
627
628                    what_text.update()
629
630                elif renpy.config.developer:
631                    raise Exception("The displayable with id 'what' was not given the exact contents of the what variable given to the say screen.")
632
633                if behavior and afm:
634                    behavior.set_text(what_text)
635
636            else:
637
638                slow = False
639
640            for c in callback:
641                c("show_done", interact=interact, type=type, **cb_args)
642
643            if not slow:
644                slow_done()
645
646            if final:
647                rv = renpy.ui.interact(mouse='say', type=type, roll_forward=roll_forward)
648
649                # This is only the case if the user has rolled forward, {nw} happens, or
650                # maybe in some other obscure cases.
651                if rv is False:
652                    break
653
654                if isinstance(rv, (renpy.game.JumpException, renpy.game.CallException)):
655                    raise rv
656
657                if not last_pause:
658                    for i in renpy.config.say_sustain_callbacks:
659                        i()
660
661    except (renpy.game.JumpException, renpy.game.CallException) as e:
662
663        exception = e
664
665    # Do the checkpoint and with None.
666    if final:
667
668        if not dtt.no_wait:
669            if checkpoint:
670                if exception is None:
671                    renpy.exports.checkpoint(True)
672                else:
673                    renpy.exports.checkpoint(exception)
674
675        else:
676            renpy.game.after_rollback = after_rollback
677
678        if with_none is None:
679            with_none = renpy.config.implicit_with_none
680
681        renpy.plog(1, "before with none")
682
683        if with_none:
684            renpy.game.interface.do_with(None, None)
685
686        renpy.plog(1, "after with none")
687
688    for c in callback:
689        c("end", interact=interact, type=type, **cb_args)
690
691    if exception is not None:
692        raise
693
694
695class HistoryEntry(renpy.object.Object):
696    """
697    Instances of this object are used to represent history entries in
698    _history_list.
699    """
700
701    # See ADVCharacter.add_history for the fields.
702
703    multiple = None
704
705    def __repr__(self):
706        return "<History {!r} {!r}>".format(self.who, self.what)
707
708
709# This is used to flag values that haven't been set by the user.
710NotSet = renpy.object.Sentinel("NotSet")
711
712# The number of multiple characters we've seen during the current
713# interaction.
714multiple_count = 0
715
716
717class ADVCharacter(object):
718    """
719    The character object contains information about a character. When
720    passed as the first argument to a say statement, it can control
721    the name that is displayed to the user, and the style of the label
722    showing the name, the text of the dialogue, and the window
723    containing both the label and the dialogue.
724    """
725
726    # Properties beginning with what or window that are treated
727    # specially.
728    special_properties = [
729        'what_prefix',
730        'what_suffix',
731        'who_prefix',
732        'who_suffix',
733        'show_function',
734        ]
735
736    voice_tag = None
737    properties = { }
738
739    _statement_name = None
740
741    # When adding a new argument here, remember to add it to copy below.
742    def __init__(
743            self,
744            name=NotSet,
745            kind=None,
746            **properties):
747
748        if kind is None:
749            kind = renpy.store.adv
750
751        if name is not NotSet:
752            properties["name"] = name
753
754        # This grabs a value out of properties, and then grabs it out of
755        # kind if it's not set.
756        def v(n):
757            if n in properties:
758                return properties.pop(n)
759            else:
760                return getattr(kind, n)
761
762        # Similar, but it grabs the value out of kind.display_args instead.
763        def d(n):
764            if n in properties:
765                return properties.pop(n)
766            else:
767                return kind.display_args[n]
768
769        self.name = v('name')
770        self.who_prefix = v('who_prefix')
771        self.who_suffix = v('who_suffix')
772        self.what_prefix = v('what_prefix')
773        self.what_suffix = v('what_suffix')
774
775        self.show_function = v('show_function')
776        self.predict_function = v('predict_function')
777
778        self.condition = v('condition')
779        self.dynamic = v('dynamic')
780        self.screen = v('screen')
781        self.mode = v('mode')
782
783        self.voice_tag = v('voice_tag')
784
785        if renpy.config.new_character_image_argument:
786            if "image" in properties:
787                self.image_tag = properties.pop("image")
788            else:
789                self.image_tag = kind.image_tag
790        else:
791            self.image_tag = None
792
793        self.display_args = dict(
794            interact=d('interact'),
795            slow=d('slow'),
796            afm=d('afm'),
797            ctc=renpy.easy.displayable_or_none(d('ctc')),
798            ctc_pause=renpy.easy.displayable_or_none(d('ctc_pause')),
799            ctc_timedpause=renpy.easy.displayable_or_none(d('ctc_timedpause')),
800            ctc_position=d('ctc_position'),
801            all_at_once=d('all_at_once'),
802            with_none=d('with_none'),
803            callback=d('callback'),
804            type=d('type'),
805            advance=d('advance'),
806            )
807
808        self._statement_name = properties.pop("statement_name", None)
809
810        self.properties = collections.defaultdict(dict)
811
812        if kind:
813            self.who_args = kind.who_args.copy()
814            self.what_args = kind.what_args.copy()
815            self.window_args = kind.window_args.copy()
816            self.show_args = kind.show_args.copy()
817            self.cb_args = kind.cb_args.copy()
818
819            for k, v in kind.properties.items():
820                self.properties[k] = dict(v)
821
822        else:
823            self.who_args = { "substitute" : False }
824            self.what_args = { "substitute" : False }
825            self.window_args = { }
826            self.show_args = { }
827            self.cb_args = { }
828
829        if not renpy.config.new_character_image_argument:
830            if "image" in properties:
831                self.show_args["image"] = properties.pop("image")
832
833        if "slow_abortable" in properties:
834            self.what_args["slow_abortable"] = properties.pop("slow_abortable")
835
836        prefixes = [ "show", "cb", "what", "window", "who"] + renpy.config.character_id_prefixes
837        split_args = [ i + "_" for i in prefixes ] + [ "" ]
838
839        split = renpy.easy.split_properties(properties, *split_args)
840
841        for prefix, d in zip(prefixes, split):
842            self.properties[prefix].update(d)
843
844        self.properties["who"].update(split[-1])
845
846        self.show_args.update(self.properties.pop("show"))
847        self.cb_args.update(self.properties.pop("cb"))
848        self.what_args.update(self.properties.pop("what"))
849        self.window_args.update(self.properties.pop("window"))
850        self.who_args.update(self.properties.pop("who"))
851
852    def copy(self, name=NotSet, **properties):
853        return type(self)(name, kind=self, **properties)
854
855    # This is called before the interaction.
856    def do_add(self, who, what, multiple=None):
857        return
858
859    # This is what shows the screen for a given interaction.
860    def do_show(self, who, what, multiple=None):
861
862        if multiple is not None:
863
864            return self.show_function(
865                who,
866                what,
867                who_args=self.who_args,
868                what_args=self.what_args,
869                window_args=self.window_args,
870                screen=self.screen,
871                properties=self.properties,
872                multiple=multiple,
873                **self.show_args)
874
875        else:
876
877            return self.show_function(
878                who,
879                what,
880                who_args=self.who_args,
881                what_args=self.what_args,
882                window_args=self.window_args,
883                screen=self.screen,
884                properties=self.properties,
885                **self.show_args)
886
887    # This is called after the last interaction is done.
888    def do_done(self, who, what, multiple=None):
889        self.add_history("adv", who, what, multiple=multiple)
890
891    # This is called when an extend occurs, before the usual add/show
892    # cycel.
893    def do_extend(self):
894        self.pop_history()
895
896    # This is called to actually do the displaying.
897    def do_display(self, who, what, **display_args):
898        display_say(who,
899                    what,
900                    self.do_show,
901                    **display_args)
902
903    # This is called to predict images that will be used by this
904    # statement.
905    def do_predict(self, who, what):
906        return self.predict_function(
907            who,
908            what,
909            who_args=self.who_args,
910            what_args=self.what_args,
911            window_args=self.window_args,
912            screen=self.screen,
913            properties=self.properties,
914            **self.show_args)
915
916    def resolve_say_attributes(self, predict, attrs, wanted=[], remove=[]):
917        """
918        Deals with image attributes associated with the current say
919        statement. Returns True if an image is shown, None otherwise.
920        """
921
922        if not (attrs or wanted or remove):
923            return
924
925        if not self.image_tag:
926            if attrs and not predict:
927                raise Exception("Say has image attributes %r, but there's no image tag associated with the speaking character." % (attrs,))
928            else:
929                return
930
931        if attrs is None:
932            attrs = ()
933        else:
934            attrs = tuple(attrs)
935
936        tagged_attrs = (self.image_tag,) + attrs
937        images = renpy.game.context().images
938
939        layer = renpy.config.tag_layer.get(self.image_tag, "master")
940
941        # If image is showing already, resolve it, then show or predict it.
942        if images.showing(layer, (self.image_tag,)):
943
944            new_image = images.apply_attributes(layer, self.image_tag, tagged_attrs, wanted, remove)
945
946            if new_image is None:
947                new_image = tagged_attrs
948
949            if images.showing(layer, new_image, exact=True):
950                return
951
952            show_image = (self.image_tag,) + attrs + tuple(wanted) + tuple("-" + i for i in remove)
953
954            if predict:
955                renpy.exports.predict_show(new_image)
956            else:
957                renpy.exports.show(show_image)
958                return True
959
960        else:
961
962            if renpy.config.say_attributes_use_side_image:
963
964                tagged_attrs = (renpy.config.side_image_prefix_tag,) + tagged_attrs
965
966                new_image = images.apply_attributes(layer, self.image_tag, tagged_attrs, wanted, remove)
967
968                if new_image is None:
969                    new_image = tagged_attrs
970
971                images.predict_show(layer, new_image[1:], show=False)
972
973            else:
974
975                # Otherwise, just record the attributes of the image.
976                images.predict_show(layer, tagged_attrs, show=False)
977
978    def handle_say_attributes(self, predicting, interact):
979
980        attrs = renpy.game.context().say_attributes
981        renpy.game.context().say_attributes = None
982
983        temporary_attrs = renpy.game.context().temporary_attributes
984        renpy.game.context().say_attributes = None
985
986        if interact:
987            if temporary_attrs:
988                temporary_attrs = list(temporary_attrs)
989            else:
990                temporary_attrs = [ ]
991
992            # Prepend speaking_attribute, if present. This allows it to
993            # be suppressed by a negative temporary_attr, if desired.
994            if renpy.config.speaking_attribute is not None:
995                temporary_attrs.insert(0, renpy.config.speaking_attribute)
996
997        images = renpy.game.context().images
998        before = images.get_attributes(None, self.image_tag)
999        mode = None
1000
1001        if self.resolve_say_attributes(predicting, attrs):
1002            mode = 'permanent'
1003
1004        # This is so late to give resolve_say_attributes time to do some
1005        # error handling.
1006        if not self.image_tag:
1007            return None
1008
1009        if temporary_attrs:
1010            attrs = images.get_attributes(None, self.image_tag)
1011
1012            if self.resolve_say_attributes(predicting, temporary_attrs):
1013                mode = 'both' if mode else 'temporary'
1014
1015        if mode:
1016            after = images.get_attributes(None, self.image_tag)
1017            self.handle_say_transition(mode, before, after)
1018
1019        if temporary_attrs:
1020            return (attrs, images)
1021
1022    def handle_say_transition(self, mode, before, after):
1023
1024        before = set(before)
1025        after = set(after)
1026
1027        if before == after:
1028            return
1029
1030        if renpy.config.say_attribute_transition_callback_attrs:
1031            delta = (before, after)
1032        else:
1033            delta = ()
1034
1035        trans, layer = renpy.config.say_attribute_transition_callback(
1036            self.image_tag, mode, *delta)
1037        if trans is not None:
1038            if layer is None:
1039                renpy.exports.with_statement(trans)
1040            else:
1041                renpy.exports.transition(trans, layer=layer)
1042
1043    def restore_say_attributes(self, predicting, state, interact):
1044
1045        if state is None:
1046            return
1047
1048        attrs, images = state
1049
1050        if not self.image_tag:
1051            return
1052
1053        # This is False when the context changes.
1054        if images is not renpy.game.context().images:
1055            return
1056
1057        current_attrs = images.get_attributes(None, self.image_tag)
1058
1059        if attrs == current_attrs:
1060            return
1061
1062        image_with_attrs = (self.image_tag,) + attrs + tuple("-" + i for i in current_attrs if i not in attrs)
1063
1064        if images.showing(None, (self.image_tag,)):
1065
1066            if not predicting:
1067                renpy.exports.show(image_with_attrs)
1068                return True
1069            else:
1070                renpy.exports.predict_show(image_with_attrs)
1071
1072        else:
1073            images.predict_show(None, image_with_attrs, show=False)
1074
1075    def __unicode__(self):
1076
1077        who = self.name
1078
1079        if self.dynamic:
1080            if callable(who):
1081                who = who()
1082            else:
1083                who = renpy.python.py_eval(who)
1084
1085        return renpy.substitutions.substitute(who)[0]
1086
1087    def __str__(self):
1088
1089        who = self.name
1090
1091        if self.dynamic:
1092            if callable(who):
1093                who = who()
1094            else:
1095                who = renpy.python.py_eval(who)
1096
1097        rv = renpy.substitutions.substitute(who)[0]
1098
1099        if PY2:
1100            rv = rv.encode("utf-8")
1101
1102        return rv
1103
1104    def __format__(self, spec):
1105        return format(str(self), spec)
1106
1107    def __repr__(self):
1108        return "<Character: {!r}>".format(self.name)
1109
1110    def empty_window(self):
1111        if renpy.config.fast_empty_window and (self.name is None) and not (self.what_prefix or self.what_suffix):
1112            self.do_show(None, "")
1113            return
1114
1115        self("", interact=False, _call_done=False)
1116
1117    def has_character_arguments(self, **kwargs):
1118        """
1119        Returns True if `kwargs` contains any keyword arguments that will
1120        cause the creation of a new Character object and the proxying of a
1121        call to that Character object, and False otherwise.
1122        """
1123
1124        safe_kwargs_keys = { "interact", "_mode", "_call_done", "multiple", "_with_none" }
1125
1126        for i in kwargs:
1127            if i not in safe_kwargs_keys:
1128                return False
1129
1130        return True
1131
1132    def prefix_suffix(self, thing, prefix, body, suffix):
1133
1134        def sub(s, scope=None, force=False, translate=True):
1135            return renpy.substitutions.substitute(s, scope=scope, force=force, translate=translate)[0]
1136
1137        thingvar_quoted = "[[" + thing + "]"
1138        thingvar = "[" + thing + "]"
1139
1140        if not renpy.config.new_substitutions:
1141            return prefix + body + suffix
1142
1143        # Used before Ren'Py 7.4.
1144        elif renpy.config.who_what_sub_compat == 0:
1145            pattern = sub(prefix + thingvar_quoted + suffix)
1146            return pattern.replace(thingvar, sub(body))
1147
1148        # Used from Ren'Py 7.4 to Ren'Py 7.4.4
1149        elif renpy.config.who_what_sub_compat == 1:
1150            pattern = sub(sub(prefix) + thingvar_quoted + sub(suffix))
1151            return pattern.replace(thingvar, sub(body))
1152
1153        # 7.4.5 on.
1154        else:
1155            return (sub(prefix) + sub(body) + sub(suffix))
1156
1157    def __call__(self, what, interact=True, _call_done=True, multiple=None, **kwargs):
1158
1159        _mode = kwargs.pop("_mode", None)
1160        _with_none = kwargs.pop("_with_none", None)
1161
1162        if kwargs:
1163            return Character(kind=self, **kwargs)(what, interact=interact, _call_done=_call_done, multiple=multiple, _mode=_mode, _with_none=_with_none)
1164
1165        # Check self.condition to see if we should show this line at all.
1166        if not (self.condition is None or renpy.python.py_eval(self.condition)):
1167            return True
1168
1169        if not isinstance(what, basestring):
1170            raise Exception("Character expects its what argument to be a string, got %r." % (what,))
1171
1172        # Figure out multiple and final. Multiple is None if this is not a multiple
1173        # dialogue, or a step and the total number of steps in a multiple interaction.
1174
1175        global multiple_count
1176
1177        if multiple is None:
1178            multiple_count = 0
1179
1180        else:
1181            multiple_count += 1
1182            multiple = (multiple_count, multiple)
1183
1184            if multiple_count == multiple[1]:
1185                multiple_count = 0
1186
1187        if multiple is None:
1188
1189            old_attr_state = self.handle_say_attributes(False, interact)
1190
1191            old_side_image_attributes = renpy.store._side_image_attributes
1192
1193            if self.image_tag:
1194                attrs = (self.image_tag,) + renpy.game.context().images.get_attributes(None, self.image_tag)
1195            else:
1196                attrs = None
1197
1198            renpy.store._side_image_attributes = attrs
1199
1200            if not interact:
1201                renpy.store._side_image_attributes_reset = True
1202
1203        if renpy.config.voice_tag_callback is not None:
1204            renpy.config.voice_tag_callback(self.voice_tag)
1205
1206        try:
1207
1208            if interact:
1209                mode = _mode or self.mode
1210                renpy.exports.mode(mode)
1211            else:
1212                renpy.game.context().deferred_translate_identifier = renpy.game.context().translate_identifier
1213
1214            # Figure out the arguments to display.
1215            display_args = self.display_args.copy()
1216            display_args["interact"] = display_args["interact"] and interact
1217
1218            if multiple is not None:
1219                display_args["multiple"] = multiple
1220
1221            if _with_none is not None:
1222                display_args["with_none"] = _with_none
1223
1224            who = self.name
1225
1226            # If dynamic is set, evaluate the name expression.
1227            if self.dynamic:
1228                if callable(who):
1229                    who = who()
1230                else:
1231                    who = renpy.python.py_eval(who)
1232
1233            if who is not None:
1234                who = self.prefix_suffix("who", self.who_prefix, who, self.who_suffix)
1235
1236            what = self.prefix_suffix("what", self.what_prefix, what, self.what_suffix)
1237
1238            # Run the add_function, to add this character to the
1239            # things like NVL-mode.
1240
1241            if multiple is not None:
1242                self.do_add(who, what, multiple=multiple)
1243            else:
1244                self.do_add(who, what)
1245
1246            dtt = DialogueTextTags(what)
1247
1248            # Now, display the damned thing.
1249            self.do_display(who, what, cb_args=self.cb_args, dtt=dtt, **display_args)
1250
1251            # Indicate that we're done.
1252            if _call_done and not dtt.has_done:
1253
1254                if multiple is not None:
1255                    self.do_done(who, what, multiple=multiple)
1256                else:
1257                    self.do_done(who, what)
1258
1259                # Finally, log this line of dialogue.
1260                if who and isinstance(who, basestring):
1261                    renpy.exports.log(who)
1262
1263                renpy.exports.log(what)
1264                renpy.exports.log("")
1265
1266        finally:
1267
1268            if (multiple is None) and interact:
1269                renpy.store._side_image_attributes = old_side_image_attributes
1270
1271                if old_attr_state is not None:
1272                    _, images = old_attr_state
1273                    before = images.get_attributes(None, self.image_tag)
1274
1275                if self.restore_say_attributes(False, old_attr_state, interact):
1276                    after = images.get_attributes(None, self.image_tag)
1277                    self.handle_say_transition('restore', before, after)
1278
1279    def statement_name(self):
1280        if self._statement_name is not None:
1281            return self._statement_name
1282        elif not (self.condition is None or renpy.python.py_eval(self.condition)):
1283            return "say-condition-false"
1284        else:
1285            return "say"
1286
1287    def predict(self, what):
1288
1289        old_attr_state = self.handle_say_attributes(True, True)
1290
1291        old_side_image_attributes = renpy.store._side_image_attributes
1292
1293        if self.image_tag:
1294            attrs = (self.image_tag,) + renpy.game.context().images.get_attributes("master", self.image_tag)
1295        else:
1296            attrs = None
1297
1298        renpy.store._side_image_attributes = attrs
1299
1300        try:
1301
1302            if self.dynamic:
1303                who = "<Dynamic>"
1304            else:
1305                who = self.name
1306
1307            return self.do_predict(who, what)
1308
1309        finally:
1310            renpy.store._side_image_attributes = old_side_image_attributes
1311            self.restore_say_attributes(True, old_attr_state, True)
1312
1313    def will_interact(self):
1314
1315        if not (self.condition is None or renpy.python.py_eval(self.condition)):
1316            return False
1317
1318        return self.display_args['interact']
1319
1320    def add_history(self, kind, who, what, multiple=None, **kwargs):
1321        """
1322        This is intended to be called by subclasses of ADVCharacter to add
1323        History entries to _history_list.
1324        """
1325
1326        history_length = renpy.config.history_length
1327
1328        if history_length is None:
1329            return
1330
1331        if not renpy.store._history: # @UndefinedVariable
1332            return
1333
1334        history = renpy.store._history_list # @UndefinedVariable
1335
1336        h = HistoryEntry()
1337
1338        h.kind = kind
1339
1340        h.who = who
1341        h.what = what
1342
1343        h.who_args = self.who_args
1344        h.what_args = self.what_args
1345        h.window_args = self.window_args
1346        h.show_args = self.show_args
1347
1348        h.image_tag = self.image_tag
1349
1350        h.multiple = multiple
1351
1352        if renpy.game.context().rollback:
1353            h.rollback_identifier = renpy.game.log.current.identifier
1354        else:
1355            h.rollback_identifier = None
1356
1357        for k, v in kwargs.items():
1358            setattr(h, k, v)
1359
1360        for i in renpy.config.history_callbacks:
1361            i(h)
1362
1363        history.append(h)
1364
1365        while len(history) > history_length:
1366            history.pop(0)
1367
1368    def pop_history(self):
1369        """
1370        This is intended to be called by do_extend to remove entries from
1371        _history_list.
1372        """
1373
1374        history_length = renpy.config.history_length
1375
1376        if history_length is None:
1377            return
1378
1379        if not renpy.store._history: # @UndefinedVariable
1380            return
1381
1382        # The history can be reset at any time, so check that we have some.
1383        if renpy.store._history_list:
1384            renpy.store._history_list.pop() # @UndefinedVariable
1385
1386
1387def Character(name=NotSet, kind=None, **properties):
1388    """
1389    :doc: character
1390    :args: (name=..., kind=adv, **args)
1391    :name: Character
1392
1393    Creates and returns a Character object, which controls the look
1394    and feel of dialogue and narration.
1395
1396    `name`
1397        If a string, the name of the character for dialogue. When
1398        `name` is None, display of the name is omitted, as for
1399        narration. If no name is given, the name is taken from
1400        `kind`, and otherwise defaults to None.
1401
1402    `kind`
1403        The Character to base this Character off of. When used, the
1404        default value of any argument not supplied to this Character
1405        is the value of that argument supplied to ``kind``. This can
1406        be used to define a template character, and then copy that
1407        character with changes.
1408
1409    **Linked Image.**
1410    An image tag may be associated with a Character. This allows a
1411    say statement involving this character to display an image with
1412    the tag, and also allows Ren'Py to automatically select a side
1413    image to show when this character speaks.
1414
1415    `image`
1416         A string giving the image tag that is linked with this
1417         character.
1418
1419    **Voice Tag.**
1420    If a voice tag is assign to a Character, the voice files that are
1421    associated with it, can be muted or played in the preference
1422    screen.
1423
1424    `voice_tag`
1425        A String that enables the voice file associated with the
1426        Character to be muted or played in the 'voice' channel.
1427
1428    **Prefixes and Suffixes.**
1429    These allow a prefix and suffix to be applied to the name of the
1430    character, and to the text being shown. This can be used, for
1431    example, to add quotes before and after each line of dialogue.
1432
1433    `what_prefix`
1434        A string that is prepended to the dialogue being spoken before
1435        it is shown.
1436
1437    `what_suffix`
1438        A string that is appended to the dialogue being spoken before
1439        it is shown.
1440
1441    `who_prefix`
1442        A string that is prepended to the name of the character before
1443        it is shown.
1444
1445    `who_suffix`
1446        A string that is appended to the name of the character before
1447        it is shown.
1448
1449    **Changing Name Display.**
1450    These options help to control the display of the name.
1451
1452    `dynamic`
1453        If true, then `name` should either be a string containing a Python
1454        expression, a function, or a callable object. If it's a string,
1455        That string will be evaluated before each line of dialogue, and
1456        the result used as the name of the character. Otherwise, the
1457        function or callable object will be called with no arguments
1458        before each line of dialogue, and the return value of the call will
1459        be used as the name of the character.
1460
1461    **Controlling Interactions.**
1462    These options control if the dialogue is displayed, if an
1463    interaction occurs, and the mode that is entered upon display.
1464
1465    `condition`
1466        If given, this should be a string containing a Python
1467        expression. If the expression is false, the dialogue
1468        does not occur, as if the say statement did not happen.
1469
1470    `interact`
1471        If true, the default, an interaction occurs whenever the
1472        dialogue is shown. If false, an interaction will not occur,
1473        and additional elements can be added to the screen.
1474
1475    `advance`
1476        If true, the default, the player can click to advance through
1477        the statement, and other means of advancing (such as skip and
1478        auto-forward mode) will also work. If false, the player will be
1479        unable to move past the say statement unless an alternate means
1480        (such as a jump hyperlink or screen) is provided.
1481
1482    `mode`
1483        A string giving the mode to enter when this character
1484        speaks. See the section on :ref:`modes <modes>` for more details.
1485
1486    `callback`
1487        A function that is called when events occur while the
1488        character is speaking. See the section on
1489        :ref:`character-callbacks` fore more information.
1490
1491    **Click-to-continue.**
1492    A click-to-continue indicator is displayed once all the text has
1493    finished displaying, to prompt the user to advance.
1494
1495    `ctc`
1496        A displayable to use as the click-to-continue indicator, unless
1497        a more specific indicator is used.
1498
1499    `ctc_pause`
1500        A displayable to use a the click-to-continue indicator when the
1501        display of text is paused by the {p} or {w} text tags.
1502
1503    `ctc_timedpause`
1504        A displayable to use a the click-to-continue indicator when the
1505        display of text is paused by the {p=} or {w=} text tags. When
1506        None, this takes its default from `ctc_pause`, use ``Null()``
1507        when you want a `ctc_pause` but no `ctc_timedpause`.
1508
1509    `ctc_position`
1510        Controls the location of the click-to-continue indicator. If
1511        ``"nestled"``, the indicator is displayed as part of the text
1512        being shown, immediately after the last character. ``"nestled-close"`` is
1513        similar, except a break is not allowed between the text and the CTC
1514        indicator. If ``"fixed"``,
1515        the indicator is added to the screen, and its position is
1516        controlled by the position style properties.
1517
1518
1519    **Screens.**
1520    The display of dialogue uses a :ref:`screen <screens>`. These arguments
1521    allow you to select that screen, and to provide arguments to it.
1522
1523    `screen`
1524        The name of the screen that is used to display the dialogue.
1525
1526    Keyword arguments beginning with ``show_`` have the prefix
1527    stripped off, and are passed to the screen as arguments. For
1528    example, the value of ``show_myflag`` will become the value of
1529    the ``myflag`` variable in the screen. (The ``myflag`` variable isn't
1530    used by default, but can be used by a custom say screen.)
1531
1532    One show variable is, for historical reasons, handled by Ren'Py itself:
1533
1534    `show_layer`
1535        If given, this should be a string giving the name of the layer
1536        to show the say screen on.
1537
1538    **Styling Text and Windows.**
1539    Keyword arguments beginning with ``who_``, ``what_``, and
1540    ``window_`` have their prefix stripped, and are used to :ref:`style
1541    <styles>` the character name, the spoken text, and the window
1542    containing both, respectively.
1543
1544    For example, if a character is given the keyword argument
1545    ``who_color="#c8ffc8"``, the color of the character's name is
1546    changed, in this case to green. ``window_background="frame.png"``
1547    sets the background of the window containing this character's
1548    dialogue.
1549
1550    The style applied to the character name, spoken text, and window
1551    can also be set this way, using the ``who_style``, ``what_style``, and
1552    ``window_style`` arguments, respectively.
1553
1554    Setting :var:`config.character_id_prefixes` makes it possible to style
1555    other displayables as well. For example, when the default GUI is used,
1556    styles prefixed with ``namebox_`` are used to style the name of the
1557    speaking character.
1558    """
1559
1560    if kind is None:
1561        kind = renpy.store.adv
1562
1563    return type(kind)(name, kind=kind, **properties)
1564
1565
1566def DynamicCharacter(name_expr, **properties):
1567    return Character(name_expr, dynamic=True, **properties)
1568