Spiral Game System Prototype2020-10-12
A prototype console app for designing games for the Spiral Game System.
I’ve always used spreadsheets to design games. I often feel it necessary to be able to compare apples to apples on game components, especially when balance is important.
But sometimes, a spreadsheet is just not enough. On Oubliette, I pushed the limits of lookups and object-oriented data in google sheets. After that, I decided that my next data-driven game would need a more robust object system.
It took a few games and several years, but we landed on The Spiral Game System, which would be the basis for our next big phase of games.
And with SGS, I my plan for a robust data system began to see the light.
This command-line app was my first attempt to build a tool that could manipulate the kinds of data structures I had in my head for the project. It later morphed into the Spiral Game System Website, but at the time, this is what I had, and it influenced the design of SGS.
At its core, the SGS console app allows you to take a document of game elements, such as stats, items, and creatures, and perform operations on them that would change their XP values.
the SGS console app was originally envisaged as a multi-platform, single user system composed of a core SGS library, and a number of Xamarin-based frontends. The pilot frontend was a simple command-line interface for the library itself, and in the end that’s all I built before deciding to upgrade to a website.
The console app was designed to run in two modes: compile or interactive. In compile mode, all options were selected on the initial call, and the app would perform its actions and compile a document for you. In interactive mode, the console app stayed open on a while loop and allowed you to perform commands on an opened SGS document.
In either mode, the program would load a document of human-readable game rule content, perform some math for each element, and write that information back to the document. In interactive mode, you could select and manipulate individual game objects, and the app would automatically refresh the UI so that you could keep track of the XP values of things and use new stats as you created them.
This was implemented as a loop of commands. Each command was composed of:
- main name, such as
- an array of alias, such as “add”, “a”, “+”, “new”
- A description string, which appeared in the in-app
- an array of possible subcommands, which further specified the user’s intent
Each command had its own custom handler, and depending on the subcommand and parameters given, it would perform actions on the SGS document held in memory.
The advantage here was that objects could be referenced in multiple other objects, without needing to update the same object in multiple places. Change the stats on the
longsword and it would automatically populate out to
As the stats and objects were changed, the XP values of each affected item would automatically update.
In interactive mode, the command loop continued indefinitely until the user entered a
Points of Interest
set command allowed the user to change values on entities. Depending on the subcommand, it would set different kinds of values. Generally you would set skill, tactical stat, or primary stat.
An example of use would be
set agility d4
Which would set the active entity’s agility score to a d4.
list List all entities: longsword 14 xp dagger 7 xp easily hidden flame wand 21 xp conjure flames small and large earth staff 19 xp Manipulate dirt & earth longbow 11 xp mace 14 xp good against armor fire bolt 23 xp Fireball 28 xp assassinate 30 xp buckler 6 xp kite shield 21 xp plate armor 82 xp impressive to common folk chainmail 48 xp leather armor 12 xp mage robes 30 xp impressive to the common folk battle axe 14 xp medicine bag 10 xp holy symbol 7 xp thieves tools 14 xp disarm traps & unlock doors crossbow 23 xp darts 4 xp easily hidden healing spell 14 xp instant healing invisibility spell 56 xp Rapier 14 xp Agile & Precise Weak Poison 5 xp Increases Damage When Applied dragon leather armor 33 xp Posh clothes 16 xp impressive to common folk Meteor Swarm 98 xp compass spell 11 xp disguise spell 4 xp Lesser Nature Magic 7 xp alter or create small natural aspects Burglar's Kit 13 xp rope and climbing equipment, disarming tools and gadgets Alchemist's Kit 13 xp bottles and tools for potionmaking, chemical testing tools Craftsman's Kit 14 xp metalworking tools, woodworking tools Generic Rogue 289 xp cunning and quick-witted, friends in low places Generic Mage 273 xp wise and kindly old father figure, well versed in the ancient lore Generic Soldier 282 xp Brave and Strong Willed, Monster Slayer Petty Criminal 55 xp petty criminal hero 54 xp Criminal Thug 104 xp Swarm of Rabble 200 xp Whole Crowd of Angry People Epic Character 2,592 xp Epic2 2,585 xp Banesword 63 xp Advanced Assasinate 122 xp skeleton 70 xp orc warrior 189 xp shambler 402 xp plant camouflage warlord 1,372 xp wily and brave, high pain tolerance Generic dragon 5,548 xp magic-resistant hide, can fly
list command showed a table of all entities in the document, their XP value, and a summary of its aspects. This quickly allowed the user to get an overview.
Object Math is a module in the SGS core library that allows the app to perform mathematical operations on dice pools. This was one of the most complex parts of the app, as it involved string parsing and some very elaborate domain-specific rules.
In roleplaying games, dice are rolled during gameplay to produce outcome numbers. On the character sheet, a string like
1d20+5 represents an operation, not a specific number. Therefore, adding the two together doesn’t come out to a single number, but another dice pool,
1d20+2d6+5. The object math module covered as many of these operations as I could forsee at the time, by breaking each string up into its component parts and applying well-defined operatons to those parts.
For each operand of an operation, the system created a dictionary of die sizes (d4, d6, d8, and so on), and placed the number of dice in the value for its respective key. Another key was used for flat numerical bonuses. Dictionaries of this type were used for each of the operations. If the pools were added together, for example, the values from one dictionary would be added to the other.
In the event that the app couldn’t find a simplification for an operation (
1d6 / 4 for example) the app would simply return that value as the most simplified value for the operation. When possible, it would perform the actual math and return the result.
The app provided the following operations, organized by the number of operands:
- Best: the best value of a stat by name on the entity or any of its equipment
- Integer: just a single integer value
- RealNumber: a float value
- StatRef: finds an entity’s stat value by name
With these operations and references, you could build complex formulae for generated statistics, such as adding up an entity’s toughness, size, and armor to get its overall health.
It’s a command line app. It’s not exactly user friendly. A lot of people wouldn’t even know how to open it. Not exactly a good situation for adoption of the system.
Most of the time, people work on games in a group, and this had no mechanism for that. In the process of trying to solve that problem (by integrating Dropbox or Onedrive) I ended up on a research rabbit hole to investigating real-time communication web technologies, and ended up deciding that a server running a socket system would be better in the long run than a console app.
What I learned
Console App Design
Before this, the only console apps I’d worked on were utilities that did exactly one thing. This was the first time I got to write an interactive console loop, and it was actually a lot of fun. A lot simpler to deal with than all the stuff between the user and the code in something like Unity. But ultimately, only useful for niche use cases.
Prototyping is valuable
Though this app is consigned to the depths of history and a blog post, it did really help me organize how I wanted SGS to work. Designing this app forced me to really consider the rules to the game in a way that I don’t usually get to do, and almost never see: at a level where a machine could understand the rules. That means being extremely literal and very careful. Additionally, while none of the code was transferred, almost all of the ideas translated directly over and I was able to make very fast progress on the initial versions of the SGS Website because of the prototype console app.