SCRL: Weeks 03 & 04

2020-03-15

Spent some time writing up another blog post on how I want Aspects and similarities to work. That post will probably go up later this week. After writing, I went and sketched up some code to calculate the “similarity index” between two monsters, and made a little command line application that I could use to test SCORLIB components and run through the code without having to run the game and test there. This made it very easy to compare a few different approaches to calculating this index, which I go over in that blog post.

2020-03-19

It’s been a busy week and I haven’t had any time to work on this project, except for some thinking here and there. The last time I started working on this I started to implement loading game data from SCORLIB. This involved parsing flat files into game objects and setting up factories that I would use to retrieve new objects / mobs / items / etc. as needed. This all works really well – I even have loot generation going, very similar to Diablo’s mostly because I straight up stole the Diablo II file format and style of loot / mob generation.

However, I came across a snag: I want SCORLIB to be as “pure” as possible. Meaning, I don’t want to sully the library with implementation details for whatever game “front-end” is using it. So, even though this game is a roguelike and uses ASCII graphics, I still might want to use SCORLIB with another project later. The problem here is I still need somewhere to store graphics-related metadata so my factories can function without huge gobs of code gluing them together and slapping graphics on things. I think what I’ll end up doing is adding a second set of metadata files to SCORLIB in their own folder / namespace and joining the graphics data (colors, the character glyphs representing entities, etc.) with game data. This way these graphics settings are still separate, but also still loaded from disk instead of hard coded.

2020-03-24

Decided to combine two weeks of dev journaling because… well, I didn’t do much last week.

Today I got a good portion of the work done for my monster factory to a) load in all the game data from SCORLIB from disk and b) load in all Monogame-specific data, also from disk. So at “design time” in my tab-delimited text file I can set all required display settings for my in-game objects, divorced from the logic behind it all (stats and other rules). If my parser somehow fails to conver the string to a proper color / glyph representation (like if I fat-finger while typing) then it gracefully falls back to hot-pink foreground, blue background, and ‘?’ as a glyph.

Because all these objects are relatively small in the grand scheme of things, I just hoover everything up into memory when the game starts, so when I need a new instance of something I request it from the factory. Like so many decisions before this, if it does become a problem, I will revisit it — but I want to move forward adding things to the game without spending too much time right now on what may be premature optimization.

The factory stuff isn’t done, but the loading from disk is 90% done. Probably tomorrow I’ll finish the monster factory and start generating monsters randomly for real!

2020-03-25

It is done!

Monsters, both their stats and display info, are drawn from disk into little factories where they can get pumped out onto maps at will. Because of the number of properties and objects that live inside the Monster class, instead of trying to get clever with reflection or MemberwiseClone or any number of things, I just sucked it up and wrote out all the assignments needed for cloning operations. It took two excruciatingly long minutes to do, but in the end it was far easier to write, test, and look at than any other code would have been — and it runs faster, too. Imagine that.

Next it’s time to extend the MapFactory to randomly assign the next map type, then assign that map a Deity type. I think I want the map factory methods to populate the maps with their creatures, as well. I’m not sure what’s next, but there’s a few options:

  • Spawning boss monsters
  • Spawning monsters in groups based on their monstats metadata
  • Implementing treasure classes for loot

Week(s) In Review

Can’t always have productive weeks. When your day job is also programming, sometimes it’s hard to motivate yourself to do more of the same during your free time. In any case, I got a few key things done these past couple weeks, and now things are starting to get “hard”. Designing and implementing systems is easy, but making them playable and fun? That’s the challenge.

SCRL: Aspects and Deities

One of the core focuses of this game lies on the concepts of Aspects and Deities. These are two separate-but-related concepts that will affect nearly every other aspect of the game: level generation, monster aggression, loot generation, and probably more. This post covers at a high level how each of these might work, and then will dive into some implementation details on how monster aggression is calculated using a “Similarity Index.”

The Goal

