Page 2 of 2

Re: Custom dialogue boxes not being cleared when scene or menu statements are used

Posted: Thu Jul 21, 2022 3:31 pm
by m_from_space
Thanks for the testing grounds, that was helpful. Still took some time to figure out how to fix those annoying little flashes. But as far as I can see, I have a solution. No need for special treatment of "scene" or "menu" necessary.

Code: Select all

# tells us if the background should now fade out (triggered via say screen)
default say_end = False
default say_who = None
init python:
    from functools import partial
    def chartextbg(char, event, *args, **kwargs):
        if event == "begin":
            store.say_end = False
            store.say_who = char
        return

    def charBg(st, at):
        global say_who
        if say_who is None:
            return Null(), None
        d = AlphaMask(Fixed(At("images/gui/{}_bg.png".format(say_who), dialoguepan)), At("images/gui/dialoguemask.png", maskpos))
        return d, None

transform bg_fadein:
    on appear:
        alpha 0.0
        linear 0.2 alpha 1.0
    on show:
        alpha 0.0
        linear 0.2 alpha 1.0
    on hide:
        alpha 1.0
        linear 0.2 alpha 0.0

transform bg_fadeout:
    on appear:
        alpha 0.0
    on show:
        alpha 1.0
        linear 0.2 alpha 0.0
    on hide:
        alpha 0.0

screen backgrounds:
    # use showif instead of if to make those neat transforms and their events work
    showif say_end:
        frame at bg_fadeout:
            background "bg_chars"
    else:
        frame at bg_fadein:
            background "bg_chars"
In screens.rpy add a line to the say screen:

Code: Select all

screen say(who, what):
    on "hide" action SetVariable("say_end", True)
    ...

Re: Custom dialogue boxes not being cleared when scene or menu statements are used

Posted: Sat Jul 23, 2022 5:30 am
by makorays
Oh my god it works, thank you so much. Now the only time I ever see any weirdness is a very tiny flash of the wrong color during rollback, but rollback kinda just makes everything weird and it's something people would very rarely see so I can't possibly be bothered by it. I didn't even know showif was a thing and its documentation is making my head hurt trying to understand it but that's okay.

If anyone is stumbling onto this thread from google or whatever, here's all the relevant code for custom animated dialogue boxes on a per-character basis, with some example characters:
At the top of your main script file (or wherever else works I guess):

Code: Select all

transform maskpos:
    pos (0.0, 0.75)
transform dialoguepan:
    animation
    align (0.0, 1.0)
    block:
        offset (0, 0)
        linear 3.0 offset (-150, 80)
    repeat

image bg_chars = DynamicDisplayable(charBg)

# tells us if the background should now fade out (triggered via say screen)
default say_end = False
default say_who = None
init python:
    from functools import partial
    def chartextbg(char, event, *args, **kwargs):
        if event == "begin":
            store.say_end = False
            store.say_who = char
        return

    def charBg(st, at):
        global say_who
        if say_who is None:
            return Null(), None
        d = AlphaMask(Fixed(At("images/gui/{}_bg.png".format(say_who), dialoguepan)), At("images/gui/dialoguemask.png", maskpos))
        return d, None

transform bg_fadein:
    on appear:
        alpha 0.0
        linear 0.2 alpha 1.0
    on show:
        alpha 0.0
        linear 0.2 alpha 1.0
    on hide:
        alpha 1.0
        linear 0.2 alpha 0.0

transform bg_fadeout:
    on appear:
        alpha 0.0
    on show:
        alpha 1.0
        linear 0.2 alpha 0.0
    on hide:
        alpha 0.0

screen backgrounds:
    # use showif instead of if to make those neat transforms and their events work
    showif say_end:
        frame at bg_fadeout:
            background "bg_chars"
    else:
        frame at bg_fadein:
            background "bg_chars"

# example characters
define narrator = Character(None, callback=partial(chartextbg, "narrator"))
define generic = Character("generic", callback=partial(chartextbg, "narrator"))

define m = Character("orange lass", callback=partial(chartextbg, "orange lass"))
define b = Character("blue fella", callback=partial(chartextbg, "blue fella"))

define bl = Character("Background Lady", kind=generic)
define bd = Character("Background Dude", kind=generic)
And then in screens.rpy, add this to the top of the say screen:

Code: Select all

screen say(who, what):
    on "hide" action SetVariable("say_end", True)
...and under "style window:", comment this out:

Code: Select all

#background Image("gui/textbox.png", xalign=0.5, yalign=1.0)
And then you can shove pictures like these into your game, adding "_bg.png" after character names, along with a dialoguemask.png in the shape you want your dialogue box to be:

Image

For the record: this project is 1920x1080 pixels, dialoguemask.png is 1920x270, and the character-specific backgrounds are 2070x350 (so they have room to slide diagonally and reset). You might have to adjust some numbers in the two transforms at the top of the code if yours are different.

Thanks again, I doubt I could've done this without your help. Hopefully other people will see this if they're also trying to do this.

Re: Custom dialogue boxes not being cleared when scene or menu statements are used

Posted: Sat Jul 23, 2022 7:05 am
by m_from_space
Glad to hear it works out. And yes, rollback is weird anyway, so. :) Wouldn't allow my players to roll back. I like that you provided this code summary.

Re: Custom dialogue boxes not being cleared when scene or menu statements are used

Posted: Thu Jul 28, 2022 9:28 am
by makorays
Oh dammit turns out we're not quite done here.

