Official development blog

Building the Ultimate Roguelike Morgue File, Part 2: ASCII Maps

Although there’s only so much information ASCII alone can hold without color or a supporting interface, it’s still worth it for morgue files to include a map of the area around the player at the end of the run for added context. For wins generally not so much, but such maps can definitely be useful for determining the tactical circumstances behind a loss. (That said, some wins while on the verge of death are only even made cooler with a final map showing a horde of hostiles on one’s tail!)

Last time I covered the general organization of Cogmind’s Scoresheet 2.0, but wanted to get into more detail with maps in particular since there’s a lot to say/show.

Other Roguelikes

Three out of four of the roguelikes I surveyed earlier for their morgue file content include an ASCII map within it.

ADOM’s morgue file is topped by a console screenshot taken at the end of the run, so naturally includes the surrounding area. Here I’ve taken a few maps from different morgue files to give you an idea.

roguelike_moruge_file_ascii_map_adom

ADOM morgue file map samples.

Unlike the others, DoomRL (DRL) reveals the entire map, regardless of whether it was explored or not:

roguelike_moruge_file_ascii_map_doomrl

DoomRL (DRM) morgue file map samples.

And DCSS morgue file maps show a relatively small area (interesting since it also has the largest maps of these three roguelikes), but are also the only ones to use Unicode characters--the other two are pure ASCII.

roguelike_moruge_file_ascii_map_dcss

DCSS morgue file map samples.

Cogmind Style

For Cogmind I decided to go with pure ASCII. One of the big limitations of exporting a text map this way is that it can’t show Cogmind’s machines properly. These are drawn with CP437 (extended ASCII), but I want the entire scoresheet to be widely compatible and not require any special encoding, which means we have to forgo Unicode. (Not to mention non-ASCII characters can look especially weird depending on the font, and because they’re narrow rather than square like in game, machines’ appearance is otherwise significantly distorted anyway.)

cogmind_scoresheet2_map_test1_fail

An example of what happens when character encoding can’t be interpreted correctly and requires manual/reader adjustment (not ideal!).

Well in most cases the type of machine is not terribly important, so I decided to draw all of them using a character not used elsewhere: the double quote. Interactive machines are certainly important, so their interactive piece is still represented by the appropriate capital letter, making it possible to distinguish that particular machine block from the rest. This is looking better:

cogmind_scoresheet2_map_concept1_floor_periods

The initial concept for Cogmind’s scoresheet ASCII map style.

Here I’ve also added a border around the map view, which comes in handy when near map borders, or when the known map doesn’t fill the available area. I left empty buffer columns to the left and right of the map, but not the top and bottom; this is because as a text file it will generally be viewed using a non-square/tall font, so the spacing looks more even this way.

In terms of area, Cogmind shows all known terrain and objects within a 50×50 area (the smallest), where areas outside FOV are output as whatever the player remembers to be there.

You’ll also notice that debris is completely removed (anything that would just be noise and interfere with interpreting the map shouldn’t be there, especially since there’s no cellwise brightness we can use to emphasize/de-emphasize various elements). This was actually kinda hard to do for previously seen areas and required a bit of a hack to retroactively “erase” debris ASCII without misinterpreting actual item knowledge.

At this point I started testing out various different map styles. Do we want to be able to distinguish between FOV and non-FOV areas? How should unexplored areas be represented? I did tests of each to compare… (see legends)

cogmind_scoresheet2_map_concept2_nonFov_floor_periods

ASCII map style concept 2.

cogmind_scoresheet2_map_concept3_fov_floor_periods

ASCII map style concept 3.

cogmind_scoresheet2_map_concept4_fov_floor_periods_unknown_marked

ASCII map style concept 4.

cogmind_scoresheet2_map_concept5_floor_spaces_unknown_marked

ASCII map style concept 5.

I decided the last one is best, as it’s not too meaningful to distinguish FOV vs. non-FOV areas, especally when doing so has the drawback of making the map harder to read. Much more important are the objects present, specifically the letters (robots).

On that note, I wonder if it would end up being useful to have a description of major features on the map, such as the names of meaningful robots and parts that can’t necessarily be gleaned from the ASCII map alone (like technically an ‘s’ could represent a weak robot, or a very powerful one, although cases like this aren’t too prevalent, and some can be determined through other context clues).

Anyway, I worked out some kinks in the content and did some more tests. If you’ve played Beta 8, you’ll probably recognize this place :)

cogmind_scoresheet2_map_concept5_in_EXI

Cogmind ASCII map style in Exiles lab.

Here’s what the caves look like in this style:

cogmind_scoresheet2_map_concept5_in_LOW

Cogmind ASCII map style in caves.

Then I went back and added a way to see the edges of the map (as with the advanced.cfg showMapBorders setting):

cogmind_scoresheet2_map_concept1_in_MAT_with_inner_borders

ASCII map style concept 1 with inner/map border.

But after all that work realized such a feature is pointless if going with a style that marks unknown cells, which reveals the map border naturally xD

cogmind_scoresheet2_map_concept5_in_MAT_without_inner_borders

ASCII map style concept 5 without explicit inner/map border.

Duh.

Samples

ASCII maps aren’t even in the public release yet, but patrons have been trying out the system in recent weeks and I’m already really liking the results as I examine scoresheets. Here are some samples from real runs…

cogmind_scoresheet2_ascii_map_sample_mines_assembled_demolishers

The classic Assembled and Demolishers in the Mines. Killed by a grenade.

cogmind_scoresheet2_ascii_map_sample_mines

Another sample Mines death, albeit to more mundane causes (a Swarmer patrol).

cogmind_scoresheet2_ascii_map_sample_complex_late_game_branch_dogpile

Getting dogpiled in a late-game branch. Mistakes were made.

cogmind_scoresheet2_ascii_map_sample_garrison_behemoth

Beta 9 makes Garrisons very inviting. But this doesn’t make them any less dangerous!

cogmind_scoresheet2_ascii_map_sample_exploded_area

Clearly there was some serious exploding going on here.

Fun

Not long after Cogmind’s map could be output in ASCII form for the scoresheet, I realized I could do something with them that I’d wanted to do for a while but didn’t have the means.

REXPaint has the ability to import text files (via the -txt2xp switch), so I could take map views from Cogmind and doctor them in the editor! I do a lot of map editing in REXPaint, but it’s mainly for prefabs, and of course it’s in the REXPaint->Cogmind direction. With the direction reversed, it’s easy to dynamically play with other font styles and colors using the editor’s features.

For fun I took a sample map and colored it in the original C64 font:

cogmind_rexpaint_c64_map

C64 Cogmind

cogmind_rexpaint_c64_map_original_ascii

The original ASCII text as it appeared in the scoresheet.

cogmind_rexpaint_c64_map_original_game

And a screenshot of the area in game.

I don’t have any practical applications for this right now, though I recall in Cogmind’s early years I did consider using something like this for testing purposes and/or to compile marketing materials.

Anyway, it’s a thing, but the main point is having maps improves yet another facet of scoresheets :D

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

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

Building the Ultimate Roguelike Morgue File, Part 1: Stats and Organization

In-game content is important, but for many roguelike players there’s a lot of fun to be had outside the game as well, be it reviewing records of one’s own runs, getting good insights into runs by other players, or relying on data to learn more about the game and engage in theorycrafting.

Back in 2015 I explored morgue files in various roguelikes and wrote about a potential future for Cogmind’s own so-called “scoresheets.” The original intent of these files, added for the 7DRL in 2012, was simply to report the player’s score for each run and give a few very basic stats. It was a 7DRL , after all, and there wasn’t a bunch of time for heavy record keeping.

cogmind_scoresheet_7DRL_2012

A sample Cogmind 7DRL scoresheet (2012!), very simple indeed.

In the days after 7DRL I wanted to run a tournament and thought it’d be fun to hand out achievements, but to do that we’d need more stats to base them on. Being outside the 7-day restriction I was free to spend some time on that and the “Stats” section got a few dozen more entries. You can see what the scoresheet became in those days in this example from Aoi, one of only two players known to have ever won the 7DRL version (the high scores, with links to scoresheets and tournament achievements, can be found here).

In the years since, aside from occasionally adding new sections to the file, the primary Stats section simply grew longer and longer as I tacked on entries for new mechanics and content, growing from 79 in the 7DRL (2012) to 328 in Alpha 1 (2015) to 818 in Beta 8 (2019)!

cogmind_scoresheet_sample_beta_8_Tone-190517-211751-0-172326_w6++

