Using Ren'Py tags to format text

JustXThings

Member
Game Developer
May 22, 2022
210
1,249
Can I somehow use the Ren'Py side image tagging system to format my text automatically w/o having to write the format in every sentence.

a "Hi"
a "*{i}Hi{/i}*"

Example... say I have character Agnes. Agnes can think, shout, whisper or talk.

I usually write like this:

define a = Character("Agnes"..., image="mood")

a "Hi" # say
a thinks "Gosh... look at this guy." # thinks
a whispers "I am sorry.." # whispers

I have side images for "mood" (bubble, think bubble, whisper bubble, etc). But that is an issue since the bubbles have a color and depending on the scene are not that visible.

I'd like to detect in the say screen that I tag the text as thinks and assign a different style, for instance italics. Smaller / Bigger font, etc depending on whisper / shout.

I know I can modify the Ren'Py engine, but I'd rather prefer doing something in the provided game rpy.

Any ideas ?
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
Given your example, my suggestion would be to have 4 characters called "Agnes".

I've posted about this sort of stuff before:
https://f95zone.to/threads/write-the-text-always-in-italic.122138/#post-8446564

But the short version would be:

Python:
define a = Character('Agnes')
define a_t = Character('Agnes', what_prefix='{i}(', what_suffix='){/i}')
define a_w = Character('Agnes', what_prefix='{size=16}', what_suffix='{/size}')
define a_s = Character('Agnes', what_prefix='{b}*', what_suffix='*{/b}')

Where:
  • a is for normal speech.
  • a_t is for (thinking).
  • a_w is for whispering.
  • a_s is for **shouting!!!**

Of course, I'd add a few more embellishments...

Python:
define narrator = Character('', what_color='#FFFF66')

define a = Character('Agnes', color='#c193c9', what_prefix='"', what_suffix='"')
define a_t = Character('Agnes', color='#c193c9', what_prefix='{i}(', what_suffix='){/i}', what_color='#999999')
define a_w = Character('Agnes', color='#c193c9', what_prefix='"{size=20}', what_suffix='{/size}"')
define a_s = Character('Agnes', color='#c193c9', what_prefix='"{b}**', what_suffix='**{/b}"')

Which are pretty much to put double quotes around any spoken text. To color none-character (narrator) text as a light yellow and to color thoughts to be a pale gray rather than pure white.

Do the same for other characters you want to behave this way. Including characters where you might want to change the character's name...

Python:
default agnes_name = "Unknown"

define narrator = Character('', what_color='#FFFF66')

define a = Character('[agnes_name]', color='#c193c9', what_prefix='"', what_suffix='"')
define a_t = Character('[agnes_name]', color='#c193c9', what_prefix='{i}(', what_suffix='){/i}', what_color='#999999')
define a_w = Character('[agnes_name]', color='#c193c9', what_prefix='"{size=20}', what_suffix='{/size}"')
define a_s = Character('[agnes_name]', color='#c193c9', what_prefix='"{b}**', what_suffix='**{/b}"')

label start:

    scene black

    $ agnes_name = renpy.input("What is your female friend's name? (Default is 'Agnes')", exclude="[]{}")
    $ agnes_name = agnes_name.strip() or "Agnes"

    a "Hello. My name is [a]."
    a_w "Nice to meet you"

    "*** THE END ***"
    return
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
I'd also add that "shouting" and "whispering" are kinda rare throughout a VN story.
Maybe rare enough that it's not worth adding extra characters and instead just stick with using text tags on those occasions.

A full list of the text tags can be found here:


Other useful text tags are:

{w} (Wait). Although I would always specify a time, like {w=2}.

Handy for stuff like "..." ".{w=0.3}.{w=0.3}.{w=1}" or to pause mid conversation "Wait...{w=1}You SAW that!?!!??!".

{p} (Paragraph). Again, I would always specify a time... although time=0 is useful for this one, like {p=0}.

