Rogue-Like Update 07/16/2019 (How to write Rogue-Like) (Patreon)
Content
I thought I'd do a little explanation on how I put a scene together in Rogue-Like, and maybe inspire some of the community to get involved, either in working on this project, or just in making your own games, because Renpy is a really simple system to use, and much less intimidating than most people would think.
My first step is that I'll just start writing. Easy! I'll just start writing out a scene, in a linear format, just dialog, and I'll have an idea in mind for where I want the scene to go, but I just let it flow back and forth naturally for a while. I'll give a little example below. Note, anything that comes after a "#" in Renpy is considered a "comment," and is ignored by the game. This way you can leave notes for yourself so you don't forget what something does.
"Rogue walks into the room"
ch_r "Hey, [Playername]."
ch_p "Hey, Rogue."
Anything in quotes like this is presented as a narration block. anything in brackets is a variable, in this case, slotting in the name "ch_r" means Rogue is speaking, ch_p is the player, etc. This is my own naming scheme, other games can do this differently
I try to avoid putting too many words directly into the player's mouth, mostly non-controversial options like "Hey, Rogue." More often, when it's the player's turn, I like to provide a menu of multiple options. When I'm first writing out the scene, I might limit this menu to just raw dialog, and then go back later to add more detail to the options, so let's scrub out that "hey Rogue," and give some more options instead.
ch_r "Hey, [Playername]."
menu:
extend ""
"Hey Rogue!":
pass
"What?":
ch_r "I said \"hey!\""
"Go away!":
ch_r "Rude!"
ch_r "So anyway. . ."
Ok, that's a fairly simple menu, let me explain what's going on there. Renpy and its underlying system "Python" is based on hat's called "blocks," basically every time you indent, everything that shares that indent is connected to the previous indentation. By that I mean that in this case, everything that is indented further back than "menu" is considered to be connected to that "menu" command, which will bring up an option of choices. Once we get down to that "so anyway" line, and it's no longer indented, that bit and anything after it won't be considered part of the menu. Script editing programs tend to make using tab to indent a very simple process (the Patreon post editor is a bit sloppier).
Below the menu:, we have a "header line," which is not a choice in the menu, it's something posted as text while the menu is up. You can put a question here, asking for an answer, or leave it blank, but extend "" will just copy the last thing said and leave it up, so when this menu displays, it will keep Rogue's last line showing while you pick your response.
So in this case I've offered three possible responses. Three is a nice round number, but of course you can give any amount. Responses have to be in quotes, with a colon at the end of the line. Then "what happens when they pick that answer" has to be intended one further gap deeper than the choice.
The first answer, the consequence of the choice is a "pass" statement. You can't just leave this section blank, there needs to be something there, so if you don't have anything to put there, you can just say "pass" and that will just kick the result down to "so anyway." You don't need a pass if anything else is happening there though. In rogue's statement "I said \"hey!\"", what I'm showing is that you can't just drop quotation marks inside of a quote, because the game would treat that as two separate blocks of text, on the same line, with a word in the middle, and that would confuse the hell out of it. Putting \ in front of the " means "ignore this in terms of code language, just pretend it's a normal "." You need to do this before each quotation mark you want to use like this. You can also use this trick on Reddit.
Now, what if you want to make the menus a bit more complicated? Let's just isolate one of those menu items:
"What?" if PlayerStupid:
if ApprovalCheck("Rogue", 600):
ch_r "I said \"hey!\""
else:
call Statup("Rogue", "Love", 80, -2)
call AnyFace("Rogue","angry")
ch_r "You know what? Never mind."
Ok, so what's happening with that mess? It's really pretty simple and a lot of it just gets cut and pasted throughout the document. Remember what I said about "just write the story out linearly," and then go back through and add stuff like this after you've done that.
So what's happening differently here is that the "what?" question will only appear if the variable "PlayerStupid" is "true." This isn't a real variable in the game, I'm just using it as an example, but you can put any sort of conditions here to hide various potential choices if they might not always make sense. So for example, if a third character might be in the room, you could make an option addressing them conditional in this way. If not "PlayerStupid," then only the other two options would show.
Next, after you've picked that answer, there is a conditional check. If the first part is "true," then do the part right under it. If it isn't, do the other thing. "ApprovalCheck" is a function I made for the game, and it's basically used to check whether a certain stat threshold gets met. This is the simplest version of it, in this case checking whether the character "Rogue" has at least 600 stat points when all three of her base stats are combined. I can get into more complex uses of this function later, but for this example, we just know that if she doesn't totally hate you, then she will just roll with your answer, no harm done. If the check fails, however. . .
The first thing it does is run the "Statup" function. This is another tool I built, and it basically raises and lowers stats by a given amount. There are other ways to do this, but this way you also get those nifty floating numbers. In this case, it will take the character "Rogue"'s "Love" stat, and IF that stat is below 800, then it will lower it by 2. Why does it say 80 instead of 800? It's a holdover from the earliest versions of the game where stats capped off at 100. ;) Doesn't cause any harm though so I leave it alone.
Next we have a facial animation change. In this case, applying an "angry" face to "Rogue." There are also additional ways to make that more complex, adjusting the blush, or replacing various elements of the base expression individually. For example, call AnyFace("Rogue","angry",2,Eyes="side") would take the default "angry" expression for her, make her blush furiously (the "2" could be a 1 or 0 to display less or no blushing), and then the "Eyes="side" would replace the default eyes for that expression with the sideways glance. This is also a unique function that I built for the game, but once set up it's quite easy to use.
I feel like that got a little complicated, so hopefully you'll have some questions I could answer, and we can digest that one a bit, but the main advice is, write the story down in a linear manner, and then go back into it and add things like conditions, consequences, stat changes, facial expressions, etc. Don't get too lost in the weeds right at the start of the scene.