A Cogmind scoresheet from a sample Beta 8 run by Tone, which I’ve restructured horizontally because the full length is 24 pages! I’ve also shrunk the image down to avoid spoilers while retaining the general idea of its breadth, however if you don’t mind spoilers (this particular win spoils a lot!), you can check out this same run in its text form here. Note the full potential length of a scoresheet is technically even longer than this--many stats are not shown unless they’re relevant to the run, omitting them either to speed up parsing or to avoid spoilers, so this sheet for example is missing about 100 entries xD

As such a massive collection of stats, all in a single section, the format has really strained to do its job, badly in need of better structure to make it easier to both parse and understand. But despite the ballooning size, I intentionally avoided making any big changes to the scoresheet format for a number of reasons:

  1. The format was at least pretty simple, and if there any significant changes to its content I’d already have to put a bunch of time into rewriting the analysis code (used to produce the leaderboards and aggregate metrics), and that code was a mess and all going to be replaced one day for the final system anyway.
  2. Some players had built useful third-party applications based on the scoresheet format, so too many changes in the interim could break these unnecessarily.
  3. As usual, it’s best to wait on designing systems that touch a lot of other features until most all those potential features are in place, so that the supersystem’s design can take everything into account.

With Cogmind approaching 1.0 now, a lot of the content and systems are already in place, plus we have a lot more experience with run data and what people want to know about their/others’ runs, so it’s finally time to bite the bullet and build Scoresheet 2.0!

Preparation

To prepare for the transition to the new scoresheet, I finally exported all the existing score data parameters to an external text file. The original scoresheet data being as limited as it was, from 2012  it simply lived inside the code as a bunch of separate arrays. It was messy, and I never cleaned it all up  simply because I knew that one day it would all be replaced anyway, so may as well wait until that day in order to do it right the first time. This allowed me to merge quite a few arrays and multiple disparate bits of data storage into a single text file with contents represented by a single data structure.

cogmind_scoretype_data

A snapshot of Cogmind’s scoreType data, a new 897-line file where all the score/stats entries and their behavior are controlled.

Even just this first step made the whole system feel sooooo much better. Moving data out of the code makes it much easier to maintain, and less prone to errors.

The next question was how the new data would be stored, but that couldn’t be answered without a clear idea of everything that would go into the new scoresheet…

Aside from organizational concerns, and new content, the most significant aspect of Scoresheet 2.0 would be its recording of stats on a per-map basis. Scoresheets historically just had a single value per entry*, and it was clear we wanted more values in order to paint a better picture of how a run progressed, but at first I wasn’t sure just how many values to store--once per depth for some values? Once per depth for all values? Once per map for… everything?!

(*There were just a few important stats already recorded once per depth--remaining core integrity, for example--but these would be rendered obsolete by an even more detailed per-map system.)

At this point I did a couple tests, the first a layout mockup to compare the aesthetics of per-depth and per-map values, just to get my head in that space.

cogmind_scoresheet_additional_data_concepts

General data layout concepts for multiple values.

As you’ll see later, I decided the total should remain at the beginning to ensure all those values line up (since they are the most commonly referenced, after all), a separator line was necessary between the total and per-map areas (for easier parsing), and along with the map abbreviations it would be important to indicate their depth as part of the headers as well.

I also calculated out the text file size for such a scoresheet, to ensure it would be within reason given the number of stats we’re talking about here. Upwards of 800 stats recorded across 30 maps, for example (a higher-end estimate for longer runs), is 24,000 entries! Not to mention the composite run-wise values and all the other data included in scoresheets (lists, logs, and more). The result is definitely larger compared to Beta 8 scoresheets (around 10~25KB each), but a majority of these newer scoresheets should still fall within 100KB. Based on real tests by players so far, this seems to generally hold true, although the longest runs visiting over 30 maps will fall between 100~150KB.

If necessary to keep the number of values down, I was considering having only a portion of the stats do per-map recording, but eventually decided to instead go for consistency and keep the full set, especially since I could envision times when almost any given value might want to be known on a per-map basis. There are just a few stats for which per-map values would make no sense and therefore be confusing, so those are excluded.

On the technical side, to make the transition easier I simply had the existing “Scorekeeper” class change its behavior on receiving a new score value. Whereas before it would just merge values with a single list as necessary, now it has multiple sets of score values (a “batch”): The composite batch is each stat’s value for the entire run, while the map-wise batches store a separate complete set of stats for each map. So for example when the player destroys a target, that event is recorded in both the composite batch and the batch for the current map.

cogmind_source_scorekeeper_scorebatch

ScoreBatch with the primary score manager class, Scorekeeper.

While we’re in the source, below is the data structure storing all static values defining each scoreType found in the external file mentioned earlier.

cogmind_source_scoredata

ScoreData struct for holding score type definitions.

There are six different tally methods to choose from, with the vast majority of scoreTypes belonging to the default “additive” category, though a few require special alternatives:

  • HIGHEST: Takes the highest between the stored and new value.
  • LOWEST: Takes the lowest between the stored and new value.
  • STRING: New value only included if a string provided with it is unique to this stat (for example only wanting to record once for each unique object).
  • AVERAGE: Takes the average of all values.
  • AVERAGE>0: As AVERAGE, but ignores any zeroes.

Mockup

There would be a lot of rearranging data to come, all amidst adding new entries, so to make sure I got it right the first time, I needed a clear and complete goal to work towards. Thus in addition to the large collection of related notes I’d already accumulated, I also took an old-format scoresheet and modified that to create the target format, in the process taking yet more notes as I thought through the requirements, feasibility, and repercussions of each change. That mockup would serve as the initial blueprint to simply implement top to bottom, step by step.

cogmind_scoresheet2_mockup_excerpt

Excerpt from the first Scoresheet 2.0 mockup.

You can download the full text mockup with annotations here. I used a winning run uploaded by Amphouse as the basis for this mockup because I wanted it to contain a wide variety of stats, though that also means it originally contained more spoilers--most of that stuff was edited out so I could share it here. Note that while the per-map data formatting gives you an idea of what it could look like, many of the numbers are either made up or outright missing.

Later there were also some small changes to the format as well as yet more sections/content I hadn’t foreseen to include in the mockup, but I saved that original version for posterity (it also includes some of my notes and reasoning about parts I hadn’t settled on yet). We’ll get into the final form next :)

Organization

For the remainder of this post I’ll be showing image excerpts from sample scoresheets to demonstrate various sections, but if you want to also follow along with the full text version of a complete Beta 9 scoresheet, you can download this recent run by Ape. There will likely be a few tweaks here and there before Beta 9 is officially released (it’s still in prerelease testing), but for the most part this is representative of the final format. (Remember a number of stats are not visible because they weren’t triggered for this particular run, so it’s technically not a complete set, nor is any individual run, for that matter.)

One of the obvious improvements over the original “endlessly expanding version” is that it’s now much better organized. Alongside the preexisting “non-stat” categories like Parts, Peak State, and Favorites, the core stats list itself has been broken down into 15 categories and reorganized as necessary to match them. We now have separate sections for Build, Resources, Kills, Combat, Alert, Stealth, Traps, Machines, Hacking, Bothacking, Allies, Intel, Exploration, Actions, and Best States! Categorization makes it easier to both find and understand relevant data. Plus it looks cooler :D

One thing I didn’t add to the scoresheet is a list of every part attached, or even every build at the end of each map. There’s just no great way to do these, and compared to the amount of space it would occupy this information would be difficult to parse, anyway. In hindsight I discovered the new automated class system will work nicely for that purpose, and that in itself also occupies two new sections in the scoresheet I’ll talk about later.

“Per-weapon damage” and other details discussed by the community before in the context of an expanded scoresheet is another of the few things that were left out for similar reasons. I thought of somehow combining it with the Favorites section, but alone this information doesn’t seem as valuable as it would be in comparison with all weapons used. We already have shots fired by weapon category, as well as damage tallied by type, both of which combine with per-map data to provide a clear yet concise picture of weapon use.

Sub-subcategories

Stats can now make use of a second level of indentation.

cogmind_scoresheet2_subsubcategories

Multilevel indentation example.

Previously scoresheets could only contain primary stats and sub-stats, but that led to a handful of problematic situations where (mainly because stat names are kept from being too long) some stats really needed to be indicated as a subcategory to an existing subcategory and were therefore prefixed with ‘^’, which is a bit esoteric :P. The new system fixes that by allowing more indentation. I’m not going overboard with it, but it is needed or advantageous in a few situations so I expanded the use of sub-sub-stats a bit.

Header

At the top of the scoresheet are a few lines serving as the general header where you’d naturally put the run’s vital info, even if that info appears elsewhere.