Paragraph can be used to split a line of dialogue onto two (or more) lines.
Think: "Wait...{p=1}You SAW that!?!!??!" for...
Wait...
You SAW that?!!??!


Without a time, both tags will wait forever (or until the player clicks the mouse or presses a key) - which is why I would always specify a time.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
And now I've read your original message again, I think you're coming at "mood" from the wrong direction.

Python:
default agnes_name = "Unknown"

define narrator = Character('', what_color='#FFFF66')

define a = Character('[agnes_name]', image="agnespic", color='#c193c9', what_prefix='"', what_suffix='"')
define a_t = Character('[agnes_name]', image="agnespic", color='#c193c9', what_prefix='{i}(', what_suffix='){/i}', what_color='#999999')
define a_w = Character('[agnes_name]', image="agnespic", color='#c193c9', what_prefix='"{size=20}', what_suffix='{/size}"')
define a_s = Character('[agnes_name]', image="agnespic", color='#c193c9', what_prefix='"{b}**', what_suffix='**{/b}"')

image side agnespic = "agnes_side_neutral.png"
image side agnespic happy = "agnes_side_happy.png"
image side agnespic sad = "agnes_side_sad.png"
image side agnespic confused = "agnes_side_confused.png"
image side agnespic angry = "agnes_side_angry.png"

label start:

    scene black

    $ agnes_name = renpy.input("What is your female friend's name? (Default is 'Agnes')", exclude="[]{}")
    $ agnes_name = agnes_name.strip() or "Agnes"

    a "Hello. My name is [a]."
    a_w confused "Nice to meet you"
    a_s angry "NOW FUCK OFF AND LEAVE ME ALONE!!!"

    "*** THE END ***"
    return

Normally the parameter image="agnespic" would be written as image="agnes", then the side images would be called agnes instead of agnespic. And I would suggest doing it that way. I only coded it like this to highlight the connection between the image= statement and the image side statements. With so many "Agnes" as parts of names, etc, new developers sometimes don't make the connection, so I'm coding it badly to highlight it.

The actual file names can be anything you like. Filenames like agnes_side_sad.png make sense to me. But you could put all the side images in a \game\images\sides\ subfolder for example and use stuff like image side agnes sad = "sides/agnes_sad.png".

To confuse matters further, these things are a mix of and are a completely separate animal from the "text tags" we were talking about early.

In my examples, "happy", "sad", "confused" and "angry" are the attributes and "agnespic" is the image tag (Edit: Actually "side" is the image tag and "agnespic" is just another attribute... but your get my point). Image tags/attributes are a way of grouping images together in such a way that only one version of them can be displayed on screen at a time (set by a combination of the tag and the attribute). Which is handy for side images, where you want to swap between different moods. A single attribute is recommended when you are just starting out, but you can get really creative with attributes for mood, clothing, lighting combinations (and don't do it... it's a nightmare).

The end result is that you add the attribute to the dialogue code and it automatically swaps to the correct side image.
 
Last edited:

JustXThings

Member
Game Developer
May 22, 2022
210
1,249
Thanks ! I initially started doing the VN like you propose. With multiple characters depending on the mood. But somehow it always felt 'weird' that I can tag the text and it reflects in an appropriate side image, while I cannot use this very same tags for categorizing my text. I understand that those tags are a side image thing, but I hoped that I could somewhat detect which side image is about to pop "side mood thinks" and present the say text with the appropriate think style. Similarly to how narrator and actor are already differentiated in the say screen depending on 'who' being set.

I only have one side image per mood for all the characters, since the whole point was to differentiate what is the mood of the character. I thought about doing that using comic style bubbles which looked like a nice idea at first, but soon enough I noticed that bubbles were hardly visible depending on the background colors.

I had something like...

define a = Character('Agnes', ..., image='mood')
define b = Character('Rebeca', ..., image='mood')
...

So, since I have the whole VN written as... a thinks... a whispers... etc. I was hoping I could just make some renpy.xyz call to retrieve either the tags, or the name of the side image from the say screens and do the magic there.