Every living thing in the game worships or is somehow bound to a Deity. Every Deity has three Aspects – something the Deity is, has, or does; something it embodies or personifies; a quality, statement, or goal. In short, an Aspect can sort of be anything you might use to describe that Diety. The followers of these Deities will most often carry one or more shared Aspects, but it is not required for every follower to share every Aspect. In addition, an entity can potentially embody many Aspects, instead of the limited number that each Deity personifies.

When the player moves through the Chapel, each “floor” generated will be themed around one (and sometimes more) Deities. Map generation will be tied to the Aspects embodied by this Deity, and typically there will be an altar of some sort where the player can perform certain actions: pray, offer, desecrate, etc. Performing these may dramatically alter that player’s standing with the given Deity.

The creatures that appear on any given floor will either be followers of that floor’s Deity, or share enough Aspects to be allowed nearby. How many Aspects are shared, and how close the shared Aspects are to each other in some given quality (Healing and Peace, for example, are friendly, but not Healing and War) come together to determine the aggressiveness of an entity toward another entity. Most of the time, this is just the monsters toward the player. Sometimes, the player will stumble upon a floor with two altars, and the monsters spawned may be hostile toward each other.

A stretch goal of mine is to also include Aspects and Deities in item generation. More than affecting which items spawn, certain items may only be usable if you share an Aspect; some rare items may only be worn or used if you worship the Deity that spawned the item. This is low on the list, and I’m not sure exactly how fun it would even be if it did work this way. Something to consider and test later.

How it Works, Probably

Below is still mostly in flux, save for how the game data is stored and modified on disk – I’m pretty set on that.

Game Data

Game data is stored in tab-delimited flat files. A header row denotes the value below. This makes it easy to open the files in Excel or some other spreadshett, add a bunch of stuff, and get going. Since the game is text-based, this is really all I need. Game data is pretty simple here, anyway:

As you can probably guess, the Deity “table” three columns that act as foreign keys to the Aspect “table”. Factories in SCORLIB are written to grab entities by ID, among other properties, making it pretty simple to just grab anything without worrying about whatever its name or other properties might be. For the example above, I used an old program of mine I called “MANG” to generate some random names.

I decided to limit myself to 64 Aspects at most, the labels of which are for now subject to change. I haven’t decided how many Deities I’m going to add. Probably no more than 16, or maybe 32.

These numbers weren’t picked just because they’re fun programmer numbers. I have a reason for the things I do, sometimes!

In the Game

The end goal for all of this is to calculate some sort of “similarity index” between two given monsters, and set their levels of aggression accordingly. This similarity index is calculated based on the Aspects each monster has in common, with 0.00 being completely hostile, and 1.00 being completely friendly.

The reason the upper limit of the Aspect count is 64 is because I’ll be using an enum, generated at runtime from the game data, whose values are calculated with bitwise operators – 1, bit shifted to the left by the ID value of the Aspect. So, 1 << 1, then 1 << 2, etc.

Each monster will contain a bitmask (which is each Aspect enum value compared with a bitwise OR) representing its Aspect flags, and when it sees a new monster, will calculate the similar index with that monster and adjust aggression accordingly.

Why use bitmasks? Well, if a monster can have at least three, but possibly more Aspects, without bitmasks I would have to implement each manually, or manage something like a List<> or array of Aspect flags. Instead I’m storing a single item with possibly multiple values inside it, so I could have a crazy circumstance like a monster with 50 Aspects but that list would still take no more space than a long.

In the end the Similarity Index calculation will really be very simple. Monster A has four Aspects and Monster B has two. They share exactly one Aspect. So A->B is .25, and B->A is .5, making Monster B neutral and Monster A aggressive. When struck, Monster B will fight back, but it will not seek out a fight, unlike Monster A. Once struck by another entity, this similarity index immediately drops to 0 (possibly only for a short time).

The lower this similarity index, the more hostile an entity is toward another. If A->B is .25, and A->Player is .33, then Monster A will target Monster B before the Player. But if Player attacks Monster A while those two are fighting, then Monster A changes its course.

