Scene Tags

Since Six Ages is our second iteration of a storytelling game, I wanted to make sure it would be easier to do certain things. Being able to categorize scenes was something that was easy to improve on, since in King of Dragon Pass, everything was a special case. For example, when we wanted to make sure you got to deal with tribal disputes when you were a king, we had to list them all.

switch (COpal::DieRoll(4)) {
 case 1:
 scene = scene_R397TradeDispute;

case 2:
 scene = scene_R398MagicalDispute;

case 3:
 scene = scene_R399PoliticalDispute;

case 4:
 scene = scene_218ComplainORama;

This was fragile — scene_218ComplainORama was actually a fairly late addition in version 2.x, and I had to make sure that it was included in that code.

Likewise any news that was considered a random rumor had to be listed. This at least was data instead of being buried in C++ code, but it was still separate from the actual news.

SceneIDT CScene::gRumors[] = {

So I came up with the idea of tagging scenes. Here’s what the OSL documentation says:

Tags begin with @. They can be any word (e.g. @help, @hurt, @tutorial, @actThree, @endgame, @notEnd, @codeTrigger, @frequent, @infrequent). Tags can be purely informational, only a few have predefined meanings. Feel free to invent tags. Tags could be used when triggering scenes at random. (For example, if the player is doing poorly, there could be an increased chance of an @help scene. If the player is very wealthy, it might be time for an @hurt scene.)

So there could be an @kingly tag and an @rumor tag. Implementing a random rumor can start with something like

rumors = [self scriptsWithTag: @"rumor" ofType: type_News];

which searches all scripts for the tag (also choosing only news scripts). A typical rumor will include the tag

news: news_R14Quake
speaker: WhoHasHighestSkill(ClanMembers, 'magic)
text: I don’t have to tell you about the earthquake, since everyone felt it.

Most of the tags are indeed invented (usually by Robin Laws as he writes scenes). To help check on consistency, the scene compiler outputs a report of tag usage, which lets us make sure it’s consistently @dwarves and not sometimes @mostali or @dwarfs. A few are used internally (e.g. @nameEntry marks a script that needs a name field added to the UI), but most of these let us control the story.

For example, Robin can create a scene where you perform anti-dwarf magic and write, “Scenes tagged @dwarves do not trigger 30 + q seasons,” which can be coded

t = "@dwarves"
w = 30 + q
triggerWithValue code_EnableTag t w

(This takes 4 lines instead of two because of some limitations in the OSL scripting language.) A typical dwarf-related scene would then be tagged

scene: scene_37DwarfTrade
scene037, left, random, @dwarves, @trade, mayRepeat

The arbitrary tags also help implement the magic system. For example, a magical effect can be defined as

 name = "Elf Friend", tag = "@elves",
 explanation = "Helps our dealings with the Aldryami"

which gives a bonus to any scene that has been properly tagged. One of the tasks to do once all scenes are coded is to verify the tagging for magical blessings that depend on it.

Robin extended the idea with what I call dynamic tags. For example, he came up with a scene which could have a variety of outsiders. So he wrote, “scene gains tag @dwarves.” The implementation is

AddSceneTag(ThisScene, "@dwarves")

Obviously, making sure a scene has the right tag for a random outsider is important when there are blessings that are tied to tags.

I’m also making use of tags to help ensure correctness. For example, sometimes a scene wants to trigger another scene years later, but perhaps a character in the scene is off exploring when the time falls due. We handle this by triggering a code fragment, which can handle whether the character is exploring, died, or whatever. This sort of check is usually added once we start testing, and it would be nice to make sure that we caught every place that triggered the delayed scene. So the scene compiler makes sure that any scene marked @triggerFromCode can’t be triggered directly (but only from a code fragment tagged @triggeringCode).

And our unit test that runs every script simply runs them in an arbitrary order. Scenes may rely on having been run in a particular order, so the unit test may need to do setup that would have happened in normal execution. So there are tags like @unitTestWithClan and @unitTestWithPerson. This isn’t necessarily an exact match for normal execution, but it works for this level of brute force testing.

By the way, the @help/@hurt idea is not implemented at the moment. I’m not sure if it will be, but the flexible tagging system makes it easy to add at any time.

Sacred Time Recap

Several King of Dragon Pass players requested a way to see what their Sacred Time allocations of clan magic were. In most cases, advisors will remind you on the relevant screen (especially in version 2, which added a lot of advice to the management screens). For example, in the Magic screen, if you put points into Quest magic, someone will have the advice “We prepared for a quest during Sacred Time. Our chance of success is good” (assuming it’s not trumped by some other circumstances).

Magic ScreenWhen I began Six Ages, I wanted to have a centralized place to see everything. Until recently, it didn’t seem like it would fit. Then Robin Laws suggested showing some other information, and I realized that the Sacred Time allocations could be added as well. (The slider lets you choose to see the gods, or the new other information.)

Concept Inspiration

Raven ConceptSomewhere along the line, Jan Pospíšil drew a concept sketch that I liked, but that didn’t seem right for a member of the player’s clan (which was the original thought).

Recently, Robin Laws and I were going over the interactive scenes that are currently in the game, and figuring out what was lacking. He came up with a new scene that seemed like it perfectly fit the sketch.

So the scene is now written and coded, and we’ll be doing a full illustration based on the quick design. Sometimes good concept ideas never make it into the game, but I’m glad this one can.