This includes the name of the game and its version, as well as whether it was a win or loss. If you look back to the 7DRL scoresheet example, you’ll see that the header remained relatively unchanged through the years (aside from adding a player name, which couldn’t be specified back in the 7DRL).

Scoresheet 2.0 adds extra detail, most importantly the cause of death!

cogmind_scoresheet2_header

Scoresheet 2.0 header format changes.

The result can of course show a win, in which case as usual it will state the type of win, but many roguelike runs end in a loss, and although it’s fairly common in roguelikes to record the cause of death, this isn’t something Cogmind was equipped to do before.

For six years now, internally the game only got as close as recording a general cause of death, for example “by cannon,” or “by cave-in,” and this was not really worth including in the scoresheet because it’s clearly an incomplete system that can’t account for all possibilities, and also needs more detail to be as meaningful as possible.

So while we’re vastly improving the scoresheet, it’s about time to include this information! (Also imagine the fun aggregate data we can collect :D) This took a fair bit of work to both identify all the possibilities and variations and store the necessary information when it’s available to provide it for the final conclusion. The sentence needs to take into account the relevant actor (Cogmind? enemies? no one?), kills via proxy, death to non-robot/attack causes… all sorts of stuff. Here are some more examples:

cogmind_scoresheet2_death_cause_tests

Scoresheet 2.0 run “Result” samples (cause of death).

Some players have indicated they’d like to be able to remain on the main UI after death to get a better idea of the situation, but this is at odds with Cogmind’s immersive approach so I decided not to. There are alternatives like examining a log export afterward, or now using the scoresheet to see the specific cause of death, and/or last log messages, and/or surrounding environment (these I’ll get to further below).

Going back to the header format changes, see how it also now includes the date and time of the run’s end, but without the more compressed YYMMHH-HHMMSS time format used in the filename.

Scoresheets for special modes or events will also be listed next to the primary difficulty mode, if applicable. Those always used to only be recorded elsewhere in the scoresheet, if at all.

The difficulty modes and their names are changing as well, but that’s a topic for a whole other article! Update 190917: Said article has now been published, Rebranding Difficulty Modes with an eye towards player psychology.

Performance

The score structure hasn’t changed much. Throughout alpha/beta I’ve been watching and waiting to see if an all-around better approach to scoring would suggest itself, but didn’t come up with anything, so the existing system will remain.

cogmind_scoresheet2_performance

Sample scoresheet 2.0 excerpt: Performance.

It has, however, been expanded slightly with the addition of Regions Visited. Rewarding exploration is a good way to encourage it, so a set amount of points are earned for reaching each new map. In the early years points for “Evolutions” served that purpose well enough, earned at each new depth, but back then the world was narrower, whereas players can now spend up to a half or even three quarters of a run inside branches, exploring horizontally. While it’s true there are also bonus points to earn in branches, not everyone may actually earn those anyway, and it’s worth recognizing that they still made the trip.

The bonus point system will continue to be an important source of points for most runs, but it’s flexible and can be adjusted/expanded more in the future as necessary. Notice that I moved that breakdown right up under the Performance section.

There were no changes to Parts, Peak State, or Favorites, which all still serve their purpose just fine.

Stats

There have been quite a few improvements and new entries added to the stats section (which, again, have been divided into 15 categories). I won’t cover all of them, but will talk about certain areas of particular interest…

Build

Much of this section is new, adding a breakdown of all slot evolutions, as well as info on subtypes for attached parts and slot usage. Combined with the per-map data, we’ll be able to have a pretty clear picture of how builds evolved across the run as a whole, in a much more condensed and readable format than if we had list after list of build loadouts or some other highly detailed approach.

cogmind_scoresheet2_build

Sample scoresheet 2.0 excerpt: Build.

Instead, after the detailed data we get a list of the most popular build classifications used throughout the run, ordered by usage. The list excludes any used for less than 4% of a run, as there could be many such transitionary states that aren’t really meaningful in the bigger picture.

cogmind_scoresheet2_class_distribution

Sample scoresheet 2.0 excerpt: Class Distribution.

Following the aggregate class distribution is a summary of the player’s dominant build classification for each map. Where multiple build classes were used, which is common, it picks the one that saw the longest period of use.

cogmind_scoresheet2_dominant_class

Sample scoresheet 2.0 excerpt: Dominant Class.

Automated build classifications are a new system I’ll be diving into later in a separate article in this series. It’ll be fun to run data analysis on the future scoresheets to learn what builds are more popular, a very new way to understand the player base.

Kills

With scoresheet 2.0 we finally get kill counts for more specific robot types! To simplify the code, this system had always relied on Cogmind’s internal robot class system, which is essentially what you saw appearing on this list before. The problem is, the game has grown so much since then and you can now eventually meet some rather special special variants that shouldn’t really be tallied along with the others--like not-your-everyday Grunts and Programmers, but there was no way for the system to distinguish them, meaning some of the most powerful hostiles in the game were being lumped with the average ones. No more! Now we’ll finally be able to see real kill counts for all of the special robots out there…

The “NPCs Destroyed” list also now includes all uniques (as well as where they were killed!), so yes that means we’ll also have a record of the nefarious exploits of those out there murdering bots who should otherwise be powerful allies ;). This list always included the major NPCs, but over the years Cogmind has gained a cast of minor named NPCs as well, so it’s finally time to start seeing them in the data.

Hacking

As of Beta 8 the scoresheet only records Terminal hacks, but that’s been greatly expanded to include every possible hack--all machines and even unauthorized hacks, too.

cogmind_scoresheet2_hacks

Sample scoresheet 2.0 excerpt: Hacks.

Remember here that a lot of entries don’t actually show unless they’re non-zero, so there are quite a few more possible data points than what you see in the sample run, hacking and otherwise.

Actions

I can see some interesting analysis coming out of looking at a breakdown of actions across a run. It’s a pretty fundamental kind of data, but was actually rather challenging to implement because Cogmind didn’t originally need to distinguish between action types in such a granular way. Anyway, we have it now :D

cogmind_scoresheet2_actions

Sample scoresheet 2.0 excerpt: Actions.

 

cogmind_internal_action_logging_test

Debugging action type detection via the in-game message log.

History and Map

It’s common in roguelikes for a morgue file to include the last X messages from the message log, and while I don’t think this section will be quite as valuable or interesting in Cogmind as they are there (due to de-emphasis of the log in general), it won’t really hurt to add it and could at least in some cases help describe what was happening just before The End, especially in combination with the map.

cogmind_scoresheet2_last_messages

Sample scoresheet 2.0 excerpts: Last Messages from three separate runs. For Cogmind I’ve chosen to record the last 20 messages in the scoresheet.

However, the ASCII map and history log (different from the message log!) are quite valuable and I’ll be dedicating separate articles in this series to each of them.

Schematics and Fabrication

Since Alpha 9 the scoresheet has included a list of any parts fabricated during the run, and where they were built. But with literally every part of the scoresheet being revisited for 2.0, I discovered this one needed some improvements, too!

cogmind_scoresheet1_fabricated

Original format for Fabricated list as used for the past few years.

The content is mostly unchanged, but the same data can be presented in a much more easily readable fashion--alignment is important!

cogmind_scoresheet2_fabricated

Sample scoresheet 2.0 excerpt: Part Fabrication List.

Due to their varying lengths, it’s far easier to scan a list of columns than with all the data mixed together. Content-wise the only addition is the new “Preloaded” marker indicating that a particular fabrication was completed using a schematic not loaded by the player themselves.

Item and robot schematics weren’t even recorded in the original scoresheet, but have been added for 2.0, also aligned in a pleasing and readable manner.

cogmind_scoresheet2_schematics

Sample scoresheet 2.0 excerpt: Item/Robot Schematics.

Often times players will obtain schematics but not actually use them, either due to lack of opportunity or deciding not to, but either way it’s interesting to see what schematics players are going after, and how they’re acquiring them since there are a number of different methods (including four more that don’t even appear in the above sample!).

Game and Options

Another area that got the formatting treatment are the last two meta sections, Game and Options.

Years ago they were very short lists with just a few entries, and output using different code than regular stats, so they were never actually aligned and this didn’t seem too problematic early on. But now they collectively account for over two dozen entries and the difference between alignment and non-alignment is stark.

cogmind_scoresheet2_game_options_alignment_comparison

Sample scoresheet 2.0 excerpt: Game/Options (non-aligned vs. aligned).

As shown in the header earlier, the date format has changed, now also recording the start time in addition to when the run ended. Originally only the latter was included, but in my research I noticed other roguelikes recording both and this seems like a good idea.

