Official development blog

Sorry macOS users, but Apple has gone too far for some of us devs

Years ago when Cogmind was first released, I kept an open mind about the future prospect of releasing an official Mac version. Cogmind is my first commercial game, after all, and having only previously released hobby projects as freeware, and only on Windows, I couldn’t be too sure what supporting additional platforms would entail. So I took a wait-and-see approach about whether to provide some form of official support for Macs.

In the meantime I’ve just ensured that Cogmind (and my other software) works perfectly via Wine and similar third-party solutions, but on Steam of course I could never mark it as having Mac support since it’s not a separate download that runs on its own, and whether or not to put together something that would fit that requirement stayed on the backburner as I worked towards 1.0.

Well, by now I’ve definitely waited long enough, and seen enough, to come to a reasonable decision: Officially supporting macOS is simply not feasible for me.

Regarding the timing of this decision, if you follow indie game developers, communities, or news, you might’ve heard a lot of noise lately about Apple, and sadly I’m here to add another voice to that chorus.

twitter_christer_kaitila_macOS_thread

Dev Twitter talking about Apple’s latest moves

Clearly I was already on the fence about the whole Mac thing before, otherwise it might’ve happened earlier (I did spend a while researching and testing the potential for a solution on Macs, in preparation for a potential future public release), but Apple has time and again taken actions which are clearly hostile to developers as they continue the trend of building and reinforcing their own walled garden.

Among their latest moves, Apple’s newest OS drops support for 32-bit applications, which will unnecessarily render a huge library of software and games unusable on Macs, compared to Microsoft which does a great job of maintaining Windows backwards compatibility in that area even as most modern software tends to be 64-bit. This makes it far easier to hold together Cogmind’s existing tech so I can focus on things players actually care about, like more features.

More importantly, Apple will require developers to register, pay an annual fee, and notarize every build they want to distribute, altogether drawing devs yet closer into the tightly controlled black box of Apple’s world.

So not only would I have to buy, learn, and update their ridiculously expensive hardware, I’d also have keep paying them money, and force both myself and players to suffer a slower build turnaround time and other issues all so Apple can tighten their grip on their users and developers. Nope.

Unreasonable Burden

Making games is already challenging enough without platform holders adding to that burden without anything to offer for it.

The notarization requirement is especially terrible for games that get lots of updates (roguelikes! Early Access!), when I might even want to put out several builds in a single day. My normal release process? Simply compile a build here, copy the files into the Steam backend and *bam*, everyone has access just like that--I don’t need to insert a third-party black box into that arrangement.

cogmind_uploaded_steam_builds_beta_8.1

The Steam backend showing Cogmind builds uploaded with modifications and fixes in short order for one of this year’s releases. This is pretty common!

(Note: My DRM-free release process is more or less the same: Simply zip up a new build and upload it to the release site.)

So yeah there are more hoops to jump through, but beyond the burden is something more worrisome: Basically I don’t trust Apple to care about my interests as a developer.

They have a history of dev-hostile actions that are pushing developers away from their ecosystem, which I guess is an inevitable side effect of having built a successful business around walled gardens and trying to lock everyone inside--some will stay, but those with good options elsewhere won’t.

Uncertainty is already a huge risk factor in game development, from design issues and technical hurdles to market forces and life in general, we don’t need more uncertainty coming from high places that we don’t have any hope of influencing, much less companies with a poor record in the first place.

Non-Apple Roadblocks

Apple aside, I should also cover the reasons that originally had me on the fence to begin with, one of which I’m sure the average Mac user will also be quite familiar with: economics.

Naturally there’s a non-negligible additional cost to developing and supporting an additional platform, one that varies depending on tech and know-how. Assuming time isn’t a factor, sufficient sales volume might make it worth the investment to support other platforms, although this is harder to do without a more mainstream game (that also sells well!) where the inevitably tiny ratio* of players using Macs is still a respectable number, as opposed to working on niche games like I do :P

(*Approximately 5% for your average roguelike, based on the data I have.)

To me, even more important are the non-financial calculations, since Mac players also tend to require a greater amount of post-release customer support, and I’m just one guy…

halfway_support_stats

Mac players account for 4% of sales but 50% of support requests, one of many such examples I’ve seen from dev data over the years, including from other roguelike devs.

