• Weekly Update – September 22, 2023

    It was a moderately productive week. My average game dev time per week has dropped by 4-6 hours in recent months. My weekend blocks are shorter because I’m not waking up as early. My morning blocks are shorter because I modified my morning routine (previously: wake up, work for 30-60 minutes, go to the gym, continue working for another 30-60 minutes; now: wake up, go to the gym, work for 30-60 minutes). I’m also not working as often in the evenings because I’ve been more tired after work.

    • History generation cumulative stats. After reviewing many generated levels, it was evident that some history event types were selected much more often than others, and some were never selected. To fully understand the history event type distribution, I added a JSON file containing each event type and the number of times the event type is selected. Each time a level is generated, the JSON file is updated. The file confirmed my observations and gave me specific event types to investigate. I focused on the event types that were never selected. In some cases, the behavior was working as intended; some events shouldn’t occur in certain section types (example: a throne room shouldn’t be built in a cavern section). In other cases, the event type eligibility criteria or entity placement criteria was misconfigured (example: a shrine couldn’t be placed in a cavern because it only allowed rectangular rooms). I corrected the misconfigured event types, but there were still some event types that weren’t being selected. These event types depend on impossible combinations of prior event types. An example of this is the leader of a faction fighting the leader of an opposing faction. There’s no event type to add a leader to a faction yet, the leader fighting another leader event type always fails to bind to existing entities. I’ll fix these scenarios next week by adding missing dependent event types.   
    • Override for entity placement path-blocking restriction. Last week’s fix to prevent entities (actors and objects) from being placed in locations that block the path introduced a new issue: enemies weren’t being added in many cases. For example, Gelatinous Cubes stopped appearing in corridors because they blocked the path through the corridor. To fix this, I added a flag to allow the placement of path-blocking entities on a case-by-case basis.
    • Entities and events created by the history generator are now associated with physical in-game entities. World history events contain abstract entities. During level generation, physical entities are created from the abstract entities. Before this week, it wasn’t possible to see a physical entity’s corresponding abstract entity and the history events involving the abstract entity. I added this capability for debugging purposes, but it may have other uses in the future.
    • Bug fixes
      • Entity placement path-blocking restriction not working at the edges of a room.
      • When inspecting an actor/object containing items, the items are displayed even if the actor/object is not next to the player. 
      • Treasure room placed next to the start of the level.
      • Cultists don’t do damage when they attack.
      • Ghosts are overpowered.
      • Wisps shouldn’t appear on level 1.

    Next week, the first goal is to filter history event types based on the level range of the items and actors in the event type. This is necessary to prevent underpowered and overpowered items and enemies from appearing in the level. The second goal is to fix the history event types that are never selected by the history generator.   

  • Weekly Update – September 15, 2023

    I started the week by playing for an hour and logging all the issues I came across. I spent the rest of the week fixing those issues.

    • Bug fixes
      • Movement-blocking objects can be placed in locations that disconnect a level. This problem has existed since the original map generator was developed several years ago. I’ve made a couple of unsuccessful attempts to solve it in the past. This week I was determined to find a solution, and I did so just before this update. The basic concept is to check if an object will disconnect the room it’s being placed in before placing the object. That sounds simple and obvious but the implementation was complicated.
      • Maps are sometimes too small. The map generator is supposed to prevent this by checking the percentage of cells that are populated and adding more rooms if a minimum value isn’t met. This check uses a counter to prevent infinite looping. The counter increments on each attempt to add a room. Many attempts were failing, causing the counter to reach its limit before the cell minimum was met. The attempts were frequently failing because new rooms couldn’t be placed in the random location chosen. The solution was to limit the list of possible room locations to areas where the room would fit. 
      • When an enemy is killed, the player’s selected weapon changes to the enemy’s selected weapon. This was an odd one. When an actor dies, it’s replaced by a corpse and its items are transferred to the corpse. An event is generated when an item is added to an actor. A listener for this event checks to see if the item is a melee weapon and, if so, equips the item. A separate listener for this event updates the player’s selected melee weapon in the UI. The second listener was responding to all events, not just the events for the player. This was fixed by having the event listener check whether the event was triggered by the player.
      • Error occurs when a damage sound effect is played for an actor without a melee weapon.
      • An error occurs when fire spreads to a cell containing an item. 
      • An error occurs when a status effect is removed from a corpse.
      • Braziers can be placed outside of non-rectangular rooms.
      • Some room types, such as Alchemy Chambers, appear in corridors.
      • Actor names are displayed in the Inspect Panel when the player doesn’t know the actor’s name.

    Next week, I’ll start with another play session, but the focus will be (hopefully) less on bugs and more on small improvements and polish.

  • Weekly Update – September 8, 2023

    I’ve emerged from the cavern proc gen rabbit hole I entered at the beginning of August. Cavern structures are in good shape now that loops can be added in a few ways. Further improvements are possible by tweaking the cavern generator parameters and adding more quality control checks. For another day…

    Cavern with loops example 1
    Cavern with loops example 2
    • Loop-generating cavern corridors. Unsatisfied with loops created by removing a single wall or adding a short, straight path, I implemented the ability to connect existing caverns by standard cavern corridors. This was pretty difficult for two reasons. First, it’s more complicated. When caverns and corridor blocks are initially generated, they only have to connect to an existing block, and their sizes and connection points are only constrained by the available space. Inserting corridors after the initial blocks are placed requires finding empty space where a corridor can be placed to connect two blocks, connecting the corridor to the two blocks, and forcing the random walk algorithm to create a path between the connection points and within the available space. The other difficulty was the code. The block placement code was complicated and not well organized. Also, it wasn’t designed to handle adding blocks after the initial blocks were placed. Reworking the code to support the new use case required massive mental energy.
    • Made darkness darker. All of these subterranean environs – caverns, catacombs, dungeons, etc. – didn’t seem dark enough. It was too easy to see in the dark. I adjusted the darkness settings to increase the contrast of light sources and the surrounding darkness, and make it darker when the player doesn’t have a light source.
    • Bug fixes
      • Some caverns have unreachable areas. This was caused by a bug in the grid traversal algorithm, which is used to create paths between disconnected areas. When the slope was exactly 1, the algorithm 
      • Can’t take a potion off a table. A recent change to how multiple entities in a cell are sorted for interaction broke this.
      • Can’t walk over an extinguished campfire.

    A couple of months ago, in preparation for the next playtest, I decided to temporarily limit the scope of the Legend to a single level theme (Caverns) and class (Knight). With the four months remaining in 2023, I’ll continue working in this limited scope so that I can concentrate on core gameplay. Next week I’ll add more cavern content along with some bug fixing and polishing.

  • Weekly Update – August 26, 2023

    • Loops. After the map structure is created, the map generator looks for places on the map where distant (graph-wise) rooms can be connected with minimal structural changes to form loops that reduce player backtracking.  After adding cavern corridors, the map generator struggled with creating loops. The patterns the map generator looked for to add loops (shared cells between adjacent but unconnected areas and areas that could be connected by short, straight paths) were no longer common. I expanded the criteria to include short, non-straight paths.
    • Cavern generation visual aids. Cavern generation has been much more challenging than dungeon generation because the former involves non-rectangular shapes. I added visualization of potential connection points, wall cells shared by unconnected adjacent rooms, and potential corridor routes to form loops. These helped immensely with testing and troubleshooting.
    • Refactoring. I extracted some of the map generation code into new classes because the functionality that the code provided was now needed in more than one place. For example, rooms needed to be generated for looping corridors, but the room generation code was encapsulated in the map generation command class for generating rooms. I extracted the room generation code into a new static class. Most of the refactoring I do now employs Data-Oriented Programming.

    With cavern corridors wrapped up, next on the todo list is an assortment of new content, enhancements, and bug fixes for caves.

  • Weekly Update – August 18, 2023

    Cavern corridors took three weeks to implement. That’s over 5% of a year. That’s enough time and effort spent on one feature to raise questions. Was this a necessary feature? Was it worth the effort? How many more features requiring 3+ weeks of development are required to release? Granted, it wasn’t a full three weeks because of my backpacking trip in the middle, and the effort included improvements and bug fixes to shared libraries (cellular automata and random walk), but it’s still cause for questioning.

    Cavern example map 1
    Cavern example map 2
    Cavern example map 3
    • Refined cavern corridors. The random walk algorithm used to create cavern corridors is configured to more often move toward the target rather than a random direction. This makes corridors look more like tunnels. Sometimes it works too well, producing unnaturally straight paths. I added a check to force a direction change when there are too many consecutive points in the same direction.
    • Alternating caverns and corridors. Realistic cave layouts are typically composed of individual caverns connected by tunnels. To mimic this, room types can now be weighted differently based on the preceding room type. This makes it possible to have caverns and corridors alternate most of the time.
    • Bug fixes
      • Fixed a long-standing bug that caused map generation to occasionally fail when attempting to place the first room. The cause was simple: for some map themes, there was no room type that could fit into the starting space because the minimum room size was larger than the margin size used to select a random point on the map. Increasing the margins fixed the issue.

    Next week, I’ll add loops to caves to reduce backtracking and add a random assortment of gameplay refinements.