Run play time and cumulative play time both have new formats as well. These were originally shown in minutes, whereas the latter now uses the gaming “standard” of hours to a single decimal point (127.3 hours is much easier to understand than something like “7636 min” :P) and run time is shown using the HH:MM:SS format. Now real-time speed runs can care about the seconds as well ;)

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

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

Robot Hacking: Upgrades

Last year Cogmind got an expansive new robot hacking system with a lot of hacking options to use as either a primary or supplementary strategy. Overall it’s not as powerful as the simple placeholder version used in Cogmind’s early years, but it’s a lot more versatile.

That said, as it was designed bothacking does become less reliable in the late game since it can’t directly deal with prototype threats, plus it can become a bit too challenging to acquire enough Relay Couplers to continue hacking while also dealing with other threats. Dedicated robot hackers tend to fall behind the power curve.

Among the easier approaches to addressing this issue were a few additional minor passive RIF effects added in Beta 7.1, followed by Beta 7.2 increasing the value of Couplers higher in the complex, but these can only be so effective so we need other ways to prop up so-called late-game “summoner builds.”

In the context of Cogmind I call this build style a “bothacker,” but in general roguelike/gaming terminology the strategy resembles a summoner since when not hacking robots for information or to induce other side effects, the best hacks are often outright control over other bots and you might need to rely on allies to do your fighting for you.

In this light I could see several more abilities which would complement such a build nicely, but would clearly be too powerful to immediately give to all players who gain the ability to hack robots. The logical conclusion here is to add something fairly rare in Cogmind: permanent upgrades!

Progression

In a lore sense these technically aren’t physical upgrades, but instead additional knowledge about operation of the Relay Interface Framework that enables Cogmind to take advantage of it in different ways. Each time Cogmind connects to an additional RIF Installer beyond the first, another ability is gained.

Of course that means entering more Garrisons, but this is something bothackers tend to do anyway either as a way to gain more Relay Couplers or (interestingly) escape trouble. With the passive RIF abilities added in Beta 7.1, Garrisons clearly became a “home to bothackers”--no passive alert gain, collect more Couplers, or maybe add some allies, and adding new abilities into the mix solidifies that route for bothackers seeking to become more powerful in the long term.

There is a pool of possible abilities to learn, and the player does not choose one--RIF abilities are learned randomly, although the opportunity is never wasted by relearning the same ability again, for example. Learning abilities continues to shrink the pool until every ability has been learned (in theory, anyway--that’s a lot of Garrison visits!).

Some abilities can be learned more than once to increase their effect, while others cannot be learned until after one or more others are learned. In a gameplay sense this latter limitation keeps the clearly most powerful abilities from being obtained too early, although there’s enough variety to keep it from being a linear progression with every playthrough.

An interesting side effect of learning RIF abilities inside Garrisons is that it can force players to skip other benefits at a given depth, by losing access to branches and their respective benefits. This kind of sacrifice is common within Cogmind’s design, and important, here allowing RIF abilities to be relatively powerful since they’re balanced against other good strategies. (RIF already makes it impossible to establish two of the most effective alliances--technically they’re still possible if RIF is installed after they’re established, but by then there will also be less time to upgrade RIF abilities, so we’ll see how that balances out! Always interesting to add entire new systems that may or may not synergize with others and see how players react :D)

Note that although Garrisons become much more challenging later, as a RIF-capable build (with new abilities, no less!) players will have many more tools to deal with them, so I think it balances out.

RIF Abilities

So what abilities are there?

If you want to avoid spoiling them and would instead prefer discovering these abilities on your own, bail out now--this is as far as you should go!

Well first of all, all the existing passives added to RIF earlier are now considered “abilities,” just given for free all at once. Adding them as abilities serves as an explicit way to show and describe them for new players, as I’ll show later in the UI. And of course as abilities all these things need names :)

  • Alert Monitor: Know the ambient influence level while in 0b10-controlled areas.
  • Garrison Interface: Gain control over Garrison Access machines, making it easier to hack them, and easier to escape once inside. Also automatically detects Garrisons up to a range of 15.
  • Coupler Protocols: Use Relay Couplers to interface with their respective robots.

Here are the new abilities that can be learned (where those which can be improved by learning them multiple times are shown with their progression of values):

  • Coupler Efficiency: +4/10/20 effective code value to all Relay Couplers. Packing more punch into each Coupler eventually gets quite good. These values are equivalent to a 20%, 50% and 100% increase in the hacking potential of an average Coupler.
  • Robot Detection: Know positions of combat bots within a range of 24 while matching Coupler attached. Also distinguishes squad leaders (L).
cogmind_rif_ability_robot_detection

Inherent Robot Detection is essentially like a weaker version of FarCom--although it has a somewhat longer range, the types of robots it can detect is limited by Coupler type coverage.

  • Patrol Navigation: Know routes of patrol squads within a range of 24 while matching Coupler attached.
cogmind_rif_ability_patrol_navigation

Identifying the current route of a nearby active patrol.

  • Alert ID Control: Block the influence of kills by allied former 0b10 bots while matching Coupler attached. This is quite powerful in combination with an allied army, theoretically making it possible for a good hacker to snowball a pretty large force of allies that can do all the dirty work without the biggest associated drawback: rising alert and Demolisher dispatches.
  • Structural Interface: Reveal any hidden door or phasewall on sight, and pass through phasewalls normally.
cogmind_rif_ability_structural_interface

Finally a way to take advantage of phasewalls! (Not to mention the advantages of having a built-in partial Structural Scanner.)

  • Program Shield: Fully prevent Programmers from hacking allied former 0b10 combat bots while matching Coupler attached (no range or sight limitations). The biggest threat to allies are Programmers, and this offers a passive way to deal with them.
  • Autooverride: 20/40/60% chance to automatically trick target system into believing it is allied with Cogmind. After 10 turns the network will perform an automated quickboot to restore it to normal, a process which takes anywhere from 5 to 10 turns. Checked once per visible combat bot while matching Coupler attached, separately for each Coupler.
cogmind_rif_ability_autooverride

Autooverride is basically equivalent to an overwrite_iff hack, but automatic and free :). Turn squads against themselves, and each other.

  • Autoassimilate: 20/30% chance to automatically rewrite all target system data, permanently converting it into a fully controllable ally. Requires 6 turns to complete the process. Checked once per visible combat bot while matching Coupler attached, separately for each Coupler.
cogmind_rif_ability_autoassimilate

Autoassimilate in action--basically equivalent to a formatsys_high hack, but automatic and free. It still has the delayed effect drawback, however, so using your Remote Datajack to expedite the process might be a good under certain circumstances.

Notice that all of the above abilities are essentially passive! This limits their impact on the UI and controls, making them really easy to manage--everyone loves passive abilities :P

The only active element is that most of the new abilities also require having a matching Relay Coupler attached (e.g. a [Grunt] version for applying a given ability to Grunts). While this somewhat limits the scope of abilities, it also makes even low-value Couplers useful in many situations.

Still, you can imagine a high-level bothacker with all of these abilities being… um, very effective!

Interface

Naturally with a new range of abilities we’re going to need a way to actually see which ones we have.

On the first time connecting to a RIF Installer, there’s a new tutorial message inviting the player to check out a new button in the top-right area of the HUD. Conveniently there was just enough room to add a [RIF] button right next to the RIF-based Influence readout.

cogmind_rif_ability_button

The new [RIF] button as it appears on the HUD.

The main annoyance of having the button there is its neighboring Evasion window, which expands whenever the cursor passes over it, and it’s highly likely the cursor will pass over it to reach the new [RIF] button xD. Part of the reason for designing the Evasion window that way was because the cursor would never need to be up in that corner anyway, but oh well it’s fine--not really much of a deal I don’t think.

Keyboard access to the new RIF window is via Shift-Alt-f. That command technically had another feature associated with it (reswapping) which was removed as a result, but that was a very old 7DRL feature that I don’t think many players were using anyway, not to mention there are now multiple useful ways to swap parts, not even including the broader “reswapping” system coming with Beta 9--one could say it was perfect timing that this key combination got repurposed :)

cogmind_rif_ability_info

Sample RIF ability window.

The new RIF ability window lists all the abilities in the order in which they were learned, as well as their current level and a description of each effect.

At the bottom is a reminder for new players that more abilities are available and how to get them (otherwise there’s no way to know this…).

Overall I’m liking this direction for late-game bothacking, and look forward to seeing what people do with it!

Posted in Mechanics | Tagged , , , , , | Leave a comment

Special Game Modes in a Roguelike Context

