Official development blog

Building the Ultimate Roguelike Morgue File, Part 4: History Logging

The final part of Cogmind’s new scoresheet that I added was the “history log,” referring to a list of important events and actions that took place throughout a run, and when (turn number) and where (depth/map) they occurred. Even without any other supporting info from the scoresheet, by covering the highlights of a run in chronological order it can essentially read like a story of that run.

For some background research I went back to my old Morgue Files article and checked out the samples I’d collected there to see which roguelikes have such a log, what they record, and how it’s all represented.

Here’s DCSS, for example, recording notable enemy encounters, kills, leveling, skills, altar discoveries, god powers, artifacts, and more. It’s pretty detailed.

roguelike_morgue_file_player_history_dcss

Sample “Notes” from a DCSS run.

Angband is similar, although a little simpler, with notable enemy encounters, kills, leveling, and artifact discoveries.

roguelike_morgue_file_player_history_angband

Angband “Player History” sample.

By comparison DoomRL is a little less informative (no turn counts) and doesn’t show much in the way of character progression besides assembled items, but has some other content like instead reporting locations and related major events, and also an exclamation! point! after! almost! everything! Exciting!

roguelike_morgue_file_player_history_doomrl

Sample “History” section from a DoomRL morgue file.

I like the format and level of detail in the DCSS “notes,” and decided something along those lines would be most appropriate for Cogmind.

Architecture

Architecturally speaking, on an individual message basis the history log is actually quite similar to the regular in-game message log. Both require:

  • Support for variables: Although a single message won’t really be needed beyond its own standard context, some of its content details may depend on the situation, for example having acquired a certain item would need to insert the item name as part of the message.
  • Per-message knowledge settings: Not all messages are always known, even if some event did technically occur. There could be different general conditions determining whether to actually include the message.

Here are some of the messages I defined first while testing the system, to demonstrate what I mean and how they’re designated in the data:

cogmind_history_message_definitions

Example history log message definitions.

GLOBAL indicates the message is always shown when triggered, not requiring any generic condition (other conditions can obviously be applied outside the message code itself, when determining whether or not to trigger it in the first place). ACTION indicates that the message will only be recorded if the actor who carried out said action is visible to the player at the time. POS means the location must be within the player’s field of view. Within the message itself, %1 is a variable, and some messages may use additional variables, e.g. %2, or none at all.

Considering the various potential sources of noteworthy events, I also needed to add more than one way to trigger a history message to be added to the log. Aside from hard coding some triggers in the source, especially where they tie into mechanics (or simply hard-coded content), a lot of the history messages would naturally be triggered by other related content, so the architecture needed to support triggering by dialogue, terminal entries, and event scripts, all of which are stored outside the source code. So each of these systems needed to be updated to understand new bits of formatting that would enable them to add history messages to the log when applicable, and even inject variables where required.

cogmind_history_message_trigger_samples

Demonstrating three data-based methods of triggering history log messages.

As for the internal message data organization--everything exists in a single file! I debated this approach with myself for a while before actually starting on the system, but after working on it for a while it became quite clear there’s no debate at all. With so many possible sources of history data, I need a quick and easy way to see what does and does not already have a message associated with it, all in one place; that and an easy way to check everything for grammatical consistency.

Even while working on the system and much of the history message content was relatively fresh in mind, I still often found myself going back and searching for existing messages for modification or to confirm certain aspects of them, and having them all in one place saved a great amount of time there. This means both more efficient work and higher-quality results. And imagine how inefficient a decentralized approach would be down the line during updates when all this stuff isn’t fresh!

Here’s the external file as it stands now (albeit not shared clearly because of course it’s full of spoilers :P):

cogmind_history_message_types_312

Over 300 history message definitions.

The largest individual source of history messages, event scripts, originally got syntax to automatically define new history messages in that file, creating them within the script itself without any additions to the code or by explicitly adding new history message definitions at all. This made it really easy to add new messages, but then they’d be “invisible” to the data and code, which could eventually be problematic for research and consistency analysis. At the start of this history log project I had originally defined about 60~70 messages that way before realizing this was a terrible idea. I’m glad I backtracked on it and moved all those definitions to the single file. Everything is together in one place now. Whew.

Message Types

The architecture is the easy part, after all it’s just storing a bunch of message definitions and spitting them out when called upon. But having worked on Cogmind for six years now, yeah there’s a ton of content to include in history messages, or at the very least considered for inclusion. This required going through a whole bunch of stuff, and ultimately became a huge project on its own that consumed as much time as much as all other parts of the scoresheet 2.0 update combined: two weeks!

That said, I don’t regret waiting until this year to do it. Honestly this is better because although it’s a lot of work, it also means the results can be even better because all of the needs of the system and various different message considerations can be taken into account at once, finding the best approach that works for everything, rather than making decisions without being sure of what might come in the future (and possibly having to redo old work).

The biggest question when starting out with content is the desired level of detail. There’s a clear spectrum in our sample roguelike files above, from maybe one message per floor, to a few, to even many. What kinds of events are important or notable enough to log and serve as a reference for what happened during that playthrough? What’s the threshold that will produce a fun and/or useful history log?

