How Succubus Stories was Made (Devlog #5) (Patreon)
Content
For this devlog entry, I wanted to detail the technology, meaning the software and programming techniques, that I used to create Succubus Stories, and detail some elements of its design, from the inside out. I have two primary goals in writing this. The first is that I hope through understanding the design, patrons and players can be assured that they are in good hands. While the systems in the game are not perfect, they are tailored toward productivity and toward generating and implementing content in as quick and easy a manner as possible.
It's very easy to design code that is hard to expand. If you know of a project on Patreon that's stalled out, one possibility (in addition to the possibility it was a scam or the creator is a goon) is that continuing to expand the scope of the project became difficult because of the way the game's systems were designed. There's always room for improvement, but SS is well-designed and easy to work with, with organized code, no "magic numbers," and a simple data-driven design. I feed in scenes or events or other data with the appropriate tags and flags—sometimes with a bit of scripting logic here and there to achieve more complex results—and the engine takes care of the rest.
The second goal is simply to entertain. I think it's fun to talk shop about this sort of thing, and I hope you find it fun too! If you have any questions about any of this, feel free to comment or reach out elsewhere!
I'll describe some of the game systems in greater depth later, but for now, let's zoom out. As mentioned in the game's credits, it is a Twine game.
Twine and Twee
I don't actually use Twine, meaning the actual application, for anything. Instead, the game is written in Twee format using the Twee3 language tools extension for VSCode, and compiled with TweeGo and some custom tools. If you're familiar with Twine, you'll know that it can leverage a couple of different story formats, which are basically game engines. SS uses a modified fork of the SugarCube story format I internally call SubtleCube (or SubC).
SubC removes a few features from SugarCube that I don't use, has customized UI elements integrated natively, and solves a couple other problems for me. However, the main reason it exists is for data compression. Normal Twine games are served simply as text in an HTML file: all the game's text, scripting, CSS, etc is plopped into a special data element in the generated HTML file and the game reads it on startup. SubC uses text compression to achieve much smaller file sizes, and the files generated are about 50-55% of the size of files generated without this compression. This makes SS load much faster than equivalently-sized Twine games. The decompression takes so little time compared to the download that even users with fairly fast Internet enjoy noticeably faster start-up times. There is a point at which the decompression takes longer than the uncompressed download would have, but such speeds are uncommon for consumer-grade Internet and we're talking about sub-second differences, so the savings are well worth it in my book.
Data compression like this obviously requires data to be both compressed and decompressed. In this case, compression happens at compile-time and decompression happens at run-time. This means the compiler tooling needs a compression step and the engine itself needs a decompression step. This is the main reason why SS uses custom tools and a customized game engine. It is not, contrary to popular belief, because Twine sucks or is limited or would not be powerful or capable enough to handle a game this complex otherwise.
In fact, I went into this project expecting to have to customize parts of the engine to remove restrictions or add functionality, and frankly, I didn't have to do much, and most of what I did do could have been done in the game without forking the engine. The only thing I couldn't have done as easily were the loading screen change and the data compression. I believe everything else should be possible in vanilla SugarCube.
As part of the compression step, the data portion of SS is changed to make it so the Twine application cannot locate it. I did not do this to hide the game's source code as Twine can't load the data either way. This was to prevent user issues. I wanted to see what would happen if the compressed data was loaded into Twine and it crashed my computer, so I wound up making it so Twine wouldn't be able to even try to load the data.
Data-Driven Design
As mentioned, SS uses a data-driven design paradigm to make adding data, like events, potions, perks, or traits, as simple and straightforward as possible. Let's add an event! I'll walk you through the process. If you're familiar with Twine/Twee, this will be easier to follow, but I think even if you aren't you'll be able to grasp the idea. First I'll create (or open) a .twee file in the game's directory, then I'll add a new passage. It doesn't matter much what we call it.
The words after the :: are the title. The words in the brackets are tags. All that's required for an event to be added is for it to have the correct tags. In this case, the event tag tells the game it's an event, and the backstreets tag, when added to an event, tells the game that this event should be in the playlist for the exploration activity in the backstreets. There are several types of activities that trigger events, each one has a tag. A single event may have any number of tags, meaning a single event may be in multiple playlists for multiple different activities, though in practice, this doesn't happen much,
Now that we've got the basics. Let's add a player character portrait.
Yep, that's all it takes. Words preceded by ! in the tags section are portraits. An ! all by itself means the player should simply have a neutral expression. If the word after the ! is a valid expression, that expression is used for the portrait. If no such expression exists, the neutral expression is used, but in debug mode an error is also raised so I'm aware of it during development. (Hiding errors from users is good; hiding them from yourself is less good.)
Let's add some content.
The <<pl>> macro gives the indicated text the player character's speech color. The <<i>> macro creates the black interactable buttons used in the game. In this very simple event, the player has two choices; each one leads to a different passage and a different branch of the event, and may have different ramifications! The ?name is simply a replacement template for the player character's alias. Dozens of templates exist in the game, many are used to dynamically create descriptions.
Let's say that the player character has, unbeknownst to her, actually wandered into some kind of restricted area, so her notoriety should go up. Well, lets add that now.
You see it there in the tags? The rep tag tells the engine we want to make a reputation change. The +(notoriety:large) tells the engine to increase notoriety by a large amount, as you'd probably imagine. That's all it takes! It is possible to change reputation with scripting in the passage itself as well, but this is generally not necessary.
In fact, if it's possible to avoid scripting, I generally do avoid it, preferring to keep content and code as separate as possible.
Another thing you might notice it that it's +(notoriety:large) instead of +(notoriety:5) or something. This is because what constitutes a "large" notoriety shift is handled my a series of values and dials elsewhere in the game's code that I can change on a whim to adjust game balance. This is largely what I meant by the game not having "magic numbers." Every number, with a few exceptions, is a constant that can be quickly changed or modified. This also allows traits to function easily, as they can effect the constants to change the game rather than requiring me to account for them every time I use the number they effect.
While events are probably the simplest thing to add, other stuff, like potions or perks, aren't a whole lot harder. In general, it's just some static data that the engine already knows what to do with. New systems, like the consequences system and the expedition system, do require a lot of programming, though. But to just add content is straight forward and simple, which makes the project very easy to expand and it scales up nicely. Story events and requests are a bit more complicated to add than playlist events and encounter scenes, usually requiring some scripting in the content still to continually, manually update flags and stage data, but I am improving the implementation of these types of content as I go, and soon I expect them to be almost as straightforward as events.
About Mod Tools
It is the basic, static data oriented design of the game that makes adding modding capabilities so achievable. As you can see, creating a simple event should be as easy as knowing which tags you need, and a little bit about how to add options. More complex events aren't really that much harder, if you can type the occasional function or macro command. If you know Twine and SugarCube, I bet you could do a lot just with the stuff I've shown already.
I don't have any dates or news about mod tools yet, but I think they are a great fit for this project. At least at the scale of adding events, scenes, and requests, if not full story content and new progression elements. We'll see though; supporting a non-existent mod community isn't worth the investment, so I'll start with some more basic tools to test the waters sometime early next year.
So Long for Now
That's all I have to share about the tech behind the game right now. If there's interest, let me know! We can look at other game systems or I can dedicate future devlogs to answering questions about the tech or development. I know the tech behind a text-based game probably isn't that compelling or interesting, but a lot still does go into it.
If you haven't yet, please fill out this feedback form for v0.3. It should only take a few minutes, though there's an option to give more detailed feedback if you have the time and energy for that.
Version 0.4 is under way. I don't have anything to announce regarding it right now in terms of content, but you can expect to play it in early November.
I will probably be out of town for an extended period of time in December, and that may have ramifications for the January update. I don't know exactly what's going on with that yet, but I wanted to mention it as early as possible to my patrons. The December update should not be effected, I expect it to be done and out the door by the time I would be away.
Stay tuned for an update preview soonish, and some images from development as I go. Thanks, as always, and I'll talk to you soon!