So as I mentioned in my last post on the new features in the June update, SFPC now has its own scripting language used to build content for the game. Here's a basic script for a treasure chest from the demo:
!SFPC_Script "Chest 1 - Long Sword"
# Only runs after the chest has been opened.
FlagNeeded "Castle1_Chest1"
DialogueWindow "BOWIE looked in the chest.^It was empty."
End
NoFlag "Castle1_Chest1" # Only runs if the chest is still closed
SetOverlayTile 27 44 49
PlaySound "open-chest.ogg"
AddItem "Long Sword" "opened the chest."
SetFlag "Castle1_Chest1"
End
Let's break it down line by line:
!SFPC_Script "Chest 1 - Long Sword" - The opening !SFPC_Script just identifies this as a valid SFPC script file. The "Chest 1 - Long Sword" in quotes is the name of the script that gets shown in the editor when placing events. For instance, the many empty barrels that are scattered about the map all run the same script. If I place a few barrel tiles on the map then write a script for them, I only need to do it once, then I can place as many empties as needed, and I can flip back through the list of events easily to get back to that script and just place more copies later as the map expands.
# Only runs after the chest has been opened. - The # signifies that this and everything past it on this line is a comment, similar to // in C++ or /* */ in C. The scripting engine ignores everything else on a line after it hits a #. As can be seen later in this same script, this doesn't have to be the only thing on the line, comments can be at the end of a line after other script commands have already been run.
FlagNeeded "Castle1_Chest1" - Checks to see if a flag named "Castle1_Chest1" has been set or not. First, a quick aside about "flags". The game engine keeps a collection of flags, which are just strings of letters/numbers/etc. Any string of characters between quotes can be a flag. They're kind of like boolean true/false variables. Back to FlagNeeded! This command asks the engine to see if a flag named within the quotes exists. If it does, move forward and run the script commands on the following lines. If the flag has not been set, ignore all commands from here until the next
End statement, which in this case is two lines down. The indentation on the commands that follow is optional but helpful, it keeps the script readable and helps show at a glance where one conditional statement like a FlagNeeded begins and ends.
DialogueWindow "BOWIE looked in the chest.^It was empty." - A DialogueWindow is a single window that shows up on the bottom of the screen for displaying text that draws in one character at a time. The text in the window is processed automatically to wrap from one line to the next. The manual '^' tells the string processor to show the paused text triangle at that point and wait for user input before clearing the window and showing more text.
NoFlag "Castle1_Chest1" - Similar to FlagNeeded, NoFlag checks to see if the flag in quotes is set and only runs the code between itself and the next End statement if the flag is NOT set.
SetOverlayTile 27 44 49 - Places the 27th overlay tile in the current tileset at coordinates 44x, 49y on the map. The top left corner of the map is 0, 0. Similar commands exist for SetBackgroundTile and SetForegroundTile. The draw order, from bottom to top, is Background, Overlay, characters, Foreground.
PlaySound "open-chest.ogg" - Pretty self explanitory, the AudioMgr code plays the requested sound. In this case it's an ogg vorbis audio file, but since I'm using Audiere for my sound lib at the moment, it can also play .wav or .mp3 files as well. The sound plays one time and is not looped.
AddItem "Long Sword" "opened the chest." - Runs the AddItem command, adding the item specified in the first parameter with the text in the opening DialogueWindow from the second parameter. If we were looking in a vase the text would be something like "looked in the vase." but it could just as easily say "checked inside the fireplace." or "searched behind the painting." or any number of options depending on the situation. The AddItem code then goes on to attempt to add the selected item to each character's inventory until it finds someone who has room to hold it, or gives up if everyone is full. I still don't have a working solution yet for the fringe case of opening a chest if the party has no room whatsoever (though I do have some ideas), currently in that case the item is lost for good and the chest remains open, but eventually the item will have to be put back in the chest and the chest will need to be closed back up again.
SetFlag "Castle1_Chest1" - I've already touched on the checking of flags, this command simply sets the specified flag to exist and be set to true.
Now, let's go back and put it all together and spell the whole thing out in english! If the flag Castle1_Chest1 has been set, we know the player has already opened the chest, so if they interact with it, just show that it is empty. If the flag was not set however, the item is still in the chest, so let them have it. Replace the closed chest tile with the open chest tile (27th in the overlay tileset), play the open chest sound, run the AddItem command to actually give the party the item and make sure someone with a free hand gets it, then finally set the Castle1_Chest1 flag so that this chest stays empty. It's a chest, not a sword dispenser, after all.
There's one thing missing here though, which I leave as an exercise for the reader. Point it out in the comments, and I'll cover what's missing in the next post on scripting for SFPC!