Also, a monster’s Wisdom will affect how it prioritizes in scenarios where multiple other entities are attacking it at the same time. A low Wisdom means the monster will continue to target its most recent attacker, no matter what. But a high Wisdom will make the monster target the weakest enemy in its list of targets and strategize to eliminate that one first.

Implementation

The Code

/// <summary>
/// Calculates the Similarity Index to determine how aggressive Actor1 should be toward Actor2.
/// </summary>
/// <param name="actor1">The source IActor</param>
/// <param name="actor2">The target IActor</param>
/// <returns>A percentage value representing how similar actor2 is to actor1.</returns>
public static double SimilarityIndex(IActor actor1, IActor actor2)
{
  var firstAspect = (Aspect.aspect1 | Aspect.aspect2 | Aspect.aspect3);
  var secondAspect = (Aspect.aspect1 | Aspect.aspect4 | Aspect.aspect5 | Aspect.aspect6);

  var aspectList = GetSetFlags((long)firstAspect);
  var aspectCount = GetFlagCount((long)firstAspect);

  var matchCount = 0;
  foreach (var aspect in aspectList)
  {
    if (secondAspect.HasFlag((Aspect)aspect))
    {
      matchCount++;
    }
  }

  return (double)matchCount / aspectCount;
}

/// <summary>
/// Counts how many flags are set for the given bitmasked value passed in.
/// </summary>
/// <param name="flags"></param>
/// <returns>A count of enabled flags in the bitmask</returns>
private static int GetFlagCount(long flags)
{
  int count = 0;
  while (flags != 0)
  {
    flags &= (flags - 1);
    count++;
  }
  return count;
}

/// <summary>
/// Gets a list of bitmask flags that have been set for the value passed int.
/// </summary>
/// <param name="flags"></param>
/// <returns>An IEnumberable<long> of the enabled flags</long></returns>
private static IEnumerable<long> GetSetFlags(long flags)
{
  while (flags != 0)
  {
    flags &= (flags - 1);
    yield return flags;
  }
}

Here we have three methods: the “main” method, used to calculate the Similarity Index between two IActor implementations. For this test I ignored those and just made my own sets of Aspects to test against.

I get a list of enabled flags and the flag count, and I check the enabled flags against the flags for the other IActor, and divide the flags in common against the flag count. Very simple. I could have used only one method for getting flags, and done the adding in the calling method to get the flag count, but I like having two methods with distinct purposes, even though they’re very similar, in case later I need to do only one or the other from elsewhere.

Initially I used LINQ to get the set flags, then in my testing watched my memory usage skyrocket when the list grew large. Partially because the LINQ had to sort through the entire Aspects enum to get the ones that matched, and partially because I think LINQ itself has some non-negligible overhead. The same thing happened before I added the GetFlagCount method – the divisor was initially aspectList.Count(), and that also caused a non-trivial bump in memory usage.

The above approach, in my testing, ended up having near-zero memory overhead, according to my completely unscientific and non-rigorous calculations. By that, I mean I just watched the memory usage bar in Visual Studio as I ran the method. The application went from using 7MB to… 7MB. No change. That’s as much detail as I feel like going into this for now.

This approach also lets me keep my foreign key relationship between Deity data and Aspect data — the underlying value for the Aspect is 1 << {AspectId}, so I get my type safety and some amount of relational integrity.

Now, all this covers why I’m using a bitmask for Aspect flags (at least I hope that’s clear). If you’re curious on how exactly these bitwise operators work, here is a good page describing them in detail.

The Rest

I haven’t made it to the point where any of this affects monster aggression yet. I’ll most likely come back and update this post, or add a new one with the details at that time. For now, it’s enough that I have these details ironed out and I can move onto the actual hard work: creating game data to use in the game. These Aspects aren’t gonna describe themselves.

SCRL: Week 02

2020-03-09

