loading...
Cover image for Breakfast Tale Dialog Update

Breakfast Tale Dialog Update

cpluta profile image Chris Pluta ・6 min read

Breakfast Tale - A Ren'Py Experiment (4 Part Series)

1) Teach Me Ren'Py 2) Setup Ren'Py Project 3) Learn Ren'Py Dialog 4) Breakfast Tale Dialog Update

Welcome back!

Last time we talked about how to create dialog for our game. Since then I've been taking what I learned so far and applying it to my story.

I want to take a minute to give some progress and what I've learned since then.

Developing the Idea

When I thought about learning Ren'Py I wanted to play on an idea of it being a kind of dating simulator. I may not be the most romantic so I thought of food as my first thought. Then I quickly went to "how do I make a sexy pancake?". This is where I started my idea and it started to take shape from there.

I've been writing this story in a very ad hoc way. I just start typing and just see where it takes me. I know there will definitely be several revisions before I'm content with the story.

Having this approach definitely could be told forever and I intended on this being a small project. So I started to think about my endings to help bridge some of those gaps. This led me to the question of "How do you identify your dream breakfast?". Is it simply breakfast items people fancy? What are the elements that make this happen?

Several solutions might be something like Salt, Fat, Acid, Heat, but I decided to go with a more RPG approach with Fire, Water, Earth and Air. With this arrangement the player can decide their path and really put an implied emphasis through the story of what qualities they prefer most. Where the first choice is most important and the last is irrelevant or opposing to the choice. For example, a choice of Water, Air, Fire and Earth might be something like a good cup of Tea in the morning, since it's wet, aromatic and warm.

With this approach there will be 24 endings with at least 16 different scenes to be written. It can be stretched out if I am going for 24 distinct variations to the story based on previous choices. We will see where the story takes me, I suppose. Either way this is at least putting a few bounds on the bulk of what I will be writing.

When coming up with placeholder endings, I used excel to help make all these combinations and labels I need for the code. I didn't want to type everything out and be error prone, so I used something to help me automatically write some code for me. πŸ˜„

The way I did this is in excel wrote the different permutations and in the next column I did concatenation with underscores(_) to help make all these statements.

Concatenating Elements with Underscore - Excel

Once I was comfortable with this column, I then made another column that concatenated the label with the previous column ending in :.

Underscored elements with label format - Excel

I took this and pasted it in my ending file to help myself out. I could have gone further but I put some placeholder text after each label of the label name and called return so the story can end properly.

# ... truncated for brevity

label fire_water_earth_wind:
    "fire_water_earth_wind"
    return

label fire_water_wind_earth:
    "fire_water_wind_earth"
    return

label fire_earth_water_wind:
    "fire_earth_water_wind"
    return

# truncated for brevity ...

Refactorability

Before going any further, I want to take a moment to discuss some of the things I did to help organize my files and labels, as well as initializing variables.

In the previous part I mentioned I made a characters.rpy to help setup all my character information. I made two more files to do the same thing, these are constants.rpy and state.rpy. Constants is used for any constant variables I intend to use and state is for any progress I want to keep track of during the game. There is no functional different between these files other than helping explain my intention with the variables wherever they are placed.

In constants.rpy I made a few variables to help with my elements. I am using this so I can quickly switch out dialog menu options, and not break the rest of the game. For instance, I have a variable called Fire but it represents the Sun. So, whenever someone chooses and option the variable will be pushed into the array and not a hardcoded string. This also saves me if I ever decide to switch ideas, I will have explicit code errors to break and not implicit ones I need to play the entire game through to find. Although I will still need to do that. 😜

Example of Constants being used - Visual Studio Code

After that decision I made a decision that whatever my file name will be the first label in the file. This will prevent me from accidentally using a label I forgot about since all labels are global by default. As I was looking more into how to do things, I found out there is a way to do local labels in a file by adding a . in the front of the label name. There is a way to access this label from a global scope as well. If I get into using that technique, I will talk about it for sure.

Label Convention Example - Visual Studio Code

The exception I made for label naming is with my variable files where I made the label initialize_{filename}. This allowed me to quickly identify what I am doing and why it needs to be located where it is in a script.

Initialization Convention Example - Visual Studio Code

Lastly, as I mentioned in the previous section, I took these variables to dynamically generate a dictionary map of all my possible endings so I don't need to write a massive condition of order to figure out which ending to use.

$ ending_matrix = {}
$ ending_matrix[Fire+Water+Earth+Air]="fire_water_earth_wind"
$ ending_matrix[Fire+Water+Air+Earth]="fire_water_wind_earth"
$ ending_matrix[Fire+Earth+Water+Air]="fire_earth_water_wind"
$ ending_matrix[Fire+Earth+Air+Water]="fire_earth_wind_water"

# truncated for brevity ...

Dynamic Jumps

As I mentioned I will have 24 endings. This is a lot to manage and I don't want to write a ton of conditions to handle it. So, my approach is to make an array and append each value into it to determine the path the player took. Since I made a dictionary to manage my endings, we can just determine the dynamic key to figure out which ending should be chosen! Getting the key is straight forward since its just each name in order. This was done with a join.

label start: 
    $ items = ["Apple","Pear","Grape"] 
    $ concatenated_string = "".join(items) # The result is ApplePearGrape 
    "[concatenated_string]" 

Since we know how to get string for which path the player took, we just need to access the dictionary to find the ending.

Now that we have the ending, we need to find out how do we dynamically jump to a label. If we just try to jump {labelName} the engine will try and go to that variable name. Lucky for us Ren'Py has us covered! We just need to add the keyword expression after the jump to tell the engine that we have a variable to evaluate when we get there.

label start: 
    $ ending = "ending1" 

    jump expression ending 

label ending1: 
    "This is ending 1" 

Conditional Menu Option with Array

Earlier I mentioned the player order matters during their playthrough. They cannot choose the same option twice. With that constraint I need to limit their options as they go through the game. To do this I have an array called dream_choices. As they make choices I append each option into the array.

When I append an option into the array I could opt to put a hardcoded string to be added. However, as I mentioned in the refactorability section I made each option be a variable to be altered as needed.

Now I have an array with the values the player already chose. I need to conditionalize which options have not been chosen yet to help guide the player.

In the previous part we talked about how we can add a condition to a menu option based on simple conditions and True/False values. In this example I need to check an array. To do this there is a special operator in python known as in. This will take the item you want to find and check it against the array.

label start: 
    $ items = ["Apple","Pear","Grape"] 

    if "Apple" in items: 
        "Found an apple" 

In my situation though I need to know what hasn't been selected yet. So, we need to invert this operation. To do that we use the other special keyword of not in to do the opposite.

label start: 
    $ items = ["Apple","Pear","Grape"] 

    if "Banana" not in items: 
        "Couldn't find a banana" 

All the storage so far is saved on a specific playthrough. There is another kind of storage that persists across games to create more of a "Game Plus+" mode. As I mentioned earlier about local labels, if I get into this technique, I will explain more about it.


Thank you for following along with everything so far. Please check out the project and see what is done so far on github: https://github.com/cpluta/Breakfast-Tale.

Breakfast Tale - A Ren'Py Experiment (4 Part Series)

1) Teach Me Ren'Py 2) Setup Ren'Py Project 3) Learn Ren'Py Dialog 4) Breakfast Tale Dialog Update

Posted on by:

cpluta profile

Chris Pluta

@cpluta

Software developer by day, game maker by night. Co-Founder of Lets Build.

Discussion

markdown guide