By their nature roguelikes are highly replayable, but can also serve as a great foundation on which to essentially multiply that replayability again and again. Adjust a few variables here, add a little content there, and you can have a very different game, one that some people admittedly might enjoy less, but they don’t have to play it--others who do like the the alternative mode have a whole new way to interact with an otherwise familiar world, or take it as a challenge depending on how it’s designed.

What I’m calling “special modes” includes everything from timed events to challenges and conducts, basically different ways to play the same game, primarily ways which are explicitly codified by the game, although non-codified alternatives are also within the scope of this discussion.

Why Special Modes?

There are many good reasons to including special modes in a roguelike. Here are some of the advantages I’ve encountered during my work with them over the years:

  • Value: Well obviously as with any additional content, there’s more value for players. This also translates to value for the wider community as well, since special modes serve as another source of interaction whether it’s sharing stories, bragging rights, or just talking strategy.
  • Replayability: Special modes can require players to explore whole new strategies within the context of a body of knowledge they’ve already accumulated about the game so far, just seen in a different light, making an already replayable genre that much more replayable.
  • Learning: Having seen the game in a new light, some of the knowledge gained through these alternative experiences can even translate back to improved skills while playing the main game.
  • Experimentation: Special modes enable players, and most importantly the developer, to experiment with new ideas that may even make it into the game proper--the design can go out on a limb here without any real repercussions. Of course you can already do this with prereleases/beta releases/EA/whatever, but the idea here is that these mods can also continue to live on on their own, without having an impact on the game as well, for those who choose to play them.
  • Interlude: As a developer, sometimes it can take a while to put out a major update. In the meantime, rather than keeping players waiting too long for something new, why not release a special mode to play around with? (Note this category also includes postludess as well, breathing new life into a game after most or all of its updates are out there and development is “complete.”)
  • Efficiency: Handling special modes as “interludes” is also made easier by the fact that they’re generally not that hard to implement. You get lots of bang for your buck in terms of dev time here, due to reusing not just assets, as you might with an expansion or DLC, but basically reusing pretty much “the entire game” and just changing the experience, possibly even significantly, in a relatively short time. This generates a lot of low-cost value.

Naturally it makes more sense to apply special modes to an existing expansive project, since there’s more content and systems to work with, though technically many of the same characteristics can apply to any roguelike, large or small. Still, these modes are more of an optional “extended development” sort of goal, and if the base game isn’t yet complete or at least itself an already compelling experience, then clearly more work needs to be focused there first :P

Sample Cases

To demonstrate some of the above, let’s look at the range of Cogmind’s special modes, which take multiple forms.

Timed events

A few times over the past couple years I’ve added special modes that trigger automatically at a certain time, for everyone to participate in. Of course there is/should also be a way for people to opt out of this if they’d like to play a regular run during that period, but I also make it so that these modes don’t kick in unless a player has already finished several runs and has some experience (or in the case of one of the events, a fair bit of experience).

I’ve seen a number of indie devs do holiday-themed events these days, though most don’t seem very expansive, maybe just a little change or bit of content here or there.

April Fools Day is a good candidate if only because the theme can be a combination of something totally crazy/funny/unexpected. Plus of course there’s the fun result that some people don’t even believe the news is true, while for those who learn it’s a real thing it also kinda counteracts the growing internet annoyance at all the fake game-related announcements people wish were true xD. I like making an April Fools announcement about features everyone can actually play with.

I’ve done a couple of these so far, starting in 2018 with Launcher Mode. This one is a good example of a quick modification that changes the entire experience in interesting ways. All it does is essentially convert all obtainable weapons to a random launcher instead. Launchers are powerful, but also resource-hungry and not great at solving every sort of challenge, so strategizing ways around their shortcomings (with a more limited set of options) is a pretty new kind of experience. Plus players get to use fun weapons that tend to be more rare in regular runs :)

cogmind_afd2018_launchers

Sample map area with all items replaced by random launchers for April Fools Day 2018.

Being put together so quickly there were a couple of oversights, like at first enemies could still drop their weapons and you could arm up with non-launcher weapons that way, but I fixed the main issues with a quick patch shortly after release.

From conception to implementation to deployment, this entire mode took only an hour to put out, and wasn’t even planned at first, but literally on the morning of 4/1 a player asked about April Fools and it sparked an idea (another reason having an active community is so great--lots of interaction to spark new ideas). Overall a relatively small effort for big gains.

Unfortunately I don’t know how many people actually played because at the time I simply decided to deactivate score uploading if using that mode to avoid polluting the leaderboard with non-standard runs, but news about the mode did get posted around (and even attracted attention from new buyers) and people thought it was fun, or at least funny even if they didn’t play :P

Certainly the AFD (April Fools Day) data could be directed elsewhere, but I wasn’t thinking about it then, and there wasn’t time much time to think through doing that anyway--it was already the morning of release!

In April 2019, however, I once again released a special mode, and this time I had both more experience and some time to dedicate to redirecting scores for this mode so we could get some player data, and a dedicated leaderboard. Participation was limited (42 players) since the leaderboards are opt-in and it was just a timed event, but it was nice to be able to look at some stats from the runs.

cogmind_pay2buy_leaderboards_190419

Cogmind April Fools Day 2019 Leaderboard

The theme for AFD 2019 was “Pay2Buy,” where players don’t find or salvage spare parts, but instead earn coins and buy items from the “Cogshop.” There’s even a market economy, loot boxes, and special deals!

cogmind_pay2buy_demo

Buying items from the Cogshop.

This was most certainly not a one-hour job like AFD 2018.

I spent a week designing and putting together the Cogshop and related mechanics, but I think it was a week well spent since as per the “interlude” concept mentioned above, it gave me something new to put out there during a lull of regular releases. The previous release was 7 weeks before 4/1, and now 7 weeks later (as I still work on some extremely time-consuming features) the next major release still hasn’t hit, so yeah we really needed a little something out there :P

Pay2Buy actually wasn’t the first idea entertained this year--in the run up to April 1st I experimented with turning Cogmind into a real-time game, but that didn’t feel like it would be fun for a majority of players so I dropped it. I talked about this mode and its design on Patreon here (patrons only).

Another important detail about these particular “timed” events, is that I built them to also be available after the event period. Not everyone who might want to can necessarily play on April Fools Day, so just like there’s a method for players to opt out of the events, they can also opt in on other days (both handled via command line switch). This wasn’t true in 2018--back then the advice was simply “change your system date when you start a new run,” but it’s kind of annoying to require that players mess with their system for something like this! I added the mode-forcing feature this year.

So these event modes have become permanent optional features. They’re simple and/or isolated enough that there shouldn’t really be much maintenance involved in future versions, may as well make them available to everyone down the line :D

Late last year we had another timed event which I approached very differently: “Limited Edition Holiday Mode 2018.”

cogmind_holiday_mode_2018_present_matrix

Cogmind Limited Edition Holiday Mode 2018 logo

Running from December 20th through January 1st, the highlight of this event was getting PRESENTS.

cogmind_holiday_mode_2018_scrapyard_presents

The Scrapyard is full of presents! Who could’ve done this…

cogmind_holiday_mode_2018_present_art

Go ahead, open it!

Opening each present revealed one or more items, really good stuff, and available right in the starting area. Not just any good stuff, though, the majority were chosen from among 17 brand new holiday-themed items, some with new mechanics (see this followup announcement for details).

cogmind_holiday_mode_2018_black_coal

Dropping a piece of “Black Coal” outside a Garrison and making a run for it.

A few of the less special items were randomized, but most I handpicked for each day to allow for different experiences through different combinations, of course taking into account special days like Christmas Eve, X-mas Day, and New Years. The craziest, flashiest item was reserved for reigning in the new year:

cogmind_holiday_mode_2018_RPDS_ASCII

Behold, the RPDS!

cogmind_holiday_mode_2018_RPDS_tiles

RPDS Demo: Now With More Tiles Edition

Yeah it looks OP, but it’s also an insane drain on resources so is not easy to properly use! Not to mention the complex gets really mad when you destroy so much stuff that quickly, and that usually means trouble. Still fun though :D

Now this all seems like a fairly big production, but I only spent two days on it! Surprised myself with that one, really. It would’ve taken longer had I decided this event’s features would be future proof and available through all following releases, but this one was a true limited-time event only available in that one version of Cogmind. As a one-time event, I was able to rapidly add content without putting any time into considering or preparing for possible long-term consequences of my decisions (or even organized/coherent scripts and code xD) like I normally have to do.

This highlights one of the drawbacks of special modes, that in order to keep them usable in future versions they may have to be maintained along with changes to the rest of the game, which can be time consuming and possibly not worth it, so it’s good to differentiate between temporary but extensive modes and those modes with a more limited feature set which can more easily be kept with minimal maintenance going forward.

