Finally, prefabs are working properly! Here are some examples:
In these examples, the layout is as follows:
Generate the outdoors area, depending on the biome
Generate the indoors area as a dungeon using a set of prefab rooms
Generate a boss area, which is a prefab zone
For the prefab rooms, I made a few quickly, but still they can demonstrate what’s possible. The rooms can use explicit tilesets or the “dungeon default” whatever that is. They support explicit placement of creatures, objects or decals/decoration (e.g. cobwebs or carpet). They might allow additional procedurally placed sparse features (encounters, objects) or not: for example in a pretty packed boss room, we might not want to randomly spawn a bookshelf. Same apply to “zones” (via sharing much of the code) which are larger versions of rooms and they can be instantiated only once in a level, for example 3 zones being “outdoors”, “dungeon” and “boss lair”. Some zone type that I still want to add is “open” zones, for example an outdoors clearing with some ruins, treasures and monsters, that is well integrated with the rest of the map.
Prefab rooms can be configured so some are rarer than others, and some can be spawned just once or multiple times in the same level. What’s not implemented yet is context-specific room choices: for example, if we have a “crypt” map of sorts, we want lots of grave-related areas and rooms, but we shouldn’t find those in a wizard’s tower. Next step is to add such info to zones/rooms so that the created maps look more cohesive and can showcase certain styles.
Here’s a video showing a dungeon run like the above, only problem is that I forgot the boss room door open and it was empty by the time I got there…
A couple things this week. The prefab work is still going slowly, due to some days off, some IRL deadlines (it’s deadline season!) and finding a new thing to “distract” myself, which is the website/blog.
I plan to change the name of the game, as the current name reflects some outdated game design goals that I’m no longer after, ergo “irrelevant”. A new story-framework has been crafted (read here if you fancy) and a new name needs to be developed to reflect that change in direction. So, the complication is that I’ve bought a domain name which expires this July, and instead of renewing to something else that is similarly prone to whatever the future has in store, I thought I’d use Github Pages instead, which allows such changes more effortlessly. Github Pages and WordPress do not seem to mix well, so after a bit of research I decided to go with Jekyll, which is a popular static site generator. This has the added bonus that everything looks more like code, rather than the very GUI-driven interface of WordPress which I’m not a fan of. Since I don’t know much about web development, this transition takes a bit of time, and importing the blog into Jekyll is non-trivial as I have to fix embeds, links, images etc, and I’ve got a couple of months to do that before the other domain dies.
Regarding prefabs, I did get room prefabs to work (see figure above), where you can see the room placed everywhere in the dungeon, and sparse features like bookshelves or encounters are placed appropriately in there. But there are some more things to be done, because it’s never that easy. Room prefabs should also be able to include content besides just the shape. For example you could make a library room and place the bookshelves yourself, rather than rely on the placement algorithm. So, to support both procedural placement and fixed placement, is slightly tricky.
So, the prefab loading work ended up being slower than projected, for a few reasons, one of them is IRL work and things, but the other important factor is just Bad Code that needs to be refactored:
I was trying to serialize 2D arrays of layout info in a variety of ways at the same time, and it backfired. 2D Arrays are stored by the game in some optimized form, but I can’t easily create that from from python (where the prefab editor is implemented in). So, eventually I had to create different classes and map from one to the other
The super-flexible json level specification is a double-edge sword, as it’s difficult to generate the json from the C# game state, especially if we start including prefabs etc. This is one of the reasons why I’m showing the same dungeons over and over; I’m choosing between a select few configurations . So now I’m slowly fixing that aspect, but I’ll possibly need some long uninterrupted hours to do that, which might be hard to come by.
Nevertheless, I did a few steps towards that direction, so it’s possible to put the prefab as a zone in a dungeon easily, but the dungeon configuration is a bit hard-coded. What’s missing is also the ability to use the prefab as a room type in a rooms-and-corridors generator. I suspect that part will be ready next week, barring any surprise IRL work.
Some backend work this week, to support item generator effects in level objects. An example is a bookshelf. Bookshelves are interactive objects that may or may not contain a scroll or a tome. Initially will appear full of books, but will become empty when searched. If an item is found, it’s automatically transferred to the inventory.
An additional fine point for bookshelves is that they are generated as a single feature in the native plugin that does the dungeon generation and placement work, whereas the C# side generates a cluster of bookshelves centered around the calculated feature position. So, some extra work to make things passable, etc. This is useful as we can do several nifty things if the native plugin generator works in terms of features, and the C# side can instantiate each feature into multiple similar entities. Another example would be a boss enemy with a number of minions, but that will come later on.
Another requirement for this work is to specify some sort of 2-dimensional enum for item types and subtypes, so we can tell the item generator system that we want a “tome” or a “scroll” object that has less-equal item level than some specified.
Another random fix was the AI of ranged attackers, where they would not take into account that obstacles might be in the way of the fired arrow. Well, now they do, and move into a position that provides a clearer shot.
I’ve also started having a look at the prefab support in the dungeon generation, written last summer, as I have to write some tool to generate prefabs to use with generators.
More miscellaneous work this week, under the general guise of “emergent gameplay”
Adjusted how diagonal movement blocking works, for QoL reasons. Before, if you wanted to move diagonally, both tangent direction had to be unblocked. So that, when moving, you never see the player going over any obstacle. I really, really disliked how that worked in practice, as 8-directional movement stops being fluid. Now, diagonal movement is only blocked when both tangent directions are blocked. This works far, far better and the movement is now very smooth again, without surprise-blocks that don’t seem to make sense.
Added support for thrown bombs (well, general category of thrown things that have a particular effect on landing). So that there are more AoE options for non-spellcasters, for example some elemental AoE for fighters, or some smoke clouds for stealth users, etc. I also added support for delayed-blast effects, so that you can have situations where you throw a delayed blast bomb, then it gets moved somehow (e.g. via trap, kick or a kinetic spell), and then it explodes. Russian roulette gameplay could then be to start throwing delayed blast bombs on teleport traps 😀
Realised the importance of a GUI for split and merge stacks, wrote some basic placeholder GUI for it, but also realised that I could not fit that somewhere reasonable based on the existing item GUI that I have… To elaborate: the current item GUI logic, which I was proud of, is to always have a pair of containers (player & ground, or player & chest, or player & other container), and you multi-select things in one container, multi-select things in the other container, and do a transfer. To help things, I also had some toggles for all/none selection per container. That’s all great, until we need to split a stack, as that just can’t happen with the multi-select which is binary in nature. So, back to drawing board for how the GUI will work for inventory transfers.
Added some initial support for destructible objects and durability. Again, this feature creep is essential, as breaking things is an integral part of roguelike life, as the remains of broken barrels, jars and doors from various games would all attest to. This can creep further into weapon durability, but I’m not going down that route, yet, although the deceptive simplicity of integrating that is tempting.
The major thing this week was making better Diablo 2-style auras but not rendering them dynamically; instead, I made a simple editor tool (based on this) that bakes auras as texture animation, bundling everything up as a texture atlas and using that. Made the in-game aura rendering code far far easier, than having separate shaders for each. Since each texture atlas supports up to 2048 animation slices (due to texture array limitations), for 64 frames per animation I can store 32 different auras, which I think is more than enough.
Another feature that I’m in the process of adding (added, bugged, debugging now) is freezing levels when you leave. First of all, freezing is important, otherwise if all the entities from all levels play, well that just doesn’t scale. So, to avoid a boring “freeze immediately” after you leave a level, at the moment I allow for 10 standard actions (a minute in “game time”), which allows entities to suffer repeated effects, move a bit, etc.
Some refactoring and some basic feature support this week, all towards bosses and elite enemies.
On the refactoring side, I ditched even more ScriptableObjects in favour of my own JSON-powered flyweight classes. The changed classes were some name generators and associated data.
On the same topic, I’ve made a “boss name generator” for boss creatures, which creates names such as “Satha the sadistic” and “Iveri the power-hungry”. Basically, the first name will eventually be derived using a language that’s prevailing in the region where the boss will be found, followed by some villainous adjective.
Fixed a visual bug where some visual effects like burning, freezing etc would be seen before the projectiles that caused them landed
Added support for creature traits and made a few. Traits are a collection of effects applied to a creature, like providing resistances, increased base health, etc. What this plans to be is what Heroes of Might & Magic did: every creature was differentiated from others based on its stats and, most importantly, traits, which gave unique flavour to units. The difficult part is to come up with interesting traits of course, befitting the genre and gameplay.
Added support for auras as an effect (applicable in traits). An aura is an effect that is centered on an entity and gets applied repeatedly to all entities (either friendly, or not, or anyone) within a particular radius. For example a boss with a damage aura could cause its enemies to take damage repeatedly while in radius, where as a boss with a damage-enhancing aura would cause all its allies/minions to get damage-enhancing bonuses while in radius.
Elite creatures get a level bonus plus a corresponding trait for elite creatures (e.g. max health, better damage, faster, etc), whereas boss creatures get a bigger level bonus plus a corresponding trait (or two?) for boss creatures (some aura, etc). I want to have something like Diablo 2’s unique monster bonuses
Elite creatures have a different visual indicator to boss creatures: same effect, different colour.
Early draft version (aka terrible looking) for visuals to represent auras. They are rendered correctly as a ground layer, alongside decals, but they need to be spiced up. I’ll be looking of course at Diablo 2 for reference, but the rest of the work for this might be art-related or making lots of pretty shaders, which is best reserved for an “I-don’t-want-to-code-architecture” day
Spawned creatures can be normal, elite or boss.
Here are the Diablo 2 auras to get inspired for any further visual work on this (source 1, source 2):
Quite a bit of refactoring this week, and some bug fixing as well. Net results: level-scalable effects and more/better status effects.
Effects now are level-scalable, and they also include a source entity. Basically, I have this Effect class everywhere in the gameplay code, which previously was just a generic Effect.Execute( Entity target). While super-general, it’s also super-problematic when I want to have more info. Long story short, I realised that changing that to Effect.Execute( Entity target, Entity source, float effectLevel) is absolutely great and useful for anywhere effects are used in the codebase. So, I can have a damage effect that scales with level, e.g. 5-30 damage at level 3, and add 1-6 to the min/max for each level thereafter. Example: instead of having a lot of healing potion variations, I could even get away with just a single healing potion with level scaling. There can be still weak/strong potion variations by changing the constant/scaling values (if a weak potion cures 1d8+2d4level, a strong potion could cure 2d8+4d4level). Level scaling is optional, so many effects are of course not scaled like that. By knowing who the source is, it’s easy to track the source entity to award XP on death, among other uses. This can/should chain, for example if a player creates a trap that applies a burning effect to a creature, that dies after 3 turns of being burnt, the user should get the XP.
Status effects got a big update, to support more complex things. I’ve added burning and freezing to test a few things:
Can now attach visual effects to the entity with that status, e.g. a freezing entity will appear blueish, whereas a burning entity will be surrounding in flames.
Can now attach effects, permanent or temporary, to status effects. For example burning has a permanent effect where, for a few turns, we repeatedly apply a permanent hit points modification (fire damage), so basically damage over time. Freezing has a temporary effect where it reduces the total speed by 50% for its duration. Status effects can optionally stack, so if I decide later to have a status effect, such as poison, that can stack, then getting poisoned twice might be twice as bad. Status effects can be associated to verbal, somatic or visual components, where as active abilities might require any combination of verbal (e.g. spell/warcry), somatic (anything that requires body movement) or visual (anything that requires a clear view of a target) component.
I’m not going to go the route of status effects cancelling each other and changing to other things (e.g. burning + frozen cancelling each other out, etc). If necessary, it can be explained anyway as your feet are burning while your arms are encased in ice 🙂
Secret things are uncovered only when they are in view, and only uncover them by the player.
I also fixed a bug with passing StructuredBuffers to Unity shaders. Unity did not want to send a compute buffer to gpu correctly when in the shader I use StructuredBuffer and in Unity I specify the buffer with a size of 4,6,8 uints and a few more. Yes, totally reasonable, but it should warn me maybe that shader and ComputeBuffer do not match?? Ugh, spent 2-3 hours on that at least.
Several “random” bits of work again this week, as my TODO list is a hydra. Solve one entry, and poof more appear. Not as bugs, but more like a dependency tree where you try to resolve a node and the fog of war dissipates and reveals more things that are not in place 😀
Framework for rendering “entity effects”, graphical effects attached to (and dependent on) a particular entity. Two examples are shown in the video above: a boss aura like Dungeonmans, and an early version of a burning effect.
Some debugging GUI for increasing attributes/skills
Added potions and tomes that increase attributes and skills
Allow per-creature blood spatters (on injury) and blood pools (on death). So that skeletons, ghosts and oozes don’t bleed.
Refactoring my configuration database to not have dependencies at all, as it got cyclic. So, I have for example the configuration for a creature race that depends on the configuration for an effect. The moment I started adding more effects, one of the effect types might depend on a creature configuration, so the dependency chain got cyclic, so I had to get rid of that. Now I’m doing a two-pass approach: first I load all the configurations in their most basic form and register their name (“allocate” them so to speak). Then in the second pass, I read everything normally, and order does not matter as I’ve allocated all the configurations by name in advance.
Machinery/planning for how a creature gets to learn active abilities (spells, active skills, special attack moves, etc):
Monsters have a mapping of ability-to-level-requirement. E.g. a monster wizard of level 3 would know firebolt, same creature level 7 would know fireball.
Equipping particular weapon categories might allow for special moves. E.g. equipping polearms would give a skill (that costs energy) which attacks from 2 tiles away
Progression in mastery levels in particular skills can unlock some active abilities.
Consumable items (e.g. book)
(tbd) Special dungeon objects
(tbd) Shops/trainers/people in cities
Try to be a bit more gamey and less simulationist with items and effects. Equipment will not have on-use effects, whereas potions should generally have different effects than, say, scrolls or items that can be used (wands? etc). Potions and scrolls should be different, beyond “this can get wet” for example
Mainly refactoring work this week, with the purpose of unifying the handling of abilities by the player or the AI. Concrete outcomes of this refactoring process:
New WIP ability, usable by both player and AI: fireball.
We need to select a target position
It has an area of effect
It’s effect is “fire damage”
It spawns a particle system, initialised with the positions of affected tiles
Fireball itself is a projectile, so if it hits an obstacle, it will stop (and explode)
We can setup the skill to be able to shoot multiple ones, homing ones, or both
AI can use the fireball skill too, by evaluating the “score” of each possible tile, determined by the possibility to hit ourselves (-5 score), allies (-2 score each) and enemies (1 score each). A score greater than 1 means “good enough”
AI applies a recency-based weight when choosing a skill to execute. So, a no-cooldown skill that was just executed will have 75% weighting which goes back to 100% after several standard actions. This is to avoid repetition of maximally scoring abilities, and make the AI hopefully feel less robotic.
New WIP ability: magic missile.
Like fireball, but does damage to a single tile
Number of projectiles can be greater than one, and for each projectile we can select a different target. Homing missiles still need a clear path to a destination
Reworked item transfer interface a bit, so that when transferring items between player/container or player/ground, we can select a number of items from container, a number of items from the player, and then click a button to make the transaction. So that with a standard action we can move e.g. 2 items to the container and pick 3 items up.
Cleared up input/keys a bit (still far, far from final though). Now we have the following categories of actions:
Ability-based: Fireball, melee attack, ranged attack, use item, etc. These typically need to be configured with some gui (which item? which direction? etc). Most of these would not appear in an input configuration file, as we have just too many. Instead, they’ll be mapped to…
Hotkeys: so that I can map any ability-based actions to hotkeys. Whereas the essential actions are limited (move in a direction, primary action, etc), most abilities have to be mapped via hotkeys.
Actions that do not require a gui, e.g. quicksave, quickload, teleport, look.
Press-and-hold actions, e.g. highlighting of creatures or objects, minimap view