Of course Mac support request ratios will vary by game due to architecture and player base, but there’s little question that they’re higher in general, which for me is a problem because I’m only really familiar with Windows. Being able to remotely troubleshoot issues is already challenging enough on a system I’m quite familiar with, but at least the years of learning are behind me, that and I can manage a reasonable number of requests. I wouldn’t be able to offer the same level of help to those using other systems, in which case it’s better to not put myself in that position in the first place.

It’s somewhat easier for a team to handle multiplatform support, whereas I’m mostly on my own here doing all the design and programming (in my custom engine--this isn’t “push button to release to platform B” xD) and marketing and all sorts of other things.

What now?

What does this mean for the future? Unfortunately given Apple’s trajectory here, I can’t see doing anything official for macOS. I’m all for accessibility and making it possible for more people to enjoy the results of my work, but this one’s beyond me. Gotta stay sane, and Apple sure doesn’t make is easy!

I will continue to make sure Cogmind (etc.) is compatible with Wine itself, especially since it also works nicely on Linux (and Proton under Steam), so for as long as it’s possible to run it through Wine on a Mac that will still be an option.

Of course, you can also try to stay on an older macOS, though I guess that will only get you so far. It will, however, enable you to use all the other software that’s about to be deprecated as well xD

In any case, hopefully everyone can understand my choices here. Not easy choices to make, but it’s important to stay realistic…

Addendum: I originally wrote this post just as a collection of thoughts and data that I could refer to when asked about Mac support, but in sharing it on Twitter it ended up stirring up a fair amount of discussion!

Posted in Gamedev | Tagged , , | 34 Responses

Quicksaving and Restore Points

For me, permadeath is a huge part of the roguelike experience. When you make enough mistakes that you lose--that’s it, it’s over. No immediately starting from a safe checkpoint not long before in order to try again, no in-game benefits carrying over to future attempts. Each run is an isolated attempt in which you’re armed only with your knowledge and experience, where the lack of second chances clearly raises the stakes, and the consequences of each and every action feel that much more meaningful compared to games where you can simply say “oh well whatever I’ll just load my last save.” Taking on added risks has an all new meaning that fundamentally changes one’s approach to decision-making.

In that vein Cogmind has been strict on permadeath for its seven years of history to date, but things are [sorta] changing…

Cogmind today is a much longer game than it used to be (or at least it can be depending on play style and goals), and some people don’t have the time or desire to be punished by losing a lot of progress/time to their mistakes, or maybe they simply don’t have the time to spare but are still eager to enjoy more of the game. These are the same people for whom multiple difficulty settings were added a couple years ago. It’s an important form of accessibility, and while it isn’t for me, features like difficulty levels have added a lot of value for a decent chunk of players (no doubt after even more people discover them following the latest visibility improvements).

The same can be said about the ability to save and load progress. If an additional subset of players can better enjoy a roguelike once they have the ability to easily load a save slot, then adding such a feature seems like a good idea already.

People more inclined to want or need this feature are already more likely to do their own save scumming anyway, so why not make it easier? Well, technically there are arguments on both sides of this issue, both sides with merit, so for Cogmind I’ve ended up opting for a middle ground on this particular feature.

So yes, Cogmind is getting it’s very own save/load feature.

But it’s “middle ground” because this feature is not included in the highest difficulty setting, Rogue mode. I decided I want to preserve that mode as the “intended/designed way to play,” and a significant chunk of people who will enjoy the Rogue setting in the first place are going to be into the challenge of traditional roguelikes and permadeath anyway.

The two other difficulty modes, on the other hand, both support the designation of an arbitrary save point which can be restored at any time.

When in either Adventurer or Explorer mode, the game menu displays two new buttons for controlling the “restore point.”  As you can see below, if there is a previously saved point to load, the load button becomes available and shows both the location and turn number to be restored.

cogmind_saveload_game_menu

Accessing the save and load feature from the game menu.

The feature is also accessible via keyboard without the menu, simply by using Ctrl-F8/F9 as quicksave/load buttons while in the main UI.

In terms of architecture, Cogmind was of course already capable of saving runs to be continued later, and even arbitrary input-based saving and loading because I rely on it heavily for testing and debugging. From there it eventually became a Wizard Mode feature available to some patrons starting earlier this year. But the next step was a big one--debugging and wizardry are fine with some limitations or quirkiness, whereas a public feature needs to be accessible and robust!