Both AFD 2018 and Holiday Mode had really high “bang for buck” quality. Less so with AFD 2019, but it was definitely still worth it since we got to keep the mode and it’s a pretty interesting way to play.

Challenge Modes

Cogmind currently has 12 “challenge modes.” The design scope for these is somewhat narrower than time-based events, which might inherently be challenging to various degrees, but the focus there is mainly on fun. Challenge modes, on the other hand, are an optional way to increase the challenge level of the game through various modifications. Multiple challenges can be active at once, and each one intermittently awards points for reaching milestones. Plus of course there’s the advantage of allowing players to simply face different or unusual challenges compared to those they might normally face during their runs.

I haven’t covered these on the blog before since I’ve generally considered them an “experimental” part of the game, although I’ve been maintaining a forum thread updated with their content.

They’re not even accessible from in-game menus, actually, only via an external config file, but challenge modes have technically been integrated into the game proper via achievements for winning them, and there’s a separate leaderboard as well.

Below is the current set of possible modes, just stuff I either 1) thought would be challenging to try or 2) easy to implement anyway and worth experimenting with:

  • Devolution: Start with three times as many part slots, chosen randomly (from a weighted distribution) and lose a random one at each new evolution, growing from super strong to eventually extremely weak.
  • Fragile Parts: All parts are destroyed on removal, mimicking the standard behavior of processors.
  • Gauntlet: All exits from each map are blocked from use except for the furthest main exit from the map’s entry location.
  • Inhibited Evolution: Only evolve half as many slots as usual at each depth (essentially weaker throughout the run).
  • No Salvage: Destroyed robots leave no salvageable parts, only matter (a resource), making it harder to find the right/useful spares.
  • Pure Core: No inventory slots are available for the entire run, only what parts be attached.
  • Scavenger: There are no random part stockpiles in the main complex, and any lone items strewn about are damaged. Everything else must be salvaged from other robots, stolen from haulers, or fabricated.
  • Simple Hacker: No indirect or manual hacking of any kind.
  • Sticky Parts: No parts can be removed or swapped out manually, and must be destroyed to free their slot(s).
  • Super Gauntlet: All exits from maps are locked, and the only way to advance to the next depth is to find and fight through the one unlocked Garrison.
  • Trapped: Most maps have 10 times as many traps, which are also more likely to trigger, and most methods of learning trap knowledge are unavailable.
  • Unstable Evolution: No control over slot type evolution, which is random.
cogmind_challenge_scavenger_example_map

Random damaged parts lying around in the Scavenger challenge mode.

cogmind_challenge_simple_hacker_no_manual

Manual hacking option disabled for the Simple Hacker challenge mode.

(See the forum thread for sample images from more challenges, along with other ideas and discussion. Also the manual has more specific mechanics and scoring descriptions than what’s found above.)

Most of these are actually quite easy to implement, again a good way to add value and new experiences to the game at little cost.

They’re also still an ongoing part of feature development. It’s been ages since the last batch was added, but I’ve got dozens more ideas in this area (and of course many suggested by players as well), I just need to decide when to add more… I guess the plan was to make an in-game menu to access them first, and do this and more as a dedicated release, but as a time-consuming but low-priority feature that’s been repeatedly pushed back.

Conducts

Conducts (also called “challenge games” or “challenge runs”) exist in many roguelikes, from as early as the classics. Conducts are different from Cogmind’s so-called challenge modes in that the latter actually modify the content or mechanics in some way to create the challenge, whereas traditionally conducts work within the base game itself without any changes--they’re purely dependent on some kind of player-based limitation.

For example NetHack includes a number of optional conducts that you can read about here on the wiki.

nethack_conducts

Some of the conducts NetHack tracks, as listed in game via the extended #conduct command.

Brogue also tracks conducts, which it calls “feats.” The full list can be found here.

brogue_win_feats

Victory screen from Brogue, having achieved two conducts (source).

Unlike the above examples which are just detected automatically as you play a normal run, Angband players essentially treats some of its “birth options” as conducts, though there aren’t very many--some roguelikes lend themselves to conducts better than others.

Not all conducts are necessarily tracked by a roguelike, many more originate in the player community and become established as “unofficial” conducts, challenges attempted by many players but that aren’t technically coded in the game, instead relying on players self-enforcing the appropriate limitation(s) or requirement(s).

NetHack also has a decent-sized list of unofficial conducts (some of which are tracked by variants even though vanilla does not), and ADOM has a number of established unofficial conducts as well. Some of ADOM’s achievements have since become semi-official by having them recognized via Steam achievements in that version of the game.

Cogmind definitely has its share experienced players who self-impose conducts, too, and even an “official” set as in those represented by relevant achievements. Although Cogmind achievements don’t require Steam, the achievement system is the only one to codify and enforce these conducts, and therefore technically each only works once (the first time it’s achieved). Later this could be expanded to recognize them in any subsequent run even after having been previously achieved, but I’m not too sure of the value in doing so just yet since there’s a lot of other more valuable features, not to mention there are so many achievements that it’s easy for players to stay occupied doing new and different things anyway, as opposed to repeatedly attaining the same goal.

One difference worth noting here is that “conducts” are traditionally more specific than a lot of achievements, which may be accomplished before actually winning the game. A conduct is not fully earned until the run is won, so they’re generally challenges by and for the minority of experienced winners choosing them as a way to intentionally make the roguelike even harder.

Cogmind currently has 256 achievements, but win-related conduct achievements all fit into the following subcategory:

cogmind_achievements_win_challenges

Cogmind sample achievements in the UI.

Then there are 12 more for winning with each of the Challenge Modes, though again these actually change the game, and I wouldn’t consider them conducts as defined here. And quite a few more of Cogmind’s achievements require long-term conducts within a given run, but can technically be earned before winning so don’t really count, either.

Non-meta Conducts

Conducts as described above and generally thought of by the roguelike community are “meta achievements,” basically optional challenges imposed on top of the game itself, and probably can be stopped/forfeited at any time by player choice, regardless of whether or not the game is tracking a given conduct.

But I think there’s an interesting alternative category of conducts embedded within the gameplay.

DCSS god conducts would be an interesting example here. Once you worship a specific god, they may expect you to avoid certain behaviors, or reward you with greater piety for other specific behaviors. This clearly restricts or at least affects the player’s “conduct” for much of the run, or at least until (if) the player is willing to suffer the consequences of leaving for another god.

See here for a list of DCSS gods, which link to more information about each, where the “Deprecates” heading indicates what kinds of conduct that god expects.

dcss_god_deprecates_chei

DCSS’s Chei is a good example of a restrictive god.

Most gods don’t have a whole lot of limitations, instead focusing more on behaviors they’d expect or prefer that you do in order to gain piety and reward you for it. An interesting exception here is Ru, the god who will only reward you (greatly) as you take on additional permanent drawbacks (“personal sacrifices”), which essentially act like minor conducts.

Cogmind also does some of this non-meta “embedded conduct” stuff, although not god-related and instead on a more macro scale. For example you can permanently install RIF in order to gain a range of useful abilities, but doing so makes two of the other factions hate you, and you’ll therefore lose access to their respective help and abilities for the rest of the run.

There are a number of long-term effects like this, though unlike traditional roguelike conducts which are purely negative, Cogmind generally (but not always) balances them out so that something else of similar value is gained in the process. Either way, the effects are for the long term and some will make certain areas, or even the entire run, more challenging.

Randomized Challenges

Beyond tradition, there is still plenty of room for innovation in the roguelike “special modes” space.

One of the more interesting possibilities is randomization of challenges. As a highly replayable run-based genre roguelikes are quite compatible with this approach, where you have a pool of mini-challenges which can include things like tweaks to mechanics, items, enemy behavior, or the player character themselves, and at the beginning of a run a combination of these tweaks is chosen to be combined as the challenge. With enough tweaks to choose from, different combinations can create very different experiences, all without too much work beyond the supporting framework.

Even better is having the combination be shared across the community as daily and/or weekly challenges, giving the community more topics to discuss and allowing for separate leaderboards.

I’m not too sure this is appropriate for Cogmind given the game’s wide range of interconnected systems and far-reaching balance concerns (it’s a very tightly balanced game compared to many roguelikes…), though the concept certainly deserves more thought and possibly experimentation. Could be fun!

While combining multiple Challenge Modes is a thing, personally I have a pretty specific idea of what constitutes such a mode, which tends to exclude tweaks which might be too small a change (or, less likely, too large an overhaul) to consider for a pool of mix-and-matchable mini-challenges. The target scope would be different in that case, so this would need to be a different system. I can see affecting game variables like item availability, salvage drops, and map demographics, adding permabuffs/debuffs of various kinds, or possibly starting from different parts of the world.

