I threw away a big chunk of the code that I just wrote last week for Stat Modifiers. I made this decision when it became evident that maintaining a running sum of all Stat Modifiers for each Stat Type wasn’t worth the complexity. I originally chose this approach for efficiency; since the modifiers didn’t change that often, it would be more efficient to precalculate and store the values. In practice, however, the values are used infrequently enough that recalculating them every time doesn’t impact performance. The added complexity, primarily due to having to synchronize the running sums with the individual modifiers, increased the implementation effort and defect risk. In general, it’s preferable to store information in one place only.
Improved Inventory Panel. The number of inventory slots has expanded from 24 to 32, the panel has widened, and combat stats are now displayed.
Tooltips for stat bars and status effects. Hovering over the Health, Stamina, or Magic bars, or a status effect icon, now displays a tooltip with additional info.
Actor Debugging Panel. A new toggleable panel displays detailed info about active actors, including location, vitals, status effects, and AI state. It’s been very useful for squashing AI bugs. This is a developer-only feature; it won’t be available to players.
Next week starts with closing out AI bugs, followed by validating combat calculations (now that Stat Modifiers are in place) and continuing to add sound effects. Also, I’m going to start on interactions between items and objects, such as scooping water into an empty vial from a fountain.
The first half of the week was solid – new feature development for release readiness, lots of bug fixes, and no scope creep. The rest of the week was consumed by messy rework that slowed progress and, I fear, will produce many new bugs. Automated testing should catch most of the bugs, however.
Stat Modifiers. Stat modifications caused by equipment, status effects, and terrain are now defined in lists instead of individual variables. This greatly simplifies the logic for calculating net stat values and displaying modifiers in an itemized fashion (which the Character Panel now does). Stat modifiers are either added or subtracted; multiplication and division aren’t used. This allows modifiers to be applied in any order.
Separate visual effects for unavailable drag drop slots. The UI dims drag drop slots to inform users when slots are unavailable. This was initially used to indicate slots that couldn’t receive the dragged object, for example dragging leather armor into a hotbar slot. When ability resource consumption was recently added, the same dimming functionality was used to indicate abilities that couldn’t be used due to inadequate resources. This introduced some bugs because the dimming logic now needed to handle combination scenarios. For instance, if an ability was dimmed because of inadequate resources, it would undim after being dragged because the drag drop logic would reset it. Using the same dimming effect for two different reasons also made the effect ambiguous. I addressed these issues by adding a separate dimming effect for slots with abilities that couldn’t be used and separating the dimming logic for the two scenarios. One visual effect darkens the slot and the other displays the slot contents in greyscale.
Many bug fixes
Next week, I’ll complete the remaining clean up for stat modifiers and add them to the character panel. Then, I need to get back to sound effects. The sound effects implementation has to be reworked somewhat. The varying sounds for physical material and item combinations are cumbersome to configure. There’s too much repetition; centralization is needed. I also want to create an in-game tool for understanding what the AI is doing. Much of the AI info is already logged but it’s difficult to wade through it.
It was a highly productive week. 100% of my time went into features and fixes that are required for the initial release (not always the case).
Status effect icons. Enemy status effect icons now appear above the enemy. Player status effects are now represented as icons as well, located next to the health bar.
Loading screen. New levels now display a loading screen, complete with a progress bar and text describing what is currently happening. At first, the progress bar was based on AsyncOperation.progress, which tracks the progress of an asynchronous scene load in Unity. The bar quickly got to 90% and then hung for around ten seconds. This delay was, of course, the procedural generation. Luckily, when I made the map generation visualizer last year, I had to reduce map generation into hierarchical, independent units of work – maps are generated by a series of processors, processors are composed of stages, stages are composed of substages, and substages are composed of tasks. The tasks are pre-generated and placed in a queue. This enables the map generation visualizer to display the result of one task at a time and interactively fast forward through substages, stages, and processors. It also makes it easy to calculate the progress percentage. I only had to make a slight modification to make it work with the loading screen progress bar, which was to wrap it in a coroutine and have it yield after each substage so that the UI could update the status.
Cell selection improvements. The area of effect is now highlighted and the cell indicator (not just the mouse cursor) is now shown.
15 sound effectsadded.
Next week I’ll take a break from new features to shorten the bug list. Saving and loading is broken again.
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.
This week, there were lots of miscellaneous improvements that stemmed from play testing. The summoned swarm of giant rats continued to be a source of both AI improvements and entertainment. I took great sadistic pleasure in hitting one of the rats to turn it against me and then watching its brethren surround it and rip it apart in my defense. The Conjurer class is going to be fun…
Summoned actors now turn hostile if attacked by the summoner.
Actors will now attack the closest target rather than an arbitrary target.
Actor items now transfer to the actor’s corpse. Previously, when an actor died, all of the actor’s items were added to the cell that the actor died on. Now, the items are contained in the actor’s corpse and can be taken by inspecting the corpse.
Random actor items can now be limited to specific item types. This enables actors to carry items that are more applicable to their descriptions. For example, a bandit may carry some gold and throwing knives, while a wizard may carry some scrolls and potions.
Actor actions. That actions that can be performed on an object or item are defined by that object or item. Previously, any actor could perform these actions. This didn’t make sense in some cases. For example, a blue slime isn’t intelligent enough to open a door (and doesn’t have hands of course!). Actions that an actor is capable of performing can now be defined.
Extended Tier system to actors. Each item has a Tier attribute that indicates the item’s power relative to other items. Tier is used during map generation to ensure that the items placed on the map are appropriate for the dungeon level. I extended Tiers to actors for the same reason. Plus, for actors that have randomly generated items, the possible items can now be limited to the actor’s Tier.
Gold is represented as an inventory item for non-player actors. The player’s gold is tracked and displayed in a counter rather than a physical item in the player’s inventory. Now that other actors can carry gold, gold needs to appear as an item that can be taken in the actor’s inventory. When gold is dragged into the player’s inventory, it is added to the player’s gold counter and removed from inventory.
Actor sprite support for map themes. Tiles, such as floors and walls, have had theming (caves, dungeons, etc.) from early on. Changing stairs from tiles to actors necessitated adding theme support for actors too. This was trickier than I anticipated. The implementation for tile themes wouldn’t work for actors. The solution was to set the sprite during actor instantiation, based on the theme of the cell where the actor was located. But, that didn’t work when I tested it. It took me a while to realize what should have been an obvious problem – actors were instantiated before their locations were set, so the instantiation method didn’t know which theme to apply. I added an optional parameter to the instantiation method for the theme. It’s a bad hack, but it works and its scope is limited (there are very few actors that will have a different sprite for each theme).
Tracked actors aren’t re-evaluated when an actor’s attitude toward the player changes
The camera doesn’t follow the player when the player was temporarily controlled by AI (such as under the effect of a Fear spell)
Rats can be summoned in walls
Rats are able to take items
Dragging from the hotbar causes the screen to scroll
Clicking the hotbar after scrolling the screen causes the player to move to the cell under the clicked hotbar slot
The default player click action for a dry fountain is attack
Mass Fear stopped working on the player
Next week, there will be more miscellaneous improvements as I work toward making Legend fully playable. I need to stop putting off the decision of how to limit spell and ability use. This decision is required to balance gameplay. Currently, spells and abilities can be used without limits, making the game far too easy.
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.
Resurrection ability particle effects. I mentioned last week that implementing Resurrection was simple. However, this week I hit a snag when I tried adding particle effects. The particle effects were triggered to start at the same time that the Resurrection effect itself started. This didn’t look right because the corpse would change to a living actor just as the particle effects were starting. I added a new Action Step for particle effects so that the particle effects could run in parallel with the Effect Action Step. Then I added a delay parameter to the Effect Action Step so that it could start half a second after the particle effects started. Now the actor appears to come back to life half-way through the particle effect emission and looks much better.
AI fixes. I fixed some problems that were rooted in my losing track of what the ActorTracker class is supposed to do. Actors are designed to only act when they need to, which is typically when they see another actor that they’re interested in. This is done for efficiency; the game would be too slow if every actor in the level acted on every turn. The ActorTracker class was created to allow an actor to continue acting after it can no longer see any actors that it’s interested in, enabling the actor to pursue another actor temporarily. At some point, I started using this class to permanently track actors that an actor was interested in, and built logic on top of this new use case. When the player’s actor was instantiated, every enemy in the level added the player to its ActorTracker. That caused every enemy to check if it could see the player at the start of each turn. This is an expensive check when the player is in the enemy’s sight range, because line of sight is used to confirm that the enemy can see the player. Aside from the performance issues, the shift in how ActorTracker was used broke AI. And, ironically, pursuing actors that were no longer visible, which was the original purpose of ActorTracker, didn’t even work anymore. To solve these problems, I went to my best tools for walking through complicated flows: graph paper and a pen. I diagrammed the AI steps, located the logic flaws, and fixed them in the code. I scaled ActorTracker back to its original purpose and added comments to help me remember its scope in the future.
Chest and corpse user experience design. One of the oldest items on my todo list is getting chests fully working. This has been held up by incomplete design. Until this week, I hadn’t determined how the player would interact with chests, and how that would translate to the user interface. I’ve been putting off making that determination until I played some other roguelikes and CRPG’s to get a sense of how other games handled this. Downloading, installing, and playing multiple games until I found a chest seemed too time-consuming, so I resorted to watching a bunch of Youtube videos to find examples. The latter was much more efficient.
Next week, I’ll get chests fully working and fix bugs along the way.
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…
Over the course of the week dev time shrunk as my work days got longer. Great progress was made on automated testing, and it’s already helping to find bugs. The biggest benefit I’ve gotten from automated testing has been with “whack-a-mole” issues, where I fix a bug in one spot and create a new bug in another spot.
Automated tests added for Abilities. The eight Abilities that have been created so far now have automated tests. Because Abilities are tied into many systems, they are a high risk area for bugs. I no longer have to test each Ability individually. This will be a major time saver, especially after the 100+ planned Abilities are all in the game.
Non-player actors can now use Abilities. Abilities are no longer limited to player classes. Any number of Abilities can be assigned to any actor. I still need to convert some enemy powers, such as transforming into a Vampire Bat, to proper Abilities, and incorporate Abilities into AI.
Next week, automated testing continues with Items. Also, I want to close out more bugs than I was able to this week. Finally, I want to put at least one new thing into the game (an item, enemy, or object).