This of course also depends on the intent of the log. What’s it for in the first place? In my mind, there are a lot of possibilities here:

  • record notable achievements that might not appear elsewhere in the scoresheet
  • remind the player of their feats, and challenges faced
  • shed light on various otherwise “hidden” aspects of a run for other players reading the scoresheet, essentially those things that numbers and item lists won’t fully reflect
  • for experienced players, knowing events the player did (or did not!) interact with can help give an idea of the play style at work
  • showing the specific order in which everything played out within an individual map is also instructive with regard to style, or just trying to reconstruct the run for other purposes of understanding or analysis.

On this last note, aggregate scoresheet analysis will also become even more interesting, since we can get a much clearer picture of parts of the game players are interacting with that haven’t ever been recorded before--lots of encounters and environmental factors that we know and love from player discussion and anecdotes but that previously required proactive discussion to bring out will now be accessible in every scoresheet!

The history log should effectively be kinda like watching a recording, only much (much) faster, and not nearly that detailed since we don’t need to know all the common stuff that goes on--so more like a short story or concise summary. In any case, it certainly won’t be as verbose as the message log, and it won’t include anything insignificant, making it more worth reading or skimming through.

Note there will be some overlap between the history log and other parts of the scoresheet. For example both include ways to see how the player’s build evolved over the course of the run, but each is a different way to examine the same information--in number form it’s good for analysis, and extremely condensed, whereas in the history log it’s spread out in chronological order among the other events.

But that’s also the kind of information that gives other history messages more context, and the history log should also serve as a standalone representation of the run, not requiring any of the other data to supplement or be cross-referenced against it.

So what kinds of message types are we looking at here? Well, there are quite a few categories! Here’s an overview:

  • Exploration: Evolving and entering a new map
  • Plot-related stuff: This is a category that other roguelikes don’t really have, but is clearly a centerpiece of the history log, knowing what major events and NPCs the player interacted with (in the past this function has been partially served by the “bonus points” scoring breakdown, which was the impetus behind expanding that part of the scoresheet over the years, but a dedicated history log will do a much better job)
  • Encounters: Non-plot special encounters are also an important part of the experience since most still have a mechanical/tactical/strategic impact on a run, so they should be included as well (and there are a lot of these…)
  • Enemies: Spotting dangerous foes and taking them down is worthy of the record
  • Items: Finding and using very special items definitely needs to be in there
  • Environment interaction: Destroying special machines or otherwise doing something that affects the environment

The screenshot below was taken from my notes that I referenced as I did the work, listing all the game data I had to parse for content and mechanics worthy of inclusion. Note it includes some SPOILERS if you look closely enough, though most of the low-level details are probably pretty cryptic with all the abbreviations anyway. The notes also mention some things which were not actually implemented, being rough notes and all…

cogmind_history_message_type_notes

Implementation notes for history message types.

After a couple weeks of work Cogmind reached its current total of 312 history message types! I’ve been making some adjustments as patrons and myself do prerelease runs with this system and examine the history logs, but overall I’m pleased with the results.

Examples

See the History Log in action with these recent recent Beta 9 runs:

cogmind_scoresheet2_history_log_sample1

Splatting relatively early on. I’m sure we’ll see a higher ratio of Garrison deaths in Beta 9 considering the new system accessible within.

 

cogmind_scoresheet2_history_log_sample2

Another relatively short run, escaping the Assembled, visiting the Exiles, then zipping through the caves and attracting a bit too much attention!

Branches and anything plot-related will tend to have more of the unique messages, as will late-game content like finding and using cool parts, so a lot of the longer runs also make for more interesting reads.

For some additional expansive examples, you can check out the history logs from these prerelease runs by CptWinky:

A Note on Grammar

At the end of the message writing and coding process, I went back over it all and did the usual spell check and parsing for grammatical consistency, then decided to remove most articles.

I had most of a system in place for handling a/an/the/[nothing] before variables like item names, but 1) it wouldn’t be perfect in all cases without a lot more hard-coded data (which would take a while) and 2) it wouldn’t be consistent with the game text itself, which intentionally avoids that sort of thing in message logs (keeping it more dry/robotic/clinical).

I did kinda like the fact that it reads more like standard English with articles in there, appropriate for the somewhat “story-like” history log, but then for the same reason it also kinda wasted space, adding characters unnecessary for understanding the text and therefore potentially slowing down visual parsing.

Non-variable-based messages, however, retained some of their articles, especially “the” in a number of cases such as special encounters and plot-related events.

cogmind_history_log_grammar_pass_articles_the

Highlighting every “the” for a grammar pass on the history message type definitions.

Extended Feature?

Another interesting idea related to history logs is allowing players to manually type notes to be added to the log as well--just enter a note and it’ll be appended to the list at that time, recording the turn and location. It sounds fun, but I’m not sure about allowing players to insert data into scoresheets like that :P

Or perhaps only include these personal notes in the local version of the scoresheet, just not in the official uploaded versions?

This is the fourth article in a four-part Building the Ultimate Roguelike Morgue File series:

This entry was posted in Dev Series: Ultimate Morgue File and tagged , , , . Bookmark the permalink. Trackbacks are closed, but you can post a comment.

2 Comments

  1. Joshua
    Posted August 27, 2019 at 5:25 pm | Permalink

    The mid-length and lengthy scoresheet links both link to the same scoresheet.

    • Kyzrati
      Posted August 27, 2019 at 8:07 pm | Permalink

      Whoops, apparently forgot to switch the link over for the second one, done!

Post a Comment

Your email is never published nor shared. Only the anti-spam entry is required. See here for the privacy policy.

You may use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>