Anyway, there’s already (still xD) lots of do, but maybe this could happen one day.

Posted in Design | Tagged , , , , , , | Leave a comment

Turn Time Systems

I’ve always enjoyed turn-based games, giving you unlimited time to consider and react to changing situations (even if you don’t always take advantage of that opportunity and instead bumble into mistakes far faster than necessary). Turn-based mechanics are especially apt in traditional roguelikes, where the RNG typically plays a starring role and therefore said changing situations can be unpredictable, but exactly what can be accomplished in the space of one turn, and what a turn really represents, varies from game to game.

At the most basic level, we have turn mechanics which are simply [player acts] -> [enemies act], all in sequence, one action per turn, and then the queue starts over again, [player acts] -> [enemies act], and so on. Then some games might add longer actions, though still very clearly based on “whole turns,” like performing a longer action forces the player to skip their next turn. Or the more complex variety: An action requires first waiting for two consecutive turns without interruption (by attacks, for example) in order to be carried out.

But sometimes even more granularity is desirable for content or balance purposes. Like what about actions that take one-third of a turn? Or 2.5 turns? Roguelikes of significant scope are more likely to need a greater level of granularity in order to accommodate more content in a balanced manner. (It’s not absolutely required, of course, but it’s helpful in the long run :D)

This granularity can be a double edged sword, though, since there’s always the chance that as a time system grows increasingly nuanced, the more oddities arise and the more opaque or confusing (or at least challenging) it might become, both to build around and for players to master. As with any game mechanic the opaqueness is at least generally solvable through a better interface and feedback, if necessary, but it’s worth noting that systems with higher granularity will also be much less predictable, so hopefully precise turn predictability isn’t an important factor in a roguelike that also features extreme granularity!

Cogmind Time, 2012~2018

Cogmind 7DRL, written in 2012, was my second roguelike project, based mostly on the X@COM source code. Well, X@COM being a reimagining of the original UFO Defense, it was a squad-based game with explicit Time Units which could be used to perform actions by each soldier until declaring the end of the turn and allowing enemies to act. There was no single player character, and most turns by each individual involved a sequence of multiple actions limited by their stats and what kinds of actions they wanted to perform.

Migrating from this sort of system to a decidedly more roguelike time system would be a pretty big leap, and being a 7DRL I wasn’t going to have a ton of time to experiment with what does and doesn’t work, so I searched around and decided to adopt the system demonstrated in this useful Rogue Basin article.

What is a “roguelike” time system? I think the most basic assumption is that it consists of a single actor performing a single action at a time, and after their action it’s at least possible, if not likely, that other actors will get to perform their own action. Of course there are many potential variations, and definitions don’t mean much in the bigger picture, but X-Com’s TU-based turns would not be considered very roguelike, which generally boil down to [one command] -> [one action] -> [implicitly done].

I picked the system in the linked article because it seemed easy enough, and at the time I wasn’t really familiar with how roguelike time works under the hood anyway. The system is not a simple queue, and has some interesting characteristics, but I’ll get into those details later when I cover how they eventually became a problem and I recently decided to replace it!

For the next bit of discussion, all you need to know is that each “turn” is subdivided into 100 “time units.” Actors can carry out actions that might require only a fraction of a turn (< 100 units) or multiple turns (e.g. >= 200 units). While sometimes more difficult for players to wrap their heads around at first, and also sometimes difficult to accurately predict even once it’s understood, I believe its flexibility outweighs those potential negatives.

Action Costs

The length of an action is extremely variable in Cogmind. Most actions require a static amount of time, but the two most common actions, moving and attacking, are calculated based on multiple factors, and therefore change throughout the game depending on your status.

To keep things simple, the majority of actions take either one turn, half a turn, or 1.5 turns:

Cost Action
100 Pick up Item
100 Attach Item
150 Attach from Ground
50 Detach Item
50 Drop Item
150 Swap Item (Inventory <-> Equipment)
100 Misc. Actions (Ram / Rewire / Escape Stasis…)

And that’s it--Cogmind doesn’t actually have a wide variety of unique action types, and for simplicity sake a lot of miscellaneous actions require precisely one turn. But with one turn the equivalent of 100 time units, there’s a lot of leeway for fine-grained requirements when it comes to the most common actions: moving and attacking.

Moving even a single space involves a potentially huge range of time, quite different from the average roguelike. How long it takes to move is highly dependent on the form of propulsion:

Cost Propulsion
40 Flight
60 Hover
80 Wheels
120 Legs
160 Treads

Those are simply base costs, though, which might vary somewhat with unique items, and which in the case of flight and hover can be further modified by using multiple items at once. For example using three flight units will be faster than using two.

So in a simple scenario, a flying actor (robot) can move three times for every one move of a legged robot, or four times compared to a treaded robot. And that’s only given the base costs! Assuming a loadout of five flight units, each of which gives a -3 cost modifier after the first, the movement cost is 40 + [-3 x 4] = 28, or 3.57 moves per turn, or the equivalent of 5.7 moves for each treaded move. For Cogmind’s first several years the additional flight unit cost modifier was -5, but for balance purposes this was adjusted down in 2016 as it was a little too easy to reach very high speeds.

The highest speed currently possible is 20 moves per turn, though that speed is much more difficult to reach than it was in the early days. Not to downplay the effects of even “average” fast speeds once compounding is taken into account--a meager three-times speed advantage means that for 10 moves by a pursuer you’ve traveled 30 spaces, which is usually plenty to reach safety.

In the opposite direction, movement is slowed if overweight (an effect that matters more for the normally faster forms of movement), so it’s quite difficult to escape a swarm of flying robots tailing you if you’re a mass of components hopping around on one leg.

As you can see, flee/chase situations can play out very differently depending on the relative speeds of those involved, but it doesn’t lead to boring play; instead it’s possible due to the world being an active ecosystem spread across huge maps rather than than based on individuals or groups of monsters waiting within a small area. Escaping danger in one area can still lead straight into danger from another.

Overall, this approach to movement leads to interesting scenarios, like being stuck in an overwhelming firefight without any propulsion (or weak propulsion) and forced to drop things and run in order to survive, or flying so fast that almost nothing can catch you (so long as your sensors help keep you from running into more trouble ahead), or so fast they can’t even see you (since robots only register targets during their own turn, by which time you could be long gone!).

Because movement speed is an important factor in turn-to-turn play, it is displayed on the HUD at all times, though in one of two forms. For beginners it’s shown as a percent of base speed, so 100% when one move = one turn, 140% when 1.4 moves = one turn, etc. This is to keep it more intuitive at first, rather than having new players misunderstand that in terms of time units, technically lower “speed” numbers are faster. Players who activate the more advanced “Tactical HUD” mode in the options see instead the actual current movement cost itself (thus the meaning is reversed, higher is slower).

cogmind_speed_display_cost_mode

Speed display, percent mode

 

cogmind_speed_display_percent_mode

Speed display, cost mode

 

cogmind_speed_display_slow_warning

Warning: Dangerously slow. When beginning a new sequence of movement while quite slow (one-third normal move speed or slower), confirmation is required in case the player simply forgot to activate/attach a propulsion unit, because the consequences can devastating if you take one slow move while under fire!

Attacking has smaller time cost scaling than movement, but is interesting in that its costs are greater than those of other actions, especially movement. The base cost to fire a single weapon is 200 (two turns!), meaning defenders can more easily escape after coming under attack (if they want to). This effect is even more apparent once the time cost of an entire “volley” is taken into account. Weapons are often fired in groups (called volleys), and the total cost of firing the entire group is applied at once (front-loaded, like all action costs in Cogmind):

Cost # Weapons
200 1
300 2
325 3
350 4
375 5
400 6+

In the earliest versions of Cogmind, firing costs for multi-weapon volleys increased about twice as quickly as they do now, but the rate was later reduced to encourage builds with more weapon slots, since they already have other issues to contend with such as the resource costs involved in larger attacks.

Most robots have two weapons, so if you’re relatively fast, each time enemies fire you can fly something like 10 spaces. Even average-speed robots can move three spaces in that time, important for repositioning to a more defensible location. And for combat-focused players, the system is obviously designed to give them an advantage in one-on-one combat, since few actors other than the player are capable of using that many weapons at once.

