renpy dialogue looks like this
define lexy = Character("Lexy")
lexy "Hello, it's me, Lexy the fox! Yip yip!"
and this is displayed on the say screen, which looks like this
screen say(who, what):
style_prefix "say"
window:
id "window"
if who is not None:
window:
id "namebox"
style "namebox"
text who id "who"
text what id "what"
and there are ten billion kwargs you can pass to Character that will override little bits and pieces of this screen. which is cool and all; it means newcomers can do rather a lot of customization without having to know anything about how screens work.
but sometimes
you want to throw it all out and do a completely custom screen that does clever things. things that can't be readily expressed as a single style property override, perhaps.
and then you may run into a problem that sounds completely bananas: how do i know who's talking?? who is a string, and necessarily must be a string, because it might be interpolated or dynamic or whathaveyou. (for what i can only hope are ancient compatibility reasons, you don't need to use Characters at all; your speakers can just be strings that are their names.) it seems like the original Character is lost somewhere along the way. how can we possibly get it back?
and actually the solution is so straightforward that i'm slightly embarrassed to be writing it out now, but it took me a while to stumble upon it so here you go:
if you give show_foo kwargs to Character, then foo will be passed as a kwarg to the say screen. so all you have to do is feed the speaker to itself. you can't do that directly, of course, since the Character doesn't exist at the point where you're calling its constructor, but you can just stuff it in afterwards:
define lexy = Character("Lexy")
init python:
lexy.show_args['speaker'] = lexy
# ...
screen say(who, what, speaker=None):
# do something extraordinarily clever with speaker
of course you will probably want a wrapper function or type so you don't have to keep doing this by hand. if you use a wrapper type, you can even pass that instead, and get access to whatever game-specific stuff you have on there:
class Actor:
def __init__(self, *args, power, wisdom, **kwargs):
self.power = power
self.wisdom = wisdom
kwargs['show_speaker'] = self
self.character = Character(*args, **kwargs)
# presumably there is a __call__ down here...
if you use NVL, this is slightly more annoying. you do need to add the speaker kwarg to the nvl screen, but remember this is actually the last speaker (i think?), whereas the nvl screen is responsible for rendering an arbitrary number of lines from arbitrary speakers. so you'll need to smuggle the speaker into each of those entries. the least invasive way i've thought of to do this is:
# this will add support for passing nvl_foo kwargs to Character
init python:
config.character_id_prefixes.append('nvl')
# ...
class Actor:
def __init__(self, *args, **kwargs):
kwargs['show_speaker'] = self
kwargs['nvl_speaker'] = self # <-- new
self.character = Character(*args, **kwargs)
and now you can get at the speaker via d.properties['nvl']['speaker']. which is, admittedly, not great, but it's a significant improvement on "impossible"