For one, saving and loading need to also be available from the game over screen, in case the run ended prematurely but the player wants to go back and try again from their save point. So that was yet another expansion to that screen, adding a new row to the bottom if and when that option is available.

cogmind_saveload_gameover_menu

Restoring a save point from the game over screen.

For convenience that also displays the save point’s depth and map (if the latter is known to the player).

Overall there were a surprising number of technical considerations behind building a quicksave system that would be compatible with the rest of the game design. It ended up taking a couple days to work through all the possibilities.

Like the game already maintains a save file for the current run, the one that is restored if you exit the game and come back later--should this manual save override that? No, that would conflict with one of the goals of having a manual save in the first place: to be able to go back to an earlier point than the most recent progress. Clearly this needs to be a separate file, meaning this system exists outside the normal save system.

So if you die and never explicitly made a save point to recover, this approach would imply that’s the end of the run there’s no option to go back. To prevent that situation, I implemented “automatic manual save” behavior, whereby if the player has made no save point of their own, at the beginning of each new map the game fills their optional save slot with one automatically. That way if they lose, the game over screen will still present them with the option to load back to that point despite having set no recover point of their own. (Also rather than blindly saving on each new map the game is somewhat smart about it and will skip the save if the player is in very bad shape :P) Anyway, allowing players an opportunity to “try that last map again” is possibly a good way to learn without having to go all the way back to the beginning.

On top of that file we also have the previously implemented periodic autosaves--backups in case a save file somehow gets corrupted (but more often for debugging purposes), and the save file used for time travel (yes that’s how that feature is implemented :P), so together with this new type, there are now four different possible types of save files for the current run!One of the other quirks of the new quicksaves is that they’re only valid in the current run. In other words, once you start a new run the save point from a previous run is no longer accessible--that would get rather confusing, suddenly switching back to a different run, and it would cause a number of other problems too, so I had to put that restriction in place.Altogether the save system is a little different than what’s found in non-roguelike games, since it was designed to work alongside the original “run-based system with automatic resuming on return” more common in traditional roguelikes, while combining the benefits of each.

I’m pretty sure that even though it’s now an “official feature” of two of the three difficulty modes, runs that have been quickloaded will be excluded from the leaderboards.

The HUD also got a new indicator that pops up in the top-right corner when applicable, showing the number of times the current run has been manually loaded so far.

cogmind_saveload_HUD_loaded_count

The load counter as it appears in the HUD, here having manually restored to an earlier point twice before on this run.

And for those who want to play Adventurer/Explorer modes but don’t want to even have the quicksave option available to them (to avoid the temptation to use it), I added a new advanced config option to disable that feature entirely, in which case the hotkeys won’t work and none of the relevant buttons will appear.

Posted in Design | Tagged , , , | 6 Responses

Rebranding Difficulty Modes

A couple years back I introduced difficulty settings to Cogmind. At the time I wrote about the benefits and drawbacks of difficulty modes in roguelikes, along with an introduction to Cogmind’s difficulty-related features, and overall I’ve been pleased with the results, but with experience I’ve discovered there is definitely room for improvement in a few areas, so here we go again :)

The Accessibility of Accessibility

Players have had a number of ways to discover that Cogmind even has difficulty settings in the first place. Aside from being described in the lengthy manual (which isn’t suggested reading for new players), it’s also buried in the packed Options menu (which many players won’t open, or if they do still won’t thoroughly explore) and mentioned in a tutorial message (which in Cogmind are fairly easy to miss depending on the circumstances since they’re not very in-your-face and just appear in the message log).

These haven’t done a very good job of advertising difficulty modes, meaning lots of players still don’t even know they exist!

Well there are also my blog and relevant announcements about these settings, but those are both time-sensitive and can only reach a subset of players--we need a good in-game method of teaching players that they have this rather important option available, something that also serves the long-term in that new players also have that opportunity. (Having separate leaderboards for each setting is yet another avenue of discovery, but again, only a subset of players actually check those.)

One of the usual ways games introduce a difficulty setting is having it be one of the first things players select when starting a new game from the main menu/title screen (maybe even the only thing between pressing New Game and starting!).

Cogmind does not have a main menu xD