Spent a little time changing the dungeon palette to make it more distinct from UI elements. I’m using Solarized colors for this because I like Solarized and it’s easy on the eyes. Also made it so the starting “Big Room” isn’t pre-lit and always lit, so I can actually test FOV / lighting properly.

Since it’s lunch time, I moved fast and not very carefully… and added pathfinding not-so-successfully:

All the goblins move in the direction they last saw the player, indefinitely, and will attack each other if they get too close. While this is kind of hilarious to watch, it isn’t really what I wanted to do, so I’ll have to just mark this down as a fun detour and return to make sure they a) actually follow the player and b) don’t attack other monsters if those monsters are “friendly”. (For now, all monsters will be friendly with each other.)

(Later…) I removed all but one monster from the map and set a breakpoint to inspect the values of the Path enumerable, as well as the “ToDirection” function that converted an x,y pair to a compass direction. After a little bit of fiddling, I realized the problem was me (again). I was passing in the next step coordinates of the path, where this function expected the delta of current position plus new position. Afterward, with pathing working, I made it so the monster only follows the player’s current position until the player is out of sight. The monster now follows to the player’s last known position, and waits there until the monster sees the player again.

screenshot of game

To cap the night I decided to finish changing the game’s color palette.

screenshot of game

I think now that I have the bare bones down, it’s time to change direction again. This game is supposed to work with a combination of prefabs and procedural map generation. To start with, I’ll work on plain old static maps loaded from external data.

2020-03-10

It was suspiciously easy to implement prebuilt maps.

I downloaded REXPaint, drew some boxes, and saved the *.xp file. SadConsole has a pretty handy built-in REXPaint reader that parses the native REXPaint format, and even lets you iterate through map layers in the .xp file for reasons. Mine was a simple file, no layers, no special coloring, just floors and walls.

After drawing the file I built a StaticMapLoader class. This has a static “LoadMap” method that loads a map file based on filename, parses its contents, and returns a valid game map from the contents. Right now the parsing is dead simple: map a REXPaint tile to its tile blueprint cousin in the map generation code, and bam, we’ve got a walkable map. Player is placed based on the map’s WalkabilityView, monsters are similarly placed, and things just work!

screenshot of game

It isn’t quite perfect. Monsters, after disappearing from my FOV, no longer reappear — though, they’re still “on” the map and will still follow, attack, and die. Also, I forgot to remove monsters from the Scheduling System when they die, so that throws a null reference exception. So got a few bugs to fix.

And now the above bugs are fixed… Next I think I’ll work on the tile blueprint stuff so it can read more tile types from REXPaint.

So I:

  • drew a full starting map in REXPaint
  • added mapped char values to Tile Blueprints
  • added Tile Blueprints to represent map objects as drawn, with my own colors

Which resulted in:

screenshot of game

Things are looking all right. Not sure I’m a huge fan of the way I’m doing these tile blueprints – but they work for now. It’s a fairly standard factory pattern that, when you call it with a “tile name”, returns a new instance of that type of tile object. I can’t say exactly what it is I dislike about doing it this way, except that I have to type out every type of tile I ever want to use (just the once, though, which is fine I guess) before I get to use it.

2020-03-12

Started working on a “message history” window to view some messages that are older and no longer displayed on the current message window, then I decided it wasn’t worth spending time on yet and moved on. I am, however, storing all messages in a list (for now) with the plan that, on death, you can go through and view it all, if you wanted.

Next I started on a MapFactory that I plan on using to transition between maps. This will have a handful of basic “GetMap” style methods, where you give a maptype and some settings and get a map in return. I also need to extend the basic map class with a few properties for starting position and entrance/exit. Then map generation itself will need to be updated to account for ways in and out.

If I plan on letting players go back and forth between maps (probably not, or at least not more than once in any direction), then there needs to be a way not only to clear entities from the map when transitioning, but to restore them when going back. Gonna think on that one, but my gut says to not do it. Progress should be forward-only, with “backward” being a return to “town”.

