Custom text for ren'py tied to Affect__ion meter


The initial gimmicky thought I had for com__et was about relationship meters in games, how they can feel very rote and prescriptive. Specifically, I was playing Fate Stay / Night, and felt like it was detrimental to the experience. There were three canonical routes, so I wanted to hit each in order. I had to make choices that weren't choices, because most of the time it wasn't giving me an option to explore different routes, it was actually just a small test of "can you get onto The Correct route you should be on."

So I thought about it inverted. What if you had an affection meter where the "correct" thing to do is to drive it down, to get off the prescribed path. (let's not point out that I did exactly the same thing I criticised in FSN, I have a canonical pair of endings where you have to hit one then the other)

Thematically this is about having the obvious and intended, unsatisfying path that flattens things. So pretty early I had the idea for what I referred to as "subtext", sections of the script that wouldn't be visible if the meter was full. ren'py comes with text tags for various things like bold text and italics, as well as more unusual things like ruby text. But in addition to that you can make custom tags. The way they work is that you make a function that will take in text, and then you can insert whatever custom tags and processes that can perform dynamically. For example, I could style text based on how full up a meter is.

So how do I do this?

Well first some quick helper functions, I want a quick test to see if the subtext is active, which I decided just meant it should be lower than 75 out of 100. That means if you choose more than 1 of the affect lowering choices, you've almost certainly activated it. The main reason to have this is so that across the entire game, if anywhere I want to decide if the subtext is active, I have one function that will always tell me the answer to this question

def is_subtext_active():
    return persistent.false_end and persistent.affectation <= 75

Next, the main way that I want to adjust the text styling is to set its visibility based on the affectation level. This is again simple code, but is just one specific function that dictates the exact colour string to use at each stage of affectation. This could have been fully dynamic, converting the 0-100 scale into the 0-F scale, but it worked better to have stages of visibility. The stages mean that it's wholly invisible at 100. It's very slightly visible at 75 or higher, but I wouldn't expect a player to be able to read it necessarily. At 25 or higher, it's slightly transparent but largely visible, and below that it's solid text.

In all cases the colour is the same, so only the visibility shifts. And even at full visibility the text stands out as different, so you can see the dual text and still read how the sentence would go without it, and what would be hidden if not for your low affectation.

def get_subtext_colour():
    if persistent.affectation >= 100:
        return "f0f0". 
    elif persistent.affectation >= 75:
        return "f0f7"
    elif persistent.affectation >= 25:
        return "f0fa"
    else:
        return "f0ff"

So time for the actual function. I have tag, argument and contents all passed to the function because that's the standard for custom text tag functions. You'll notice I only use contents, there's no arguments passed to this one and the tag is irrelevant for the function. But all need to be included in the function definition.

I start off the function itself with getting the colour from above and whether or not the subtext is active. I make a list called results which I'll be returning. A text tag function needs to return a list with entries in a specific tuple format of (TAG, string). I first insert a colour tag opener, this is equivalent to something like {color=#f0f0}. And then if subtext is not active, I also add a cps tag which speeds up the pace at which characters appear. This is so that when there's invisible or barely visible characters, the user isn't waiting long periods without any visible change. 

def st(tag, argument, contents):
    colour = get_subtext_colour()
    results = [(renpy.TEXT_TAG, u"color={}".format(colour))]
    if not is_subtext_active():
        results.append((renpy.TEXT_TAG, u"cps=*30"))

Fun fact, I spent quite some time trying to also find a way to remove or modify {w} tags, which are what you use to pause mid text. I use these a lot for pacing in lengthy sections of text. And some of the subtext bits get looong for effect, so I did want to be able to break those up with pauses but not make people click through invisible text. But after lots of experimenting... I saw the documentation said "custom tags can't edit the wait tags". So the lesson is: read the docs.

Next I just loop through the contents, which is all of the text that comes between my custom tags. So this is effectively just saying "include all the text as it is". Because all we're really doing is wrapping this text in dynamically determined tags. And lastly, make sure to include the closing tags as appropriate, and then return the list of results we've built.

    for kind, content in contents:
        results.append((kind, content))
    if not is_subtext_active():
        results.append((renpy.TEXT_TAG, u"/cps"))
    results.append((renpy.TEXT_TAG, u"/color"))
    return results

That's our function! To actually use this for subtext, we need to first tell ren'py this is a custom text tag by adding it to a custom text tag dictionary. Where the key is the name used in the script, and the value is the function itself to be run.

config.custom_text_tags["st"] = st

And then in my script I can use it like any normal tag:

n "She pushes Me off {st}with a hint of genuine irritation in Her voice,{w=0.2} but it's so brief I can't get a real read{/st} of Her."
n "I mumble a sorry, laughing {st}to hide My awkwardness, as I try to understand Her.{w=0.2} I feel like I can't just ask Her, when I know I'm always hiding from Her too.{w=0.2} It's safer for both of Us to be this way.{w=0.2}{/st}{nw}"
n "{st}Instead of guarding My own secrets while sniffing out Hers{/st} awkwardly."  

Also note in the above sample, I got around my wait tag dilemma by still including wait tags, but they always have built in brief timers. So instead of {w} which will only progress when the user clicks, I set a time limit after which it automatically progresses. This means some amount of waiting with invisible text, but it's overall very little time per section.

And that's it! Once in place, I can just use this tag extensively throughout my script. (126 times apparently)

Get com__et

Buy Now$4.00 USD or more

Leave a comment

Log in with itch.io to leave a comment.