In last week’s update, I stated that sound effects would be the focus this week. I didn’t touch that. Instead, I wound up spending the entire week on visual improvements.
Replaced ability icons. I was using effects and item sprites from the Oryx 16-Bit Fantasy sprite set as temporary ability icons. The images were loosely applicable at best, and too abstract to infer their purpose. They were plain and in some cases downright ugly. I didn’t want to see them anymore so I bought a giant RPG icon set from the Unity asset store to replace them. The visual style didn’t quite match the rest of the game, but that’s ok; I still plan on replacing them with original artwork eventually. I wasn’t able to use them “out of the box.” They were 32×32 pixels in size, while item/ability slots were 16×16. My solution was a compromise: I’d crop the icons to 24×24 and increase slot sizes from 16×16 to 24×24. I spent more time on this than I expected. It took a while to find the right icon for an ability, and additional time to find the right 24×24 section within the original to crop. It also required resizing slots everywhere they were used (inventory panel, ability panel, inspect panel, hotbar, quick switch slots). Since the slots use a common prefab, I only had to change the prefab rather than each individual slot, but then I needed to change panel sizes and move some UI elements around to accommodate the larger slot sizes.
Increased the hotbar size. This was necessitated by replacing the ability icons. I’m not thrilled about giving up more screen real estate for the hotbar, but I don’t have an alternative because I believe 24×24 is the minimum resolution for an image to portray an ability.
Replaced the font. The original font was an 8×8 monospaced font commonly found in early arcade and console games. The main issue with this font was space. It was difficult to read at a small size. I could have increased the resolution for text, but it wouldn’t have looked right to have pixelation at two different resolutions (one for images and one for text). Also, my aesthetic preferences have changed since then. It can be a long road to finding the right font. 80% of my time this week went into searching massive font databases and curated lists, studying screenshots from other games, and trying different fonts out in the game. I could have easily spent the entire week, and many future weeks, on this pursuit. I have a persistent problem with wanting to start with the largest space possible, explore every possibility, and narrow down to the absolute best choice. With something like fonts, there just isn’t a best choice; it’s too subjective. I decided on Merriweather because it’s a serif style but is designed specifically for screen use.
New status effect: Charmed. Causes an actor to fight for the caster.
Charm. Temporarily charms an individual actor.
Mass Charm. Temporarily charms all actors in a 5×5 square.
Improved stat meter visuals. I wanted to make these look a little less plain.
On the Title Screen, the Continue button now appears above the New Game button, since the former will be used more often than the latter.
Select cell prompt repositioned so that it doesn’t appear over the player or other UI elements.
Increased the number of recent log messages displayed from 4 to 8.
Reduced the size of tooltips.
Next week, I plan on adding a loading screen (the presently inefficient proc gen freezes the game for ~10 seconds per level), enemy status effect icons, and sound effects time if time permits.
After literally years of research, brainstorming, analysis, and procrastination, the types of resources consumed by abilities have been determined. This decision has been drawn out by many dependent questions regarding Legend’s underlying resource management design: are resources finite or infinite? Is there a hunger clock? Do health, magic, etc. regenerate? Not all of the questions have been answered. I aspired to find the perfect solution, but the model is too complex to assemble, and I no longer believe a perfect solution exists anyway. I increasingly aim for solutions that are good enough, solutions that don’t offer complete flexibility, but are flexible enough. I embrace the resulting constraints, for they shrink the answer space, and foster creativity. And thus, at the end of the ability resource design journey, I arrived not at revelation but convention, with the health/stamina/magic trinity, a staple of the PC RPG.
Ability resource consumption design. Abilities may consume stamina, magic, health, and/or items. Magic fuels spells, stamina powers physical feats, and items are needed for specific skills such as picking locks. Like health, stamina and magic are represented in points. The quantities of resources consumed by each ability will be fine-tuned during balancing.
Player magic and stamina meters. Stamina and magic meters now join the Health meter. I reduced the dimensions of the meters so that they cover up a smaller screen area.
New Action Step: Consume Resources. Luckily, I didn’t have to implement a new system to handle resource consumption; it fits nicely into the Action Step framework, becoming another building block for constructing actions. This was the most logical solution, since abilities essentially just invoke actions.
Added Action Source to Actions. Actions are defined using the following structure: [Actor] do [Action Type] with [Item(s)] on [Target] in [Context]. For the Consume Resources Action Step to work, it needs the source of the action (the ability that invoked the action), so that it knows which resources, and how much of each resource, to consume. The action source has been added to the actor structure, making the structure now: [Actor] do [Action Type] from [Source] with [Items] on [Target] in [Context]. This required a lot of changes in the code, but was done quickly using global search and replace (which is surely an architecture smell).
Magic and Stamina Potions. Self explanatory.
Health to Magic. Recover magic at the cost of health. No class with healing abilities will be able to use this, since it would be possible to have unlimited health and magic.
Magic to Stamina. Recover stamina at the cost of magic.
Visual effects. While looking for a way to modify sprite colors at runtime (I need to display grayscale versions of sprites and make sprites appear frozen when they are hit with an Ice spell), I came across a Unity Asset, All in 1 Sprite Shader. It does far more than I need (at least currently), but it was very popular and highly rated (and on sale) so I bought it. Of all the Unity assets I’ve installed, this asset is probably the easiest to learn; simply add the component to a gameobject and, using the component’s UI, select the visual effects you want to apply. However, I don’t think I’m using it in the intended way. I didn’t want to add the component to the dozens of actor and item prefabs because I didn’t want the visual effect to be always on, and I didn’t want to maintain an extra component for actors and items. So, I just used it to make materials, and I’m applying the effects at runtime by changing the sprite material.
Improved random Map Element selection. Previously, all Map Elements were assigned a rarity level, which served as the weight in a weighted randomizer. The problem with this is that the rarity distribution changes depending on the number of Map Elements of each rarity level. I’ve now split the selection into two steps. The first step selects the rarity level using a fixed set of probabilities. The second step selects a Map Element from the list of Map Elements of the selected rarity level. This ensures that Map Elements are correctly chosen based on the rarity distribution.
Quick Switch and Hotbar slots still interactive after the player dies
The action indicator cursor still appears after the player dies
Enemies stop moving when the player moves out of sight
The Escape key now cancels the Select Cell prompt. My goal is for the game to be completely playable using a keyboard and/or mouse.
Ability resource consumption support
Preventing abilities from being used if there are insufficient resources
Dimming slots containing abilities that can’t be used due to lack of resources
Tooltips indicate why ability slots are dimmed
Next week, the major task is reassigning sound effects. I’ve purchased several collections from the Unity asset store, but I recently pulled the files out of the Unity project because they were making project snapshots huge. Now, I need to bring only the sound effects that the game is using back into the project. Beyond that, there will be more play-testing and tightening things up.
Adding a new ability to summon a swarm of giant rats nearly derailed my plans for this week (and potentially the next few months) when I tried out the ability and, to my surprise, the rats started attacking each other. I realized that there was no way of indicating that the rats were both allies of the player and each other; I needed to be able to specify relationships between actors. Factions were the first thing that came to my mind. They’re overkill for this single use case, but I’ve always intended to have factions in the game and reasoned that I could use them to solve the rat problem. As I’ve done with other aspects of the game design, I start with a flurry of research and brainstorming. I read up on factions in game design and roguelikes specifically, including a FAQ Friday on the topic, and wrote down all of the ideas and details I could think of. And, as I always do in this situation, I got lost in the subject and created a conceptual design that far exceeded what I likely needed and what I had time to build. That’s not a bad thing, as long as you can quickly pull yourself back into reality and reduce a grandiose design to something practical. I was able to do that in this case. My solution was to add an actor attribute indicating whether the actor is friendly, neutral, or hostile to the player. It’s a compromise, and there’s a good chance it will be thrown away in the future when I add a true faction system, but it’s the right solution right now, because it was extremely simple to implement and I don’t have a clear vision for how factions will work.
New Ability: Summon Giant Rat Swarm. Summons a swarm of giant rats to fight on the player’s behalf.
New Item: Fire Sword. Sets enemies and objects it strikes on fire. It’s overpowered at the moment, but I haven’t decided how to nerf it.
Chests and corpse item access. The items contained in chests and corpses can now be viewed and taken from the Inspect Panel. A “Take All” button lets all items in the container be taken at once.
Inventory Panel is automatically displayed when inspecting a container. When viewing a container, such as a chest, the player inventory is now displayed automatically. This allows items to be dragged from the container into inventory and vice versa.
Weighted observation selection. One of the configurable AI components is the ObservationDecider. This component examines the actor’s observations since the last turn and chooses an observation to react to. There are two implementations of this component, one that selects the first observation involving a tracked actor, and another that randomly selects an observation (used by the Fear status effect). These implementations are primitive. They don’t work well when there’s more than one suitable observation to respond to. To address this, I modified the ObservationDeciders to weight each observation and select the highest weighted observation.
New automated tests: Eggs. I’ve repeatedly broken the functionality of eggs, so this was a great automated test to add. The unique behavior of eggs (turning into a cracked egg upon detecting the player, waiting 10 turns to hatch, hatching a creature at the end of waiting) tests a number of systems.
Installed History Inspector from the Unity Asset store. My project asset list has gotten enormous. ScriptableObjects are used extensively, and many of these ScriptableObjects reference other ScriptableObjects. I often have to follow a trail of ScriptableObject references to fully understand how something is working, or go back to the previous asset I was viewing. The time spent scrolling through the asset tree, and the effect of navigating the structure on cognitive load, is impacting my productivity. I looked for a built-in Inspector history viewer in Unity but couldn’t find one. So, I turned to the Unity Asset Store and found the History Inspector asset. It’s already become an indispensable tool that’s saving me a lot of time.
Next week, I’m play-testing combat and overall level difficulty. I’ll adjust and fix based on what I find. The overarching goal for the year remains getting to a version of the game that I can distribute to others.
Ring of Protection. Improves the wearer’s damage absorption.
Ring of Evasion. Improves the wearer’s chance of evasion.
Resurrection. Resurrects a targeted corpse. I was pleasantly surprised by how easy it was to implement this. It was 95% accomplished in the Unity Editor leveraging existing capabilities.
Fear now works on the player. The Fear ability, implemented several weeks ago, only worked on enemies. It needs to work on the player too because an enemy may have this ability. Also, I debated whether the caster should be affected if a Fear spell’s area of effect included the caster. I decided that gameplay would be more interesting if the caster was also affected. This forces the caster to be more careful about choosing the area of effect.
AI can now be applied to the player. This enables status effects that temporarily take control of the player, such as Fear. This required a lot of work to implement. I originally assumed the player wouldn’t need AI, and that assumption was reflected in multiple code locations that needed to be modified. This enables some interesting potential future capabilities. For instance, an AI could be written to simulate playing the game.
Fixed several bugs related to torches. Torches have been the source of many time-eating bugs. The underlying logic is undoubtedly overengineered. Torches are designed to be lit when placed in a Quick Switch Slot, and extinguished when placed back in inventory. When in a Quick Switch Slot, they generate a light tied to the carrier’s position. Lighting and extinguishing torches is handled by a Convert Item effect, which destroys the original item and replaces it with the new item. This effect is triggered by moving the torch in or out of the Quick Switch Slot. I’ve spent far too much time getting this all to work. Bug fixing has been arduous because it requires tracing through a sequence of triggered events rather than walking through code line-by-line. The implementation had to be simplified, even if that meant hard-coding. I settled on changing how Convert Item worked. Instead of replacing an item, Convert Item now keeps the original item and changes the item type and some other item attributes. It’s not as clean, but it gets rid of item destruction and creation (and the logic triggered by those events). It’s also preferable from a performance standpoint, but that was a minor concern.
Changing levels is fully working (again). I’ve been play-testing on a single level for months and broke most of the functionality around changing levels (example: going upstairs to the previous level placed the player on the upstairs cell rather than the downstairs cell). To support the new way that actions work, I had to add Ascend and Descend actions. I discovered some functionality that had never been implemented, for example preserving Quick Switch and Hotbar slots. This was a little tricky because the Quick Switch slot state is part of the standard actor inventory (as selected items, decoupled from the UI), while the Hotbar slot state is housed outside of actor inventory as it’s only needed by the player.
Automated tests added for potions, scrolls, and rings.
Next week, I’ll keep fixing the known bugs and play-testing to find more bugs. This will continue until the game is in a state where I can play it for an extended length of time and not encounter a single error in the log or exception. I’m getting there…
The two days I had off from work this week doubled my available dev time. Note those weren’t full days; they were just two extra 3-4 hour blocks of time similar to weekend work. A wide assortment of to-do’s got done, including minor tweaks and enhancements, bug fixes, refactoring, and some miscellaneous items. The general theme was getting the game to a stable, bug-free experience.
Turn Undead. Classic cleric ability that causes nearby undead to flee.
Heal. Heals the caster.
Self-Targeted Abilities. Abilities, such as Heal, can now be configuredto target the user by default.
Entity collection-specific Abilities. Abilities can now target specific entity or entity collection types. This capability was used to implement the Turn Undead Ability (it’s basically the Fear status effect, limited to undead actors).
Interesting bug fixes
Some enemies were fleeing from the player unexpectedly. Upon further inspection I determined that this only occurred when multiple enemies were present. Even stranger, when a new enemy spawned, an enemy that previously attacked the player started fleeing. It turned out that enemies were reacting to the movement events of other enemies. This shouldn’t happen because enemy AI contains an actor tracker component that limits the actors that the enemy will react to (typically, enemies only react to player actions). The actor tracker wasn’t being used by the enemy’s movement generator.
I wanted to display bones on some of the spikes so I configured the map generator to randomly add a bone pile actor on top of some of the spikes. When I tested this, ghosts appeared where bones should have been. I knew instantly what had happened. The bones are configured to spawn ghosts when they are destroyed. The spike damage destroyed the bones in the first game turn, causing the ghosts to appear. This was an easy fix. I changed the effect trigger for spikes from Touch, which is applied each turn, to DidEnterCell, which is only applied when an actor enters a new cell.
Other bug fixes
Tooltips remain on screen when the Inventory and Ability panels are closed.
Exception thrown when displaying the Abilities panel.
The default player action for Rugs and Summoning Circles was attack.
Cracked Eggs stopped hatching.
Eggs weren’t changing into Cracked Eggs.
Exception thrown when removing the Ring of Invisibility.
Heal Ability wasn’t working.
Closing the Select Cell prompt ended the player’s turn.
Poisonous gas, once spawned, would spread throughout the entire dungeon instead of a limited area.
Run Ability wasn’t working.
Exception thrown when Fires burned out.
Minor tweaks and enhancements
Eggs now have 1 HP so they are killed in one hit.
Destroyed eggs now leave behind the corpse of the creature inside the egg.
Added descriptions for Cauldrons and Crystals.
Grass is no longer damaged by arrows or other piercing weapons.
Bones are now randomly added to some spike cells.
When the game crashes before play starts, unusable saved game files are created. These are now automatically removed.
The default action type for an Open Door was changed from Close to Move. Having Open Doors close when clicked was annoying because 99% of the time the desired action is to move into the doorway rather than close the door. Closing a door can still be done through the Inspect Panel.
Refactored / cleaned up several classes, including the Cell class, which had gotten up to 1,000 lines. There was a lot of code that didn’t belong in Cell – handling projectiles landing on the Cell and handling actors taking items in the Cell, for example. I pulled this code into new static handler classes. This is a poor long-term solution, but it’s an effective way of quickly extracting code that belongs elsewhere.
New automated tests: Ring of Invisibility. I haven’t added any new automated tests in a while. The Ring of Invisibility provides a good set of tests not just for the ring itself but for the Invisibility status effect as well. I intend to write automated tests for all Abilities.
Improved ergonomics (for me). I’ve been having some pain in my right arm lately. I’m not an ergonomics expert, but after identifying the positions that caused my arm to hurt, I concluded that I needed to reduce the rotation of both my forearm and wrist. My mouse was too far off to the side, and I was holding it with a pronated grip (the normal mouse grip). I bought a smaller keyboard (the Logitech MX Keys Mini) to move my mouse closer to me. I bought a vertical mouse (the Logitech MX Vertical Advanced Ergonomic Mouse) to maintain a more natural, neutral grip when using my mouse. I’m very happy with the result. In the first week of use, the pain has mostly subsided.
Next week, and for the near future, stability and bug removal will remain the focus.
It was another light week due to being on vacation for the first half of the week. More bugs were squashed and some new abilities were added. Abilities continue to require a surprising amount of work, including rework and expansion of the supporting systems.
New AI Type: Fear. This AI Type causes the actor to select a fleeing action each turn, even when cornered.
New Status Effect Type: Changed AI. This Status Effect temporarily changes the target actor’s AI Type. It’s used by the Fear spell to change the target’s AI from attacking to fleeing. When the effect ends, the target’s original AI is restored.
New Abilities: Fear and Mass Fear. The former causes Fear in a single target. The latter causes Fear on all actors in the area of effect.
Refactoring: Action consolidation. The recent rework on Actions, in which Actions were reduced to a series of Action Steps, made a number of Actions identical. For example, the Eating, Drinking, and Reading Actions consist of a single Action Step that causes the effect of the selected item. Since Actions are uniquely defined by their Action Step sequence, the number of Actions can be reduced to the number of unique Action Step sequences.
Next week will be more of the same, but I’ll have more time available. There are still many known issues to fix, and many more, I’m sure, that are still unknown. After a few weeks of mainly bug fixing my motivation is waning. It’s been fun creating and trying out new Abilities and I may spend more time on that next week than anything else.
Time was limited this week and mostly went into bug fixing. I add a new map element and a new ability to keep content creation going.
New Map Element: Barrel of Water. This is a variation of a standard barrel that contains water. Destroying the barrel will create a puddle. It can be used to reduce the impact of explosions and put out fires.
New Ability: Run. The Run ability enables an actor to move across multiple cells in one turn. I’m thinking all or most player classes will get this ability.
Item type configuration validation. A few recent bugs were caused by improperly configured item types, for example a ranged weapon having a maximum range of 0. I added some checks to catch these issues when the items are initially loaded.
Next week, time will again be short and bug fixing will again be priority one. I will squeeze in one or two new abilities too.
Implementing the Vampire’s AI was a lot of work. The process revealed the need for additional capabilities to support more varied and advanced AI. With the new AI framework, creating AI for new enemies in the future will be faster. The Egg, also added this week, is evidence of that.
Vampire. The Vampire is a dangerous enemy that has the special ability to transform into a bat and flee when its health is low. It returns to Vampire form after its health regenerates.
Egg. When the Egg detects the player nearby, it starts to hatch. The Egg can be destroyed in one hit if the player gets to it in time. But, if the player doesn’t get to it in time, a creature hatches from it.
Coffin. A Coffin can be opened or closed and can contain a Vampire and/or items, or be empty.
Puddle. This is a single cell containing shallow water. It’s only found in caves. It’s just decorative at the moment. I plan to leverage the existing Physical Materials framework to add interactivity.
New Map Elements
Coffin Area. This map element places a single Coffin in a significant location in a room.
Egg Area. The Egg Area Map Element contains a cluster of Eggs.
Built new reusable capabilities. The above features required the implementation of new capabilities including a new action type to move an actor and trigger an effect at the same time (used when the Vampire flees to move away from the player and transform into a bat), a health regeneration status effect (used by the Vampire), the ability to prevent health bars from animating in specific situations (used when a Vampire transforms into a bat and health is transferred), generate particle effects when effect types are triggered (used to display a cloud of smoke when the Vampire transforms), and a new AI for actors that triggers an effect when the player is seen (used by the Egg to transform into a Cracked Egg), with an optional delay (used by the Cracked Egg to hatch after a number of turns).
`Next week will be a mix of new content and bug fixing.
This week’s focus was on expanding Action Steps and Abilities. A big chunk of time went into solving an issue with Action Step sequencing. An action’s Action Steps are instantiated and queued when the action is instantiated. A manager class then executes the Action Steps in order. This is a problem when an Action Step depends on the outcome of a previous Action Step, for example selecting a cell. I solved this problem by passing the action to each Action Step constructor and using the common properties in every action to pass values (every action has an actor performing the action and optionally, a target entity and entity that the action is performed with). It’s an imperfect solution but I needed to move on.
I also resumed the practice of adding at least one new Map Element each week.
New Ability: Charge. The Charge ability causes an actor to move up to several cells in a straight line and perform a melee attack within a single turn.
New Action Steps: Move and Melee Attack. These Action Steps enable an action to move an entity to another cell and perform a melee attack, respectively.
New Map Element: Spikes. Spikes simply cause damage when an actor walks on them. The player will have different ways of dealing with these, and they can also be useful in some situations, for example pushing an enemy onto them. They’re currently placed in a random row or column within a room.
Refactoring. Redundant parameters.
Next week’s goals are undecided. I’m due for a round of bug fixing; there are a number of known issues that I’m able to work around during playtesting but must be removed before release. I also need to keep the pixel artist search moving forward by posting in a couple more places and contacting the best artists from the original post.
I got off to a great start last weekend, but the weekdays were consumed by my full-time job. I now have four weeks of time tracking data and will share a separate post on that in the coming week.
New ability: Fireball. This ability shoots a projectile at a nearby cell and creates fires in the 3×3 area around the cell. Much of the implementation effort involved supporting code rather than the ability itself. The Create Actor Effect was extended to allow actors to be created in multiple cells at once. I consolidated the Effect.Cause(Actor) and Effect.Cause(Item) into one method, Effect.Cause(Entity) (the Entity class was introduced as a parent of Actor and Item after writing the Effect code. I fixed existing bugs – items in cells weren’t being destroyed by fire, the select cell prompt was wrong, etc. All this work will make implementing other abilities that are projectile-based spells easy to implement. In most cases, this type of ability can be created exclusively in the Unity editor.
Refactoring: StandardMeleeAttackAction class. Melee-based abilities vary in terms of targeting, area of effect, sequence of events, animation, and stat modifiers. I’m still determining how to architect a solution that enables melee abilities to be easily created, modified, and maintained. The starting point is the StandardMeleeAttackAction class, which is responsible for executing standard melee actions. It actually does much more than it should, and the things it does are the building blocks for the melee-based abilities. I cleaned up this class and moved a lot of the logic into other classes for better reuse.
Pixel artist job ad. I’m at the point where I need to start getting original art in place. It’s going to take some time to find an artist (or artists), and much more time to create the new art assets. I wrote a job ad for a pixel artist and will post it in a few places next week..
Next week, I’ll finalize how melee abilities will be implemented and build out one or two melee abilities. I’ll also post the pixel art ad.