For immersion reasons I always wanted to go straight into the game after the startup animations. Before release I eventually gave in and added a short title animation as well, which itself disappears automatically after animating so it’s not really a menu, but that was it. I didn’t want a main menu, which means forgoing the functionality that might normally appear there.

But over the years I’ve noticed a fair number of people were unaware difficulty settings are available, have to repeatedly inform players on forums and such. Even recently when I announced in various places that I’d be “rebranding” Cogmind’s difficulty modes (another topic we’ll get to below), despite the fact that rebranding implies I’m making changes to an existing system, some thought I was just now adding them even though they’ve been in place since 2017 xD

In light of my findings, I started to come around to the idea that it would probably be a net benefit if this important option was somehow placed front and center.

Considering my desire to maintain the flow of starting up the game (and lack of a main menu), along with Cogmind’s overall goal of avoiding breaking immersion where possible, there’s really only one option: add a new menu before even the title screen. There it wouldn’t interrupt flow, since once that’s chosen there at least won’t be any more need to stop the usual animation sequences and wait for input. And although I really like the idea of players starting up the game and having it automatically proceed through the opening into the gameplay, I’ve decided it’s worth making this exception for new players.

Cogmind has always defaulted to the hardest difficulty setting and expected players to lower it on their own if necessary (again, if they even know they can!) but this approach clearly has a number of drawbacks. Some of them I’ll get into later, but at this point let’s say difficulty setting obviously has a broad effect on the player experience, and players should start better informed about that choice, and take an active role in making it. Cogmind may have been designed for its hardest difficulty mode, but on that setting it’s also too punishing for a fair number of people who would better enjoy the game with some survival aids :)

Dedicated Difficulty Menu

Enter the dedicated difficulty menu. This menu only appears the first time the game starts up, so it’s not like it’s becoming a permanent fixture before every run, but it’s nice being 100% sure that players know this is thing, and can start where they feel most comfortable.

cogmind_difficulty_selection_demo

Interacting with Cogmind’s new difficulty settings before the first run.

Selecting an option brightens it, and like most important decisions in Cogmind requires confirmation to continue.

Designwise my first instinct was to align the options horizontally, which overall works better when a user has to select from two or three options, so I was experimenting with some styles in REXPaint:

cogmind_difficulty_selection_unused_mockup

Early mockup of horizontal-style difficulty selection.

But the options were too verbose for that kind of treatment, at least with the content I wanted in there, resulting a lot of short lines which are annoying to read. A horizontal alignment that also looks decent would require fewer words and instead rely more on images or icons, a route I didn’t think was appropriate here.

One of the advantages of having a dedicated menu is that it has plenty of space for more words with which to describe each mode and set expectations, compared to the limited space available in the Options menu.

So the final version of my mockup was instead a vertical design:

cogmind_difficulty_selection_mockup

Vertical difficulty selection mockup.

I then used REXPaint to also draw out the subconsole breakdown for implementation:

cogmind_difficulty_selection_mockup_subwindow_breakdown

Mockup showing how each difficulty option is broken down into areas in the underlying architecture for rendering and input processing.

Expectations and Rebranding

Taking a closer look at the menu’s content, each mode has a name, a short two-word definition, and a longer description, along with an indicator of whether the mode includes quicksaves (which also introduces players to the fact that this is a feature).

cogmind_difficulty_selection_18

A screenshot of the difficulty settings menu, for reference.

The short definition exists to enable an at-a-glance understanding of the approximate level each setting represents, while the the longer description sets expectations more clearly. One could say that rather than naming the modes at all it’d be even simpler just label them by their definition, or even go with easy/normal/hard, and although that could technically work, the idea of “normal” being in the middle there would imply it’s the standard/intended way to play, when it really isn’t. Besides, the unique names are both thematic and more evocative.

The reason I’ve been calling this effort a “rebranding” of the difficulty modes is that besides the new menu, one of the biggest changes happening to the difficulty modes is that the naming scheme has changed. The substance of each mode is largely unchanged from its initial implementation, but how players respond to the new options as they’re presented will definitely be quite different.

Since difficulty settings were introduced, Cogmind has silently defaulted to the hardest/intended way to play, and the two other choices were available in the Options menu as “Easy Mode > Yes” and “Easy Mode > Very,” or so-called “Easier” and “Easiest” mode.