I just noticed that whenever I use the {w} text tag in the middle of a dialogue line, the box glitches out. When I click to continue past the {w}, the dialogue box seems to double itself up on the next frame and then fades away, leaving a blank background for the rest of the dialogue. Of course, I'd want it to instead do nothing until the whole line is complete.

Any idea why this is happening? I tried to figure out how exactly the wait text tag works or why it's doing this but the documentation is pretty bare-bones on it.
"The wait tag is a self-closing tag that waits for the user to click to continue. If it is given an argument, the argument is interpreted as a number, and the wait automatically ends after that many seconds have passed."

It's like it's causing a new say statement to start and then instantly end, or something. I'm not quite sure how to work around that. Is there a way to tell the code "if wait tag is used, don't do all this stuff?"

I tried messing around with the "interact" argument mentioned here, but haven't yet gotten anything to work properly. https://www.renpy.org/doc/html/character_callbacks.html

Re: Custom dialogue boxes not being cleared when scene or menu statements are used

Posted: Sat Oct 29, 2022 8:09 am
by makorays
Hey, bumping this in case anyone sees it who might be able to help me, it's been 3 months and I'm still at a loss. Maybe it's just my lack of programming skills, I dunno, but this is one of those things I have no idea how to figure out on my own.

EDIT:
Okay, I asked on the renpy discord and someone by the name of "Fen (Feniks)" came up with an alternative solution for me, which happens to be a lot simpler and works with existing systems rather than replacing them. All it really does is make use of the "window_background" parameter for character definitions...plus a bunch of extra things to make it work properly. No shade to m_from_space, I'm still very grateful for their help, it just didn't QUITE work perfectly, and as far as I can tell this solution does.

As per usual, I barely understand how any of this code works, but I want to include it here for posterity (and so this thread doesn't become one of those google results with an almost-but-not-quite solved problem).

So first of all, this stuff makes it so dialogue boxes fade out and in properly during menu/scene transitions, rather than flashing to the narrator's background:

Code: Select all

#make it so when an empty dialogue line is said (such as when a menu or scene change happens) it fades out using the previous character's background rather than the narrator
init python:
    def my_empty_window():
        try:
            who = _last_say_who
            who = renpy.eval_who(who)
        except:
            who = None
        if who is None:
            who = narrator
        if isinstance(who, NVLCharacter):
            nvl_show_core()
        elif isinstance(who, ADVCharacter):
            who.empty_window()
        elif isinstance(store._narrator, ADVCharacter):
            store._narrator.empty_window()
        
#and then this stuff makes it so it fades back in using the appropriate dialogue background
#this part was just copied from 00library.rpy and the two "store._narrator"s in the elif were changed to "who"
        try:
            scry = renpy.scry()
            # When running in a say statement or menu-with-caption, scry for
            # the next say statement, and get the window from that.
            if scry.say or scry.menu_with_caption:
                who = None
                for i in range(10):
                    if scry.say:
                        who = scry.who
                        break
                    scry = scry.next()
            else:
                who = _last_say_who
                who = renpy.eval_who(who)
        except Exception:
            who = None
        if who is None:
            who = narrator
        if isinstance(who, NVLCharacter):
            nvl_show_core()
        elif not isinstance(who, NVLCharacter):
            who.empty_window()
        else:
            store._narrator.empty_window()
        
    config.empty_window = my_empty_window
Then here's the transform for the scrolling dialogue box background, set up as a function which uses some time shenanigans to make it not reset itself on every interaction. This is still using the same image dimensions I described above, so change the numbers around if needed.

Code: Select all

#dialogue box pan animation
init python:
    import time # so it works on real time not the shown timebase
    def tile_function(trans, st, at):
        trans.align = (0.0, 1.0)
        # 3.0 is how long it takes to move the image to a new tile spot
        t = time.time() % 3.0 / 3.0
        trans.xoffset = int(t*-150)
        trans.yoffset = int(t*80)
        # immediate refresh
        return 0

transform move_tile():
    function tile_function
And here's some example characters, complete with some non-relevant code that I'm including in case I ever have to refer back here for something. The namebox_background in the generic character is something that I apparently need for generic character nameboxes to work properly? I guess? That's a separate topic but I'm including it anyway. I don't remember when I added that but the namebox breaks if I get rid of it.

Code: Select all

define narrator = Character(None, window_background=AlphaMask(Fixed(At("images/gui/Narrator_bg.png", move_tile)), "images/gui/dialoguemask.png"))
define generic = Character("generic", namebox_background = Frame("Generic_nbox", gui.namebox_borders, tile=gui.namebox_tile, xalign=gui.name_xalign),
window_background=AlphaMask(Fixed(At("images/gui/Narrator_bg.png", move_tile)), "images/gui/dialoguemask.png"))

define m = Character("orange lass", window_background=AlphaMask(Fixed(At("images/gui/orange lass_bg.png", move_tile)), "images/gui/dialoguemask.png"), image='m')
define e = Character("blue fella", window_background=AlphaMask(Fixed(At("images/gui/blue fella_bg.png", move_tile)), "images/gui/dialoguemask.png"), image='b')

define bl = Character("Background Lady", kind=generic)
define bd = Character("Background Dude", kind=generic)
You can leave screens.rpy how it was by default rather than making any alterations to it, and simply shove all that code into your main script file or whatever.
There's probably a way to more efficiently define those characters rather than repeating those long window_background segments for each one but I tried and couldn't figure out how, so if anyone sees this and knows a way feel free to share.