Sometimes individual weapons may modify the required time (firing speed), though they’re the exception rather than the rule. More unique among this system are melee weapons, which can only be used one at a time (multi-wielding is possible, but additional weapons only have a chance to follow up a primary attack). They are almost always slower than projectile weapons (~300 time/attack), but also more damaging, and come with their own special effects.

Before firing, the HUD displays how much time the currently activated weapons collectively require to fire:

cogmind_volley_firing_time_demo

Volley firing costs indicator, changing as weapons are toggled (Autoguns have a faster firing time).

I think Cogmind is a good example of a unique approach to time management, showing that even while using a very traditional system roguelikes don’t have to follow the principle that the cost of one action is near or equal to a single turn, or a multiple of turns.

New Challenges

Of course it’s only a matter of time before extreme min-maxers come along and pick apart all the system details in a roguelike to scour it for every last possible advantage. They got around to the time system in 2017, and discovered that it was possible to game subturn actions such as quick movement to figure out when it would be impossible for AIs to mark the player as a target, thereby guaranteeing free peeks around corners without being spotted.

Only a few players knew about this, and even fewer actually used it, but as an “optimal but tedious” strategy (and also an unintended one) this was definitely something to eventually put an end to. Besides, the original time system might not even be the most appropriate for Cogmind--heck, I barely understood its current gameplay implications and had just brought it on board for a quick 7DRL years ago!

At first, mainly because it wasn’t having a broader impact on play, I just wanted to try some quick band-aids to see if I could fix this under the existing system without digging too deep.

So I slapped on two separate “fixes” that year, specifically: 1) randomizing turn offsets for each AI (so they didn’t always start their life at 0 time, and therefore be likely to act on turn boundaries, especially if stationary like guards), and 2) changing the “wait” action to cost 100 time units, rather than simply draining the actor/player of all their remaining time in the current turn.

And neither of these worked because, well, I admit I didn’t understand the time system I’d borrowed seven years before, and hadn’t actually read through to understand what was really going on xD

At this point I should explain the actual mechanics of that time system! Basically:

  • Each actor gets a certain amount of time units to spend on actions during their turn, for example a base amount of 100 time units, which might be modified by stats or status effects.
  • During their turn they perform actions that consume this pool of time, and as soon as their remaining turn time reaches zero (or even goes negative due to a time-intensive action), their turn is over, and they are moved back into the queue of actors where they belong, depending on their current time value.
  • Each time they reach the front of the queue, they again have a new pool of time units to use, again based on their stats or status effects.

This system has some interesting characteristics due to the fact that in some cases it can allow for an actor to make multiple uninterruptible actions in a row, assuming the actions cost less time than their available pool. Really you might notice that’s a lot like the X-Com TU system, only handled at an individual actor level (plus you can overspend your available time) and therefore feels much more roguelike.

Such a system also allows for different actors to have different amounts of time available to them per turn, essentially speeding up or slowing down all of their actions relative to other actors. Now in Cogmind I actually never used this adjustable time pool feature at all! Funny enough, I do distinctly remember back in 2012 liking that the system offered such a possibility. There was a spot for it in the actor turn code, but every actor always got exactly 100 time for their pool. Apparently this just wasn’t how I wanted to handle the 7DRL actor “speeds,” not to mention Cogmind’s special movement and attack time mechanics already introduced a lot of variability without the unnecessary complexity that another layer on top of that would bring. Actors could already affect the cost of their individual actions, such as using utilities to fire faster, or attacking with weapons that were simply faster on their own, or speccing for fast movement, or any number of other actor-controlled variables--this level of nuance seems more appropriate for robots built of components.

Anyway, the whole “player always gets at least one full turn” aspect made it impossible to solve the peeking issue--the time system simply wasn’t compatible with Cogmind’s “enemies cannot spot you if it’s not their turn” mechanic. It’s a good system, just not right for Cogmind :P

At this point I still didn’t go as far as replacing the time system though, because there was one other relevant mechanic I’d thought up in the meantime: “off-turn spotting.”

This tackles the issue from a different angle, allowing actors to maybe know about something passing through their line of sight, even if it happens extremely quickly, where the chance for them to notice depends on their type, e.g. Sentries should be more likely to investigate a “potential anomaly.” While so-called “partial spotting” won’t lead to an aggressive chase, they’ll at least check out the area where they think they saw something before going back to their original task.

I like how this mechanic turned out, but honestly it’s tangential to the whole peeking thing.

New System Time

Seeing as I wasn’t even using the original time system as intended, the obvious answer for this problem is to just allow “turns” to be interrupted--immediately reorder the actor queue after every action. Duh. I finally made this change in Beta 8.1, ripping out the old 7DRL system and replacing it with a very simple standard queue.

This change has a number of implications:

  • It’s impossible to be completely sure one won’t be immediately spotted when rounding a corner
  • The chance of being spotted immediately becomes essentially directly proportional to movement speed
  • Common sequences of actions that cost less than one turn, like dropping a couple items, can now technically be interrupted by other actors
  • Other fast-moving actors won’t appear to move so fast compared to a fast player, since moves will be spread out and not lumped together in a sequence

Honestly I’m pretty sure the differences will barely even be noticeable for most players, if at all, but it was about time to remove this unintended, basically non-designed, part of Cogmind. Revisiting it also had another nice side effect which I’ll get to, but first… the bugs.

Oh my, the bugs. I’m pretty bad at technical stuff, so I needed some extra debugging features before I could track them all down.

I already had the first debug visualization from back when I was messing with turn offsets ineffectually trying to solve the problem the first time. (Seriously, if you truly want to solve a problem, examine the entire circumstances first or you’re probably just wasting your time like I was xD)

cogmind_debugging_new_time_system_entity_values

Visualization: Current relative time values for every actor on the map.

This wasn’t enough, though, since under the shiny new system some actors were acting out of order. I originally tried just debugging it internally by looking at values and queue contents, but this was a slow process and before long I had a fully visible turn queue.

cogmind_debugging_new_time_system_entity_queue

Visualization: Current turn queue, soonest to last to act.

The visible turn queue not only shades actors by the relative amount of time until their next turn, but also shows in yellow any that are acting out of order (or somehow managed to get a positive value :/), which had thankfully been fixed by the time I took the above screenshot.

Once all the bugs were ironed out, I also used this opportunity to actually add binary searching to the turn queue. The original time system just uses a linear search, which is fine if you only have maybe 20 actors on a map, but Cogmind has literally hundreds! I did a bit of quick profiling and found that on a mid-game Cogmind map (Factory) 11.59% of the turn processing was taken up by putting actors where they belong in the queue.

cogmind_debugging_new_time_system_optimization_before

Profiling Cogmind’s turn queue updating under the old linear search.

That seemed like a lot, and switching to a binary search brought it down to 2.25% :)

cogmind_debugging_new_time_system_optimization_after

Profiling Cogmind’s turn queue updating under the new binary search.

 

cogmind_debugging_new_time_system_scrapyard

The final time system and visualized turn queue in action in Cogmind’s opening Scrapyard.

 

Turn Queue Explained

Since I’ve gotten questions before, I should probably go into slightly more technical detail about how my basic roguelike turn queue works, especially since it seems so amorphous with regard to absolute time. Where does the idea of a “turn” even fit in here?

A specific example will really help:

  1. Say you have three events in your queue: Player [0], Enemy [0], and Turn [100]. The initiative is set in that order.
  2. Player goes first, spends 120 time on their action, and the new queue order is Enemy [0], Turn [100], Player [120].
  3. The first event in the queue is always the next one to take place, so Enemy goes next, and decides to perform an action that requires 50 time. The new queue order is Enemy [50], Turn [100], Player [120].
  4. So now the enemy gets to act again because they are still at the front of the queue. This time they do something that requires 100 time. The new queue is now Turn [100], Player [120], Enemy [150].
  5. At the front of the queue is… the Turn counter itself! So it handles any absolute turn updates, i.e. things that should happen “once per turn.” Then because each turn is set to be 100 time, the new queue is Player [120], Enemy [150], Turn [200].
  6. So the player acts next, and so on… As you can see, the turn itself is an event/actor, just like the others. You can even add other types of events into the queue if you like, for example as of Beta 8 Cogmind has autonomous weapons that take their own actions independent of the turn counter or even their owner.

Action costs are repeatedly added to each actor and everyone’s time goes increasingly positive as they take actions.  Technically if you have a lot of persistent actors over a very long period, you’ll want to consider eventually resetting the values by subtracting from every event the [time] value of the first event in the queue.

Inserting a new actor is as easy as adding them to the front of the queue and matching their current time value with that of the actor already at the front (or insert them elsewhere as appropriate if it’s desirable to delay their first action).

Posted in Mechanics | Tagged , , | 8 Responses