This original scheme was me being way too conservative, emphasizing the “proper” way to play, and everything else is below that. This introduced bias and people didn’t really want to proactively select lower modes due to how they’re named. For some the nomenclature didn’t matter much, but for others it could affect their decision to switch modes or not, even if subconsciously. Would you rather lower the difficulty setting from the default to “easier,” or switch from Rogue to Adventurer? Even if the end result is the same, for some people the answer is different for each question, regardless of ability or need!

Psychologically, having the default difficulty set high and later asking that players lower it in order to better enjoy the game can be harder for them to stomach. Basically, by letting players start out with their own informed choice, and using less loaded names (Explorer, Adventurer, and Rogue are all cool :D), we can side-step multiple issues.

Beta 9

When this feature goes live in the next release and everyone’s difficulty is reset so they have to see this menu for the first time, it’ll be interesting to compare the percentage of players using “non-default” modes (excuse me, Adventurer and Explorer! :P) vs. past data. Currently only about 9.5% of players are using those modes, but I’d be willing to bet that the ratio is going to grow by a decent amount in the future.

cogmind_leaderboard_excerpts_easier_easiest_beta_8

Sample “Easier/Easiest” mode leaderboards as of September 13, 2019, representing only a small minority of players. Still Beta 8, as you can see, pre-rebranding!

In any case, it was important to get this rebranding into the game before 1.0, when even more people will be starting it up for the first time. The goal is to focus on making the game fun for new players, and many who might otherwise enjoy it could be turned off by automatically experiencing the hardest mode as the default. Defaulting to the hardest mode might make sense for a traditional roguelike, but less so in the context of modern games. It is not uncommon for games to not default to their most challenging level, though I think in this case it’s better to not have a default at all, in the end possibly saving the player time.

Besides, the option to go all-out roguelike masochist is still sitting right there for those who want it, and at least they’ll know what they’re getting into ;)

For patrons, there were more comments and discussion of this article over on Patreon here.

Read the followup to this article, about the other big change coming to difficulty modes.

Posted in Design | Tagged , , | Leave a comment

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:

Posted in Dev Series: Ultimate Morgue File | Tagged , , , | 2 Responses

Building the Ultimate Roguelike Morgue File, Part 3: Mid-run Stat Dumps

In the first part of this series I described how I didn’t want to get into fundamental changes to the stat system while in the middle of development, leading me to completely avoid modifying the underlying architecture despite greatly expanding the number of stats recorded. Among other limitations, this is also why we’ve gone for years now without a way to examine stats in the middle of a run. But that ends now!

Clearly “mid-run stat dumps” (AKA character dumps) are a really useful feature in a roguelike of decent length, and even more so in a roguelike with an active community that enjoys discussing strategy. Some reasons to support dumps:

  • Share with others the full details of runs still in progress
  • Check the current scoring breakdown and other stats
  • Help track progress towards in-run achievements or other goals (this would be nice to have in game, but Cogmind has hundreds of achievements and it would be a ton of work to implement a way to review this progress in game, so I’m not sure if that will exist)
  • Make the system easier for me to debug, and work with in general (I have been loving the ability to output stats at will since this feature was completed! Previously having to self-destruct and reload all the time was a pretty annoying and repetitive sequence compared to just a single command…)

I’m sure there are more, or more variations on what I have above, but the point is: they’re useful!

Form and Architecture

Years ago when thinking of this kind of feature, I envisioned a mid-run stat dump as an alternative short summary of the run’s progress and (most importantly) current situation, different from scoresheets. But other than the advantage of keeping it concise, that doesn’t really make sense when we already have a great source of comprehensive info to draw on--the scoresheet itself. Stats for the scoresheet are maintained throughout the run and (mostly) ready to be output at any time, so why not just create an “in-progress scoresheet” on request?

So that’s the form we’re aiming for here, basically just a scoresheet-on-demand :)

This did, however, require a fair bit of work.

The original code for stats in Cogmind was quite simple: Have a class storing all the data in a few simple arrays, and when the run ends mix it in with whatever other player/run data is necessary, rework it all into a printabble format, and write it to a file.