Anyway, I don’t think I’ll get too much done tonight. Work has been a little more draining than usual, and my sleep has not been very good lately, so I’m moving slowly tonight.

2020-03-13

I decided to not pass in strings for the map factory. Instead, there’s now a MapType enum, with the first two slots reserved for STATIC and PREFAB. I like the idea of having a sort of strongly typed set of map possibilities. Plus, it’s easier this way to randomly select a map generation type by picking a random integer within the range of enum types.

In putting together the map factory and the other pieces to allow for smooth map transitions, I’ve realized how scattered some of the map update / drawing code has become – to center the viewport on the player, for instance, can fail thanks to bad order of operations. Or, creating the ScrollingConsole that acts as a renderer for the map itself can fail if I try to instantiate it before the map itself is created. These are small things and very easily fixed, but it’s still a distraction and again I find myself looking at this late at night after a long day and don’t have the energy to do it. Oh, well. That’s what weekends are for, right?

What else am I going to do with that coronavirus out there? 🙂

2020-03-14

Mostly have map transitions working well. A few hitches here and there and there is definitely some more cleaning to do. I’m in phase 1 of this, which is “get it working.” Soon, I’ll be on to phase 2: “make it right.” For now, I just want to be able to hop from map to map without too much hassle.

I redid the main map a little bit to add some hard-coded stairs down and also some light sources. Then I wrote the code for light source entities, made them cast their light, etc., but there’s currently a bug where the player can sometimes not see inside the light source radius of these entities if his own FOV so wills it. Going to file that one under “fix me later”. For now, I kind of like how it is, and will spend my time further refining the flow of map-to-map travel.

Here’s the starting map with its torches:

screenshot of game

(Later) I added some TileFlags (which is an enum of bitshifted ints) to allow for StairsUp and StairsDown — later, maybe I will split these flags based on the flag’s purpose, but for now there aren’t many and it’s Good Enough — so when the factory creates stairs I don’t need to do anything else for the map to know where they are. When descending, the player will start on the stairs that lead back up, and when ascending, the player will just pop back to “town”. For now the stairs and placed randomly on a walkable position. When you stand on stairs and ask to go down, you’re prompted so the game is sure you didn’t hit that key by accident. If you say yes, you descend.

For funsies, I made it so I can tap M to jump to the next map automatically. Here’s a gif of me holding down the M key.

screenshot of game

I only have three “random” map generators right now: the Big Room Generator, which just makes a big room; the Cellular Automata Generator; and the Random Walk Generator. I think I’ll work on a Binary Space Partition generator next, or maybe just a regular old random room / hall placer.

Week in Review

Feels like I got an okay amount done this week. Pathfinding, dead simple AI, map transitions, static map loading… Not too shabby!

In the coming week I plan to work more on game library stuff. Right now my single monster is a hardcoded entity. I want to take advantage of my SCORLIB library and external data, and generate monsters using a Monster Factory. At first it will just be a small range of monsters, but the eventual plan is to generate specific types of monsters on specific types of “dungeon floors,” where each floor will be devoted to a deity, and the monsters there that deity’s followers. So, with the aggression system, you may end up on a floor of totally peaceful creatures.

All the “hard work” of serialization and deserialization has already been done, so right now it’s a matter of a) actually creating the data and b) plugging that data into the game itself to plop monsters down in the world. Next week.

SCRL: Week 01

2020-03-02

Spent too long rewriting FOV code because monsters were being drawn even outside the player’s FOV, only to realize my calls to get/set “isvisible” were setting the wrong property. Rewrote everything for nothing. Instead, called Animation.IsVisible, and they turned invisible as expected.

So I reverted back to old FOV code and set the appropriate flags there. Tomorrow: – Rearrange UI consoles – add “DialogueWindow” to handle dialogue and message pop ups – add “InventoryWindow” to show inventory

2020-03-03