I mean, I have already done that directly modifiying the Ren'Py files since the license allows it and calling the say screen directly with the appropriate text. But it feels plain stupid to modify an abstract engine to add some custom constants from the VN.

I suppose that I will just mass find/replace 'a thinks' to 'a_thinks' and create each mood separately.

Thanks a bunch !
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,958
16,192
I'd also add that "shouting" and "whispering" are kinda rare throughout a VN story.
Maybe rare enough that it's not worth adding extra characters and instead just stick with using text tags on those occasions.
Yes, whispering are generally expressed by reducing the text size ({size=-10}Shh ! Not so loud{/size}), while shouting is expressed by using bold text ({b}Stop right here, whoever you are !{/b}), but can also be expressed by increasing the text size.


{w} (Wait). Although I would always specify a time, like {w=2}.
It's to use with caution.
Too small waiting time will make the effect pass unseen, especially with none native readers that will need more time to read the dialog. This will to long waiting time will lead the more impatient players to click to advance, what sometime lead to the rest of the dialog to be simply skipped.
Ideally, the waiting time should be adjustable by the player, depending of his reading speed. But it's not something possible without a really complicated syntax:
Python:
define waitingTime = 0.5

label whatever:
   $ mc( "Hi, long time not seen.{0}w={2}{1} How are you ?".format( "{", "}", waitingTime * 2 ) )
Or a deep dirty hack, that I'll not show. {w} is not a tag that apply to the text, therefore it's proceeded really early, and it can't be applied as part of a custom tag.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
{w} (Wait). Although I would always specify a time, like {w=2}.
Too small waiting time will make the effect pass unseen

Yup.
I'd only ever use times of under 2 seconds, usually more like 0.5.

The effect is more like the character taking a breath - and yes, could very easily go unseen. But then I would view that as "nothing lost", since without the {w=0.5} - that brief pause would not have been there at all.

I am a player who plays with "auto-forward"/reduced text speed switched on, so I'm more familiar of the pitfalls of getting the timing wrong. The worst case scenario in my mind is that the player is left wondering why the game is not advancing at a normal speed. Which is why I would always set the time to be lower than I think a "normal" person would notice.

In reality, the vast majority of times someone might use this... they would be better splitting the dialogue into two separate lines and letting the player do the advancing at a pace normal to them. But's one of those massively under utilized tags... which was why I thought to mention it.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,958
16,192
In reality, the vast majority of times someone might use this... they would be better splitting the dialogue into two separate lines and letting the player do the advancing at a pace normal to them.
Wait... this is the solution to the "user defined delay" issue...

The problem is that you can use interpolation even inside tags, but those interpolations can't be computation. Therefore you've no way to adjust the duration for {w} accordingly to the number of words to read and the player preference.
But this can be solved by splitting the dialog, and relying on a combination between the {w} and {nw} tags, and the extend meta sayer:

Python:
# Default reading time for one word.
# /!\ Totally arbitrary value used because it's big enough to be noticed
# but small enough to not be annoying during the tests.
define waitingTime = 0.5

label waitForIt( loops ):
    while loops > 0:
        #  Wait one unit of time, then automatically pass to the next line.
        #  Like the sayer is /extend/, it add to the current dialog line.
        extend "{w=[waitingTime]}{nw}"
        #  Do this as many times as asked.
        $ loops -= 1
    #  They go back to the normal game flow.
    return

label whatever:
    #  The use of {nw} will make Ren'Py automatically pass to the next statement.
    mc "Hi, long time not seen.{nw}"
    #  Here we make Ren'Py wait for 2 times /waitingTime/.
    call waitForIt( 2 )
    #  And now back to the dialog. 
    #  All this without a single interaction from the player.
    extend " How are you ?"
It's a bit inelegant, but not dirty.


Be noted that this don't change the others caveat regarding {w}. It just offer a way to adjust the waiting time to the player preferred reading speed when the dialog are displayed instantly.