The problem is all of this was done in a single function--there was essentially no division of responsibilities, although clearly we can’t use an identical process for mid-run stat dumps because there can no longer be an assumption that the run is ending! For example the original code would even change game data as part of its work without worrying about negative repercussions, since the run was ending anyway :P

And as you’ve seen in the samples before, Cogmind’s scoresheets contain quite a lot of data, so there was a lot to go through…

cogmind_score_system_architecture_notes

The scoresheet architecture diagram that’s been sitting on my desk for months now, from when I first sketched out the difference between what we had and what we needed.

In terms of architecture, the main goal would be to migrate everything from a linear system to a properly compartmentalized one which could handle three separate possible outputs independently of each other: scoresheets, mid-run stat dumps, and uploading of online stat/leaderboard data.

The most important part here was separating out all the necessary data into a new intermediate format, basically a new data structure outside the game data that contained everything necessary for any of these purposes without relying on the game. Technically as described before I already had such a separate structure outside the main internal game data, but it still relied on a lot of support from the game data itself because that was what was easiest at the time :P. Now the code has been restructured around a whole new intermediate data structure that purely contains all the raw data necessary for any desired final form.

Although I haven’t done it yet (though it will come soonish), this is also a stop on the road towards having an online stat database, so the dump feature’s development comes at just the right time.

Content

What’s inside a mid-run stat dump? Well, it’s almost identical to a scorehseet, simply stopping at the current point, except for one main difference: there’s no Result to show in the header. I mean… of course there isn’t :P

That said, it’d still be nice to use the expected “Result” line in the header to indicate that the run is still in progress, and show something else interesting instead (rather than just “in-progress,” “stat dump” or something else mundane), so I came up with some new content for it.

I thought it would be neat to provide a short one-line summary of the run as it stands, which in cogmind basically boils down to two things: your build, and your status. Pretty simple, although each half of this would require a new system and take some time to realize.

Build Categorization

I started out thinking the first element would use terms along the lines of what we’ve come to be using in the community, e.g. “flight hacker,” “infowar combat,” and so on, sketching out some related notes on how to make that determination.

cogmind_build_classification_initial_notes

Initial incomplete build categorization notes.

At first it looked like it might be in the form of [prefix][main][subtype] (e.g. infowar combat hacker), with the possibility of other hybrids mixed in there and whatnot. It’s a practical approach, but also given the kind of options I saw it was kinda boring and probably overcomplicated. (During this process I drew on my character archetypes post about Cogmind from a while back on r/RoguelikeDev, which you might find interesting if you haven’t seen it before.)

So I thought what if we had it instead detect builds as something more akin to true classes? The focus would still be on clear, functional names rather than creating whole new ideas unique to Cogmind or anything crazy, but it would be more fun than the dry (and even more limited) terms we normally use.

Unlike many roguelikes/games where you select a class to play, as you know Cogmind is a dynamic, flowing experience and it’s interesting to explore classification of these builds as they happen.

To this end I came up with a chart of characteristics and 13 names to go along with them. In the code, a long series of nested conditional statements determines which “class” most closely applies to the build.

cogmind_build_classification_base_source_sample

A sample from the beginning of the base class determination--it continues for a while after this, filtering further and further down through possible builds based on state summary variables. Lots of if-elsing :)

Then, since a single name would have been too limiting and many more creative combinations can be captured via a two-name system (kind of like multiclassing!), I came up with an additional 15 special designations that can more specifically describe some aspect of the current class.

Unlike the main designation, this one is determined via a point system. Your build earns points in each category based on its parts (and sometimes other factors), and whichever category accrues the most points is applied as a modifier to the class. If nothing applies very well, then only a single base class is used.

Cogmind is not constantly recalculating your class. That would waste a lot of time, and also be pretty inaccurate when you’re doing things like transitioning between builds, or lose a part or two for just a little while or are in the middle of swapping things in and out for whatever reason. What I did there is have it not only calculate only every so often, but also wait until there has been a decent stretch of turns without any actions that affect your loadout, like attaching, dropping, swapping, etc. Only when your build is considered “stable” will it take another sample for analysis.

In the end this automatically determined “build class” appears in the format [special modifier]-[base class], where the modifier is empty if nothing really applies. Everyone starts as a Mutant, the default base class, since that’s more or less what Cogmind is at heart, a non-specific machine attaching a random variety of parts while not really capable enough in any one area to be noteworthy.