No need to implement special Dialogue windows. SadConsole already implements Message and Prompt windows. Message windows allow you to show a simple message window with text and an OK button, while Prompts are yes/no dialogues with Action as a result callback.

Instead I’m going to focus on the UI a little bit. I want to make the MessageLog a little more readable and add a hotbar / belt style window which the user can click for useful actions like drinking a potion.

I shrunk the Adventure window and centered it, along with the MessageLog. On the left side of the screen is the Belt, and the right has Stats. For now, this layout will be good enough. I’m not going to work on an Inventory window right now, since there are no real items to speak of. This UI will suffice. It’s time to start working on:

  • spawning monsters
  • log messages when a monster is seen, how aggressive it looks, etc. – partially done (need aggressive check)
  • add monster to Stat window with neutrality ([n] for neutral, [a] for aggressive, etc.) and health
  • make monsters aggressive toward player attack player
  • make monsters aggressive toward each other attack each other
  • make neutral monsters do nothing

Procrastinated by setting up blog. Procrastinated a bit more by setting up a git repo for the project. For now it’s private.

Progress so far:

screenshot of game

2020-03-04

Decided to bring in a fork of SCORLIB from the old project. A lot of work was done there for the little details that I will eventually be using, mostly in the ActorInterfaces namespace. I’ll probably drop the ‘xxClasses’ folders in favor of implementing the interfaces in the game itself, rather than the library. I just didn’t want to rewrite all this stuff.

Got caught tinkering with MessageLogWindow because I didn’t like how some code could cause null reference exceptions if a monster spawn in the wrong spot at the wrong time — order of operations on generating map & FOV then logging monster appearances. While tinkering, noticed SCLR uses ~165MB memory almost right off the bat. Just for fun I looked at the old scrl and saw it used around ~70mb. A memory snapshot shows ~30k SadConsole.Cell objects and ~30k EventHandlers — an event handler for every cell? Is this a SadConsole 8 thing, a GoRogue thing, or did I do this by accident?

Looking further into the dump it’s partially GoRogue, partially me. In this fork of the GoRogue.SadConsole helpers library, the “Tile” class instantiates an event handler on every single tile. With a 100×100 map, this adds up. There’s a few other issues in here too, but I’ve just decided to leave it. Focus on the game first and come back to the memory later to clean things up.

Couldn’t leave the message log how it was, so I move the queue away from the UI so it’s a static queue with a set size within the library. the ui just draws the messages and that’s it. Dropped 10mb of memory by dumping the scrollbar? Interesting.

Moved FOV calculation out of base LivingCharacter into the Actors descending from it. So Player does Player’s FOV, Monster does Monter’s FOV, and no more checks for “if ControlledGameObject” or anything like that.

Monsters will log when they first spot the player, but that’s it right now. I won’t be doing the aggression-check stuff until later. I can spend time doing basic “move in player direction and bump him” attacks, or I can spend time on a command / scheduling system. I think I’ll do the latter, but dunno if I want to do that today or later.

2020-03-05

Debating the merits of using GoRogue’s Actions vs. building out / rebuilding my own command and scheduling system. GoRogue’s feels a little overengineered for my needs and I feel that if I try to conform my own work around it, I’ll end up fighting it rather than being productive.

On second thought, I think I will try the GoRogue way of things. It has many moving parts, but allows for more flexibility for each entity to manage its own actions, rather than a central processor processing actions for everyone. We’ll see how that goes.

(later) Turns out that was easier than I thought. Monsters just sit around and wait to die, but combat technically “works”. Player can hit, miss, etc., based on his and the monster’s stats. Very basic, but it gets the job done for now. When the monster dies, it’s removed from the map, and the player can move into that spot.

Next:

  • Make the monster drop some gold or something
  • Make the player pick up that gold when he walks over it
  • Make monsters who have sighted the player move toward him to attack

Later:

  • Add / update a scheduling system so monsters and players move at their own speeds, and so that no one moves if there’s no player action taken