There are currently 195 multiclass combinations (plus the 13 pure base classes), some more specific and less likely to be discovered than others. Below are a few screenshots of my first test run in which I traveled and battled through five maps as I changed my loadout around and saw how the game classified it. For this purpose I put a nifty “class readout” right at the top of the parts list. (I wasn’t aiming for any particular builds, this is just what I ended up with, among others I didn’t screenshot.)

cogmind_build_classification_test1

Build: HAULER-TANK. Armor and firepower!

cogmind_build_classification_test2

Build: HAULER-GUNNER. A more offense-oriented combat build.

cogmind_build_classification_test3

Build: SKIRMISHER. Combat bot supported by infowar utilities.

Although the class indicator seen on the HUD there was originally for testing purposes, I later decided to include that as an advanced player option as well. It’s kinda non-immersive so I don’t want it appearing there by default, but it’s a fun thing for veteran players to turn on.

In designing the above system, I realized it could be expanded into an interesting permanent part of the full scoresheet, as described in the first part under the Build section. Thus we get two new scoresheet sections:

cogmind_scoresheet2_class_distribution

Sample scoresheet 2.0 excerpt: Class Distribution.

cogmind_scoresheet2_dominant_class

Sample scoresheet 2.0 excerpt: Dominant Class.

I’m happy to see that builds aren’t whipping all over the place, which would otherwise be likely if the classification system wasn’t stable enough. Having relatively broad definitions helps in that regard.

Builds also have a place in the History Log, to be described in Part 4 of the series.

But getting back to the idea of mid-run stat dumps and that Result line, this is where it all started, just to drop the class name in there like so:

cogmind_scoresheet2_dump_header

Sample mid-run stat dump header, with build classification and situation analysis.

There you can see we have the current build type, followed by a second piece of information we’ll get into in the next section.

Situation Analysis

Since we can’t describe the final result of the run, I thought it would be interesting to try to summarize the current situation in one word. This is determined on a scale from dire to wonderful.

cogmind_scoresheet2_dump_situation_types

The full situation analysis scale for a run’s status.

The default is “fine,” and a number of factors are checked to simply raise and lower the scale to arrive at a final value. So having some negative factors mixed with some equally positive factors will still average out to a “fine” situation, but having extra factors in a given direction will start pushing the rating towards an extreme.

The calculations are all quite simple, just checking factors that we has Cogmind players tend to rely on when considering what’s truly important in terms of how we’re doing. Some of these are pretty common to roguelikes in general, like having low HP is not good, being surrounded by enemies is bad, and you don’t want to be completely defenseless.

Here’s a general list of factors used to analyze the situation:

  • Core integrity
  • Speed (can’t be ridiculously slow)
  • Essential parts (missing power or propulsion is dangerous)
  • Spare parts (this is never included as a negative factor, but it’s always a good thing to have spares of everything)
  • Part condition (empty slots count against this)
  • Matter levels
  • Armed enemies and allies nearby
  • Known exits

The entire thing is only 77 lines of code, but seems to work pretty well. Like build classifications I added a current situation analysis indicator to the HUD for testing purposes so I could see how it was changing throughout a run, but I don’t plan to make this even an optional feature for players.

cogmind_situation_analysis_debug_HUD_sample

Current situation analysis debug mode view (you can tell it’s not for release because clearly it shouldn’t be lowercase).

If you want to see that in action, I was using it in this Beta 9 preview stream where I ran the debug version of Cogmind. Actually for a simple example, here’s a debug shot in -9/Materials:

cogmind_situation_analysis_sample

Situation analysis: “good.”

UI

Scoresheets are normally produced at the end of a run, so how do players get a mid-run stat dump?

There is a new command that can be used on the main UI, Shift-Alt-s (for “stats”), which creates the file and puts it in a new separate directory in the Cogmind file structure, /dumps/.

Of course this also needs to be mouse accessible, so for that we conveniently have a logical place to put it: the Records menu.

cogmind_character_dump_ui_demo

Dump Stats button in the Records menu.

Even just in prerelease so far players have already been using the stat dumps, not to mention this feature has proven quite useful for my own needs whenever it comes to testing or debugging anything stat related.

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

Posted in Dev Series: Ultimate Morgue File | Tagged , , , , , , | Leave a comment