Right now:

  • Add a small display to show in the UI the monsters on-screen with a health bar

screenshot of game

2020-03-06

In true Me Fashion, dropped the last thing I was doing – not going to display monster health on the sidebar for now. Just went ahead with dropping items when monsters die. This involved:

  • Adding ItemBase and ConsumableBase
  • Adding an Inventory with a limited size and stackable consumables
  • Adding a Gold class – for now inherits from ItemBase, as it’s “outside” the Inventory, but I might change later and make gold a Consumable inside the inventory so it, too, takes up space. This feels… draconian.

Now, monsters drop gold on their deaths (that’s it right now). When a player tries to walk into gold, he picks it up, but doesn’t move there.

I think how this will work in the future is a player can move onto the gold tile and auto pickup, but items must be picked up manually. I want multiple items to drop on one tile, and when the player tries to pick up, a dialog pops up showing item details and the inventory, and the player picks which ones to get.

As a side note, I need to be more disciplined on my commits, and commit when something is done and working instead of letting it all build up over the day…

screenshot of git changelog

2020-03-07

The ActionProcessor stuff from GoRogue wasn’t working out for me. When it came time to implement an actual scheduling system on top of it, I struggled with conforming my own ideas to the action stack concept — instead, I went with a Scheduling System / Command System similar to my old project, where the Schedule is a sorted dictionary of times and ISchedulable entities. The game loops through the schedule and executes actions for each entity at a given point in the schedule, waiting for input when it’s the player’s turn.

This way, a monster or NPC can have a faster/slower speed than the player and thus act more often or less often, depending. Not going to be usable or useful for a while, but it’s there for me.

I also made it so you now auto-pickup gold when walking over it.

I got tired of crawling through caves to get to monsters to test this so I just made a “map generator” that generates a single room. This room is always “lit”, but the player sight is still small, to test monsters appearing and those types of messages.

screenshot of game

Next, I implement pathfinding. What I want is for monsters to move toward the player while they see him, and if he leaves their sight, they continue moving to his last known location or until they see him again.

Week in Review

It doesn’t feel like a great deal of progress was made this week. I spent most of my time shoring down systems in place, making sure they worked well enough, and moving on. So there haven’t been any gameplay additions. I’m okay with this for now. My first (non-blogged) days were spent on map generation and endless tweaking of stupid nonsense that doesn’t even exist anymore, so in comparison I was very productive while I blogged!

Writing out what I have worked on, and then what I intend to work on next, has been very helpful in keeping myself on track and away from falling into the pit of nonsense work. I have a good feeling about moving forward and the progress I’ll make next week. I think, at least for the near future, things are going to speed up some, and I’ll have something moderately playable and fun.

SCRL: Introduction

Project SCRL, or “SadConsole Roguelike”, will be a Diablo-style roguelike game written in C#, using Monogame / SadConsole / GoRogue libraries for display and various rogue-specific utilities.

This project originally began in 2018 when I decided to try to make a game “for real”. I read a bunch of tutorials, set my sights high, and got to coding. I had a functional, but basic, game, which I though was well-engineered and extensible, but in the process of actually extending it I ran into a lot of roadblocks. Most were of my own doing with my ambitions, and some were with the tools I used. I let the project languish.

A few days ago I picked it up, dusted it off, and saw something that could be fun in there. In true Developer Fashion, I am not using the old project, but starting from scratch. I’ll reference old code and old design documents here and there, but I want to try and do things a new way this time. To start with, I’ve decided to document the process every day I work on the project.

At first, it will mostly be raw notes for each day of work. These will include to-do lists, accomplishments, or trains of thought that lead absolutely nowhere. Hopefully, if the project continues long enough, there will be enough material to write detailed and informative blog posts.

For now, here’s the original “game design document”:

=====

Are you a bad enough dude to come out alive?

The Chapel of Stilled Voices

Deep in the jungle lies the mansion of an ancient and wealthy madman. In his elder years he grew to fear death, and built shrines to every god imaginable so he could pray and offer his material wealth to them to stave off death. This didn’t work, naturally, and he died, leaving behind no family.

The mansion is a small castle, with countless wings and rooms filled with both unimaginable wealth and unimaginable horrors — for some of these gods were not benevolent.

Word traveled, legend grew, and adventurers came from far and wide to try to get to the fabled treasure. Some, wiser than the others, decided to simply set up shop on the road leading to the mansion, to sell to and buy from those about to give their lives to one of the countless gods in what would be called the Chapel of Stilled Voices.

You, the player, are one of these foolhardy adventurers, set on finding the treasure within and becoming rich beyond your wildest dreams.

Game Loop

The game play will consist of two main loops.

Dungeon Crawl

In this portion of the game, you are trawling through the dungeon, killing monsters, picking up loot, and killing more monsters.

The Dungeon Crawl will be high-paced action, with a high number of mobs on-screen at once to deal with. This makes the next portion of the game a welcome reprieve and more likely to be cherished rather than dreaded.

Every level of the Chapel (dungeon) is randomly generated the first time the PC explores it. Subsequently, each play-through re-randomizes the Chapel, so Level 1 will be different for PC1 and PC2.

The random generation should use a combination of prefabs (prefabricated sections of dungeon, hand-designed and combined like puzzle pieces), as well as pure “random” generation like Binary Space Partitioning.

Towntime

In this portion, you are interacting with merchants in-town, accepting and turning in quests, or enhancing your items or yourself.

In the beginning of the game this is just a camp where you can rest and store items. You’ll have a bed and a chest. If your run lasts long enough, or if you accumulate enough wealth (via gold or item worth), new merchants will be drawn to your camp area. As the game progresses, more and different merchants will appear, each offering different services or themselves in possession of more wealth.

This progression may or may not last between runs. If the player reaches yet another threshold, then the game considers this place “noteworthy” enough for certain merchants to stick behind after the player dies, so the next player character who arrives won’t start completely from scratch.

Religion

Religion will play a big role in SCRL, mechanically speaking. Because the Chapel is a mish-mash of altars to various gods and spirits, the character’s religious affiliation will have an impact on every playthrough.

This page will attempt to list the major religions of the game setting, along with appropriate metadata.

Alignments

As with many RPG systems, SCRL will use a type of alignment system, though (as with other systems in SCRL) it will be simplified. Rather than a 3×3 grid of alignment types a la D&D, it will be this:

  • Lawful
  • Good
  • Neutral
  • Evil
  • Chaotic

These alignments will not affect gameplay choices. They will affect how certain spawned monsters in-game respond to the PC’s presence. If the PC happens upon a shrine room to their chosen god, the monsters within may not respond to the player, or perhaps will even interact with and act as special in-dungeon NPCs (one-time shops, like in Fate).

Aspects

An Aspect is a quality, statement, or goal a character may strive toward, and which a Deity personifies. A Deity may personify multiple aspects, and many Deities with different Alignments may share Aspects among each other. The more Aspects a PC has in common with a given Deity, the more friendly that Deity’s followers may act.

Aspects will be defined in a metadata file, but here is a basic list of some.

  • “It is a good day to die!”
  • Intrepid
  • Wisdom
  • Desire
  • Pleasure
  • War
  • “No stranger to pain”

Aesthetics

“Aesthetics” refers to visual aspects of a Deity. A Deity may be dark and broody, or bright and bubbly. This data is mostly meant to be used in room / shrine generation, and will also be contained in a metadata file. Some examples of Aesthetic types are:

  • Alien
  • Dark
  • Colorful
  • Strange

Deities and Demigods

The world of SCRL is filled with countless deities, demigods, major and minor spirits, primordial beings, ascendants, etc. From now on they will simply be referred to as Deities.

Each Deity follows one Alignment, embodies up to three Aspects, and holds up to two Aesthetics. Full Deity data will, of course, be held in a metadata file.