Rainy Day – Day 7, Success!

I managed to complete my 7DRL #18. Rainy Day!

I reached the final day with a semi-working game, glad to at least had invested quite a bit in advance in content generation, but with a lot of tweaking to do.

The first thing I tackled was making sure there was some kind of progression both in the difficulty of the levels but also in the development of the player character. This including moving a lot of the monster stats and doing some playtesting to see how it felt, and also incorporating a lot more skills from Rodney that the player could acquire by using “Wallemite Chambers” found within the levels.

The “active” melee skills I included were: Charge, Assault, Corner, Counter, and Rage. These demanded a lot of work to integrate them from the old Rodney code (which is actually the basis for JSRL but is super outdated now). Then I also had the “stats upgrade” skills for HP, Attack, Sight Range, and Carry capacity; each one required refactoring stuff at some level in the combat code or the UI.

At one point in the day, I decided to stop adding skills and disabled the rest of the Rodney list. However, I added some new skills to improve the ranged attack as well.
I also invested some time trying to fix the “overflow” of the messages textbox; I imported the one I had for Emerald Woods, but it didn’t work that well here mainly because Emerald Woods has no combat and thus has much fewer concurrent messages. Ideally, I wanted to make it so that a [More] prompt was displayed on overflow, and the game would stop until the player had read and pressed Space, but for this to work, it seems some more stuff needs to be streamlined into an async flow and time was just not enough.

Another important thing I added was populating the items in the levels. Funnily there was a game a couple of years ago (Heroes of Noresskia) where I completely forgot about healing! so it was probably impossible to win. In the end, I went a bit overboard with this, adding too many healing items.

I also added another facet of inventory management with ammunition for ranged attacks, so you have to balance your inventory between healing items and ammo.

About 30 minutes before the deadline, I got a pack of sound effects and music from QuietGecko. I hesitated on integrating them since I still had to work a bit in the balance of the game, but in the end, decided they were important enough to set the mood of the game. I managed to integrate just one of the three tracks he provided (the one for the Wallemite Reactors), as well as a single SFX for walking, and the plasma shots for ranged attacks.

The final 5 minutes were me frantically trying to address the balancing issues because the game was still too hard! so I nerfed enemies to 25% of their power, and buffed the healing items to 200%. A lot of testing is still required but at least it proved to be enough to make it playable.

Rainy Day – Day 6

Day 6 I added ranged combat which included refactoring a bit of the game turn flow so that actions didn’t have to have immediate results (so that I could add the ranged attack animations). It was easier than expected due to JavaScript’s async/await combo.

Planned Refactor
What I actually did 😛

Also added healing items, again with some refactoring over the JSRL code to move item usage execution into the player class and out of the ItemCategories. This allowed at least having a gameplay flow of losing and recovering hit points, with the associated inventory management (i.e. a game)

Finally, I added some support for audio, so that QuietGecko could start adding some of his magic.

RoboLike postmortem 3

This, finally, is the proper “what went right, what went wrong, and anything else you learned” postmortem. You’ll probably need to read the design devlog entry to make much sense of it.

What Went Right

Working environment

Mostly I do my hobby dev on the Linux (technically a dual boot machine, but Windows is very much the secondary OS) machine that is really set up for media streaming and a console-like gaming environment, This means its got terrible ergonomics for long coding sessions. So I did the vast majority of the work during jam week on a MacBook set up in a more programming-friendly environment, The reason I don’t use this machine normally is that it’s on the elderly side and just can’t cope with the demands of Unity 3D, which is where most of my game dev has been done. But 2D, turn based in Godot? Yeah, it coped with that just fine.

Also, I’ve got a proper hi-fi in that room, and getting up to change CDs rather than having a continuous stream is great for encouraging drink/wrist/posture breaks.

Choice of engine

The more I use Godot, the more I appreciated the integrated development experience it provides compared with Unity, which always feels like a disjoint collection of program bundled into one interface. Except the code editor, which explicitly isn’t.

I’m also starting to get my head around GDScript properly, this being the first project of any size I’ve used Godot for.. Having a custom language was one of the things that initially put me off Godot, but experience with Unity now makes me appreciate having a language so tightly coupled to the game engine. The Pythonicity has been both a blessing (Python is my default language) and a curse. As I said in an earlier entry, no matter what anyone says, GDScript is only superficially like Python, and I would keep instinctively reaching for constructions like comprehensions and enumerators only to find them missing. And forgetting about key language features like signals.

And creating the UI is so much easier than in Unity. A benefit of the Godot Editor being a Godot application. It feels like the kind of UI design I was doing 15 years ago (yeah, it really is that long since I did any non-web UI work) rather than the 30 years ago (Xaw, if you’re curious), which is how Unity makes me feel.

Combat balance

OK, I didn’t get a chance to thoroughly test it, but it feels right at level 1. Enemies can kill you easily, but not one-shot you, and you can kill them with a bit of luck and cunning. And once you’ve managed to steal a ranged weapon, it becomes a fairer fight. I threw in a last minute bonus to melee weapons (compared to ranged weapons using the same damage calculation) to make it an interesting decision whether to shoot an enemy from a distance or get in close and personal, but it’s not clear how this (or my choices of random functions for combat outcome) have played out.

Outcome

I submitted a game which could, in theory, be played through to an end following the designed progression through levels.

What Didn’t Go Well

(Nothing really went wrong)

Outcome

The submitted game is horrifically buggy, and is missing at least 50% of the features and graphics it was designed to have. As the week progressed, I realised I’d have to descope more and more features to stand a chance of submitting anything at all playable, and the extra sprites I was planning on creating had to be dropped completely. To go into more detail:

Missing sprites

My intention had been to have unique visuals for each weapon type — 8 in total, or possibly 7 with the EMP giving no warning sign. I had 3 pre-prepared for ranged weapons, and wound up duplicating those across the other 3 ranged weapons (including EMP) just to give some kind of clear sign of who’s going to shoot at you. The two explicit melee weapons are completely missing.

I had vague plans that the colour cast of the eye strip would indicate armour class, which would require two more sets of robots in different colour casts, plus a little @ decal for the player. (Decals on enemies could indicate level.) Dropping the player sprite into a created scene for the first time immediately convinced me this wasn’t going to work: 48x48px just isn’t enough space to have any kind of clear decal, and without that you can’t distinguish the player from unarmoured enemies. So I abandoned all that (and, conveniently, the work involved) in favour of yellow=player, green=enemy.

I had slightly more concrete plans to have yet another set of robot sprites with alternatives to the “walking treads” to indicate the drive class, hence how fast you could expect an enemy to move before they did so. But I always recognised this was going to be a bit of a stretch, and don’t really regret it’s lack.

It’s like you need these graphical clues to tell you about an enemy (or yourself) — it’s all there in the Status/Scan panels.

Choice of tileset

I still like the look of the tileset I chose, but didn’t realise until I’d pretty much committed to it that it doesn’t provide complete coverage if you’re using Godot’s 3×3 tilemap mask. Which means you sometimes (often) get these brownish “default” walls where there’s nothing for the autotile system to place. I suppose that’s another set of missing sprites, but not ones I was planning on creating myself.

Also, there’s no floor tile in this set. Instead, the floor is a 72x72px from the same asset pack as the robot tiled as a layer underneath. This meant that I had to flip the level generation code, which in the original carves floor out of roof, to add walls to a blank canvas.

Fog of war

The first thing that was in my plans when I started to get dropped because of time pressure was fog of war. Apart from the graphical aspect to it, there were code considerations to be made. I’d implemented panning the view independently of player movement really early on, and while that wouldn’t have to be directly affected it would mean the cursor could be placed outside the range of the player’s knowledge and used to gather supposedly hidden information. I think it’s justifiable in the game scenario: these levels aren’t unknown to the player, so having a full map is fair, and it would make sense for robots to have some kind of low-level automatic detection of each others’ presence.

Minimap

Any minimap code could have been made a lot easier without fog of war, but even so it was a nice-to-have that didn’t make the cut. Being able to pan all over the real map makes it somewhat redundant in a turn-based game.

Level map

I was also planning on having an effectively side-on view of all levels (like Quazatron’s) to show what state each of them was in. This is just over the edge of nice-to-have into useful features, as there’s no other way to look up this information.

Hybrid enemies

They probably could have gone in without much effort, but I think they’re really a nice-to-have, so I just tweaked the level placement of the fixed classes and quietly forgot about them.

Grappling

This is what Quazatron calls the combat mode where you enter the mini-game (which I’d descoped before the start of the jam) which allows you to defeat enemies and steal their parts. I had everything in place, including the random outcome code based on the critical-win code I’d mocked up in the design phase, but had no idea how to map a win onto damage. (An unconvincing grapple win does a lot of damage, and may not leave you with anything worth salvaging. On the other hand, a grapple loss won’t destroy you, but will leave you damaged.) I was running out of time, and had other things to implement (specifically, the enemy AI), so grappling got dropped. Fortunately, I’d (unintentionally) designed the code in such a way that made this really easy.

EMP (sort of)

The EMP was intended to be a directionless ranged weapon (in Quazatron, it hits everything on the level it’s fired on). I never got around to the special code for it, so it’s just a directional weapon with a radius splash damage.

Implicit damage

I didn’t consciously drop the implicit damage (or any kind of hunger clock). It just slipped my mind until it was too late.

Enemy AI

This was the last thing to go in, started with less than 24 hours to go. So it’s there, but isn’t what I was planning on it being. Specifically, I’d intended the AI having some sort of “threat level” of the player, and either ignoring them, running away, attacking on sight, or deliberately hunting them out depending on that assessment of threat and the class of enemy concerned. I lost a lot of time in those last 24 hours debugging issues with the turn mechanism and detecting whether the player was within ranged weapon range, and in the end was lucky to squeeze out something a bit more interesting than “keep going forwards until you hit an obstacle, then pick a random new direction”. (There’s a 25% they’ll take a turn at a junction on a room level, a 10% chance they’ll randomly change direction in the middle of an open space, and a 28% chance they’ll do absolutely nothing.) Which means they present a challenge not by being pro-actively hostile, but in being utterly unpredictable.

Extra equipment

Pretty much all the extra equipment I had designed only made sense in the context of features which in the end got descoped: masquerading stats doesn’t help the player if there’s no threat assessment, always have the advantage of initiative in a grapple is meaningless without grappling combat, and detect all enemies on a level happened automatically when fog of war went away.

This had the nice side effect of meaning the player sprite could be the (cuter, rounder) “unequipped” robot and the enemies the “horned” equipped version. Robots still have an extra equipment list in their stats, and chose a sprite accordingly, so this was achieved by giving enemies a piece of extra equipment called “none”.

Save/Load

I’d implemented a save/load/restart/quit UI about half way through, but only the “quit” really worked at that point. I always intended revisiting, but didn’t get back to it until there was 30 minutes to the deadline. I made a last-ditch effort to at least save the game seed, but even that failed and with no time to debug it, only the working “quit” was left.

To be fair, I’d never quite worked out whether I was going to do a proper roguelike no-backtracking save, or have a single save but letting you quit without saving, so you could bail out of hairy situations and resume at the last time you saved.

Polish

Apart from the font in the info panels (Nova mono), the entire UI is out-of-the-box Godot. One of these days I’ll learn how to use Godot’s UI theming.

What I’m Not Sure About

These are really things where my lack of genuine familiarity the genre have left me taking sometimes wild guesses as to what would be playable, and I don’t know how good that guess has been.

Sizing

The original plan had been for each level to be a 30×30 square, which would mean the full width would be visible at 1080p, assuming no information panel.  Given an information panel, that wasn’t that important, and given I was aiming for a browser game as primary target, and the default itch.io resolution for embedded games is 640x360px, I decided to target 720p instead (figuring the scaling down from that to 640×360 would be better than from 1080p). I still started with 30×30 tiles, but the BSP Tree code for room levels was struggling to produce anything interesting, so I doubled it up to 60×60. Which looked great, but when I finally got to something where you could move the player around and discover things I got the feeling that it was much too big. So it went back down to 30×30 and the level generation and made some heavy adjustments to the BSP Tree parameters. Although that produced nice looking levels, it would sometimes hang trying to place items due to the proximity rules I’d imposed, so those needed adjusting too. I think the result is OK.

As for the number of levels, I realised that 56 levels with no save mechanism was a bit crazy, so I made it adjustable. Unfortunately, I didn’t have time to adjust the enemy distribution, so you don’t get to see all the classes unless you play with at least 4 levels. I honestly don’t know what would make for a good game if you did have a save feature.

Combat balance

As I said, I never had a chance to give it a real test below level 1 (I did a bit on level 2 before adding the AI, which made all the enemies sitting ducks, and may have made the rail gun over-powered). And grappling of course never got tried. The thing that’s been a really wild guess is how to turn the original maximum-hit-only design code into something random. I went with the normal-distribution probability from GDScript’s random number generator, but while the mean value to choose in each case was pretty obvious, I’d really no idea what to use for deviation, and no time to experiment.

The removal of grappling, where the logic stat is key, rendered the weapons which specifically target logic (logic probe and EMP) slightly less useful. Possibly. Similarly, the lack of implicit damage to the chassis, or anything which drains power, makes attacks targeting those specifically less significant. In fact, those three critical stats become pretty much interchangeable, apart from those specific weapon vulnerabilities.

Unlocking levels

The original design was to require all robots on a level to be deactivated to unlock the downwards lifts. Fairly early on, I decided it would make more sense in-world, and possibly from a game-play perspective, to turn the “charging points” into “access points” that had to be visited and “reset” to unlock the lifts. And since this was while fog of war and the minimap were still on the table, maybe unlock those. I’m unsure whether this was the right thing to do, particular given how I changed …

The endgame

I went in knowing the victory condition had to be “deactivate all robots” and in roguelike tradition return to the surface. But I had no in-world reason for the return, and no in-game challenge. But as I was writing the “All robots deactivated” message, it suddenly struck me that putting a turn timer on return would provide a challenge, and an in-world explanation would be that this is how long it would take the systems to reboot, after which all robots in the facility would be wiped. Although writing that now I realise it doesn’t make sense, as wouldn’t that solve the corrupt robot problem? Anyway, if victory condition is “deactivate all robots and return to the start in a tight time limit” and you can descend without clearing a level, you could just leave level 1 occupied once you’d upgraded a bit, clear everything else, make a leisurely return and one-shot everything left for an easy last phase.

Graphics

Quite apart from the missing sprites and tiles, I’m not sure how well the graphics I do have work. Obviously, the artwork isn’t mine, but the choice to use it was. My feeling is that for the look I wanted I should have gone for 72x72px tiles/sprites  at 1080p.

What I Learned

Be really careful with version control when doing game dev on two different machines. You may think it would help with sync, but the problem is if you’ve got things which can only be reliably edited by the game editor but are stored as text files which git thinks is just code (in this case, tscn files), you can get into a real mess if your local copies diverge and you try and merge them. I think in future each machine will get a branch of its own.

FSMs are a great idea, and I should implement them properly for character (player and AI) control, rather than doing half a job with a pile of enums and states being changed by simple assignment all over the place.

GDScript is not Python. It has its own strengths and weaknesses in comparison.

I’ve still not got the hang of estimating project size.

There’s a good game in here. I don’t know whether it’s from finishing off this turn-based version with all the planned features, or turning it into the action game I’d originally been thinking of for “Quazatron with procgen”. Or maybe, in between, a SuperHot (which is so a genre now).

Devoting a week to writing to a consequence-free deadline, for fun, is a really enjoyable. Especially when you’re in lockdown so don’t have to feel guilty about not going out to do something less work-like. Or is that just me?

RoboLike postmortem 2

I want to write a technical postmortem of what went well and what didn’t, but realise a lot of that’s not going to make sense to anyone reading it without some understanding of what the design goals I was aiming for, and spectacularly falling short of. So this is that missing piece.

The Idea

The tagline is “Quazatron/Paradroid as a roguelike”, but really Quazatron was the version I played and loved as a child. So the gameplay was intended to be very Quazatron-like, ie you upgrade by taking parts from defeated enemies rather than taking them over wholesale, while the graphics would by top-down 2D (in true roguelike fashion) like Paradroid (rather than Quazatron’s isometric 3D). OK, there was a much earlier design in my head which would have been 3D, but 7DRL came along and changed my direction on that. And also introduced the idea of making it turn based, which seemed like it should be a good fit.

The basic concept is that the systems in an automated mining facility have become corrupt, and in order to perform a clean reboot of the central systems all the robots in the facility need to be “forcibly deactivated” otherwise they’ll just recorrupt the central system when they reconnect. The only way to get in undetected is by remote control of a very basic robot, but once inside you’ll be able to scavenge parts from defeated enemies to upgrade.

Ironically, the mini-game of the originals which allows you to take over/disable an enemy was the first thing I decided to descope, before even starting the jam. I was unconvinced about how it could be made properly roguelike (despite not being a player of the genre, I have strong views about What Counts, and being turn-based is an absolute requirement in my book), and realised that it was a lot of development effort I could save by just implementing the effect as another weighted random event. And on the back of that decision, I decided that the player should be able to scavenge from any defeated enemy, not just those defeated in this way. (Although, of course, if you’ve shot an enemy to death it’s going to have fewer usable parts.)

The other big difference in making it a roguelike was to have procedural generated levels. The overall Quazatron design of multiple levels of an underground city with tougher opponents further down is a very natural fit, it’s just that it uses static level design. Which is not something I’m into. (As I joked in the first entry in this series, I don’t know much about roguelikes. I’m just here for the procedural level generation. (And possibly the permadeath.))

Level Generation

If I hadn’t been working within a constrained time frame, I would probably have written my own level generation code from scratch. As it was, I decided pulling in somebody else’s map-generation GDScript was within the bounds of the jam rules, especially as I was intending to tweak it. There are two level generation algorithms there: BSP Tree to create rooms, and cellular automata to create caves. The presence of both of these led to my decision to make the setting a mining complex, with the idea that as a level is mined out it is turned into rooms (why robots need rooms, I don’t know) using the BSP tree algorithm while working levels are more cave like using cellular automata.

Following the “try and get a 7 in” maxim of 7DRL, I decided to create a tree structure 7 levels deep, with 7 cave levels at bottom, with intermediate levels either being a room set with one cave and one room level below, or a cave level with just another cave level below. This makes for 28 levels total, which seemed like a reasonable number.

Sizing

Which brings me to the first point in which I needed to feel my way around a lack of roguelike experience. From the ascension stories of friends (the ones who instilled the very determined ideas about what makes a roguelike), 28 seemed like a reasonable number of levels. But what size should each level be? Here, I went way back to the origins of the genre, and thought about a non-scrolling 80×24 single-character-tile map fitting on terminal. However, I had no intention of doing this character based — that’s one trad roguelike rule I disagree with. I grew up with 8-bit gaming, and we put up with lousy graphics because we didn’t have better options. We can do better now, and should do so. Or, to put it another way, I really don’t get the retro aesthetic, either in obviously pixelated art (let alone continuing to use glyphs rather than sprites) or 8-bit music. Which means using a nice tile set, with a scrolling view, and any level size I wanted.

Tileset and other assets

Probably my biggest limitation when it comes to game creation is a serious lack of skill in creating graphical assets, whether 3D or 2D. Quite often, what I write is determined by finding an asset I really like. So in the run-up to 7DRL, i poked around at the 2D art assets available from itch.io to look for anything that would fit into my vague plans.

What I came up with was a suitably cavernous tileset and a nice robot as 48×48 sprites. This seemed to me to hit a nice point between detailed-enough graphics and being able to fit a decent number of tiles on screen (22 full tiles vertically at 1080p, which is my default resolution for working to). I then gave myself a crash-course in aseprite for tweaking and created a couple of variants of the robot (krita also got used for its global fill functionality) and some weapons. More weapons and robot variants were in the plan, but I stopped because it was starting to feel like I was doing too much work on the game in advance.

Combat System

One thing I had no qualms about preparing in advance was the combat system, because that was completely a legitimate design phase. Rather than trying to produce an exact copy of the inspiration, I designed something heavily influenced by it. Critical stats are power, chassis and logic (CPU in the original) — if any of these drop to zero, you’re deactivated. You also have stats for strength (how effective your weapon is), protection (how effective your armour is) and speed (how many moves you can make in a turn). “You” here applies to all the robots in the game — enemies and player alike. On top of that, you have a drive class, which effectively determines whether you’ve got enough power to move at maximum speed, and an armour class which determines whether your armour stands any chance against the weapon being used against you.

Different weapons, as well as being more or less countered by different armour classes, inflict different types of damage at different chances. One general type inflicts damage to all stats with a probability of attackers strength to defenders protection, modified by armour; another inflicts a random amount of damage (determined by the attackers strength modified by defenders armour) to a number of stats (determined by the attackers strength) starting with the lowest stat (or the highest, depending on weapon type) and working through in order. Other weapons specifically target logic (logic probe), chassis and armour (rail gun) or logic and power (EMP). Some types of ranged weapons have splash damage: lasers keep going and also inflict damage to enemies behind the initial target, plasma barrages and ion cannons also inflict damage to any enemies within a one tile (including diagonals) of the target, and EMPs are non-directional and affect any robot within range.

I took this design and coded up some Python to play off robots with different stats (character/monster classes) without a random element, but assuming a maximum hit from the attacker, chucked the results into a spread sheet and went to work tweaking stats and formula to try and achieve balance. What I was after was a level 0 (initial player state) robot being immune to being one-shotted by any level 1 enemy, and in turn being able to take any of them down (eventually), checking that the same applied for level 1 v level 2, and then being confident it would apply from thereon up (although not actually planning to go beyond level 3). I then figured out how to arrange the 12 classes by 3 levels over the 7 levels of the game in increasing order of threat.

Robots can also have one of a range of special components fitted as additional equipment, which let them do things like masquerade their true stats, always have the advantage of initiative in mini-game-substitute combat, and provide protection against immobilisation (speed dropping to 0 but not deactivated).

One added wrinkle: if the player is allowed to mix-and-match components from defeated enemies, why shouldn’t the enemy robots do the same? Given the concept, the idea was that the robots at the deepest levels had become corrupt first and started scavenging from each other to create random hybrids, at which point the systems sent their toughest units down to deal with them, before they got corrupted too, and then the central systems, explaining why you’ll find dangerous military hardware at the lower levels of a mining operation.

Implicit Damage

As well as taking explicit damage in combat, a robot can take implicit damage to its chassis if its weight (drive + armour) exceeds the chassis’s capacity (with adjustment to stop it spiralling out of control) and to its drive if its power is insufficient. Power can be restored at charging points scattered across the level.

How It Started

  • So at the start of the week (13:00 Saturday, local time) I had lined up:
  • the tile assets, including charging point and doors for going up and down (at the start of the week I wasn’t sure whether these were going to be lifts or teleports, but almost immediately settled on lifts because the tree structure of the levels just didn’t make sense if you could teleport)
  • the original robot asset
  • three variants I’d created: one without the “horns” on the head, meant to indicate a robot without any special equipment, which would be used for the player, and duplicates of these with green tinted eye strips (which, thanks to krita, added a green cast to nearby body work) which would be used for enemies
  • dead form and idle animations for all four robot variants
  • two weapon animations (idle and firing) for two-and-a-half weapon classes (plasma barrage is just plasma beam doubled up)
  • level generation code
  • Python code for deterministic combat

How It Went

… see next entry

RoboLike postmortem 1

I’ve been blogging my progress on the game devlog rather than here, but I’m planning on doing a series of postmortems which I’ll duplicate. This first one is a reflection on my take on the project from a personal point of view.

Well, I submitted. It was kind of touch-and-go whether I would or not (in the end I did two releases in the last five minutes as I realised I’d left my cheat codes in). it’s missing a lot of features I originally intended to include, and is horrifically buggy. In particular, it has a nasty habit of hanging at random points, and I was half a day short of being able to write a savefile system. The last-half-hour effort to at least save the RNG seed failed, so even that’s missing. On the other hand, you can theoretically progress through the game to its win condition as intended. Although now I come to think of it, I didn’t even manage to test that. Just the lose conditions.

It’s a flaw I recognise in myself that I will dream up great creative projects which look perfectly achievable on paper, but in reality turn out to be much more work and way more involved than originally recognised. I’ve talked about how game jams have given me a fresh take on this in the devlog of my main project, and that practice in being positive about descoping really helped me here. I reckon RoboLike needs the same amount of effort again to implement the features I think are missing, and I’m OK with that. At some point I will come back and finish it (as much as any piece of software is ever finished) and that doesn’t feel daunting.

The big question that needs to be asked, then, is did I underestimate the amount of work in my original design (bearing in mind that even before the start of the jam I’d descoped some of the ideas from my original original design), or did I overestimate my abilities? If you’d asked me at any point after the start of the first full day, when I posted that the project had a “20% chance of playability, 0% chance of quality, 100% chance of learning stuff” I would have said it was a bit of both. But now I’m not in the midst of it, and looking at the other submissions, I think the truth is that it’s almost all down to my lack of knowledge (and maybe an overestimation of how quickly I can learn).

To break that down a bit:

  1. I’ve been doing game dev for barely two years (although non-game dev for a lot longer)
  2. Almost all of that time I’ve been working on 3D games in Unity, while RoboLike is a 2D game in Godot. So that’s an engine and a language I’m still very much learning, and a completely new approach to graphics to learn. (Well, I did do some 2D graphics as a child, but that was back in 8-bit days.)
  3. On top of that, it’s a genre I don’t play (just admire the tales of others).

Which means I’ve spent a lot of time learning how to do things rather than actually doing them. Stuff like:

  • the value of FSMs (which I only appreciated in an 11th-hour effort to debug enemies that wouldn’t stay dead)
  • how to use the Godot RNG for procgen
  • why my sprites weren’t aligning
  • how to integrate a turn-based system with a real-time game engine

I reckon half my time was spent solving problems that wouldn’t have come up if I wasn’t trying to figure it out on the fly. Combine that with it needing “the same amount of effort again” and that suggests I pretty much nailed the sizing. It just needed someone who wasn’t having a “100% learning experience”.

And even then, there’s one critical thing I still don’t know:

Is the damn thing balanced?

Rainy Day – Day 5

One first concern was how to make combat more interesting. While I still got to add ranged combat, I thought I could spice up melee combat a bit by adding some of the skills from Rodney (inherited from DrashRL and also permeated the design of some of the classes of Ananias). I added the Slash and Backslash skills (pass thru the enemies to damage them), and am still debating which others to include, and tie them to the player’s progression.

Also, a small detail that may greatly improve the player’s experience, I made it so the walls and stairs of the level will be remembered. This is especially helpful when you are on your way back to the surface.

In order to have a winnable game and a more complete structure, I placed the four Willemite reactors and the flow to destroy them by placing an explosive at their core and returning to the surface. I also changed how the maps are populated, to spread some enemies and include a boss party at the end of each one.

Finally, worked in level metadata and enemies definitions, lots of spreadsheet work.

Rainy Day – Day 4

I spent some good time thinking on “Innovation”: What was going to set this entry apart from others and bring something new (even if tiny) to the genre? so far it was heading to just being a traditional roguelike with a fancy monochrome display (which is probably hardly an innovation as that’s how these games were played in the early ’80s).

Reading a bit on it, I remembered one way to see it is by creating something by mixing different ideas. Just what I did (with half-baked implementations) for my first 7DRLs I guess.

At first, I thought I could create a roguelike with strong western RPG elements, like, taking ADOM and stretching it to an Ultima VI level of (hand-made) character and quests design, with its own lore and towns with a unique flavor. I played a bit of Jeff Lait’s Malachite Dreams, which I always had on my mind was like an Ultima RPG at least for the exploration aspects, and found it a bit empty so plenty of possibilities to extend on the idea. Luckily, that threw a preemptive OverAmbitiousScope Exception, especially with just 3 days of dev left, so I discarded the idea.

Then I wondered, what games have I been playing lately that I could use as a source of inspiration? luckily, my list is short with just two games I played seriously in 2020: Horizon Chase and the Final Fantasy VII Remake. Then I remembered I actually had the idea for 2020’s challenge to create a game based on the upcoming remake, thinking about riding the wave of increased attention. (Actually, in 2020 there was an entry based on FF7).

Seeing how it was compatible with what I had so far, I decided to go with it.

But enough talk, here’s a list of things I did in Day 4. Have at you!

  • Added enemies, including adding them to the level generator which implied refactoring it so that it received the metadata for the enemies to include.
  • Added simple AI for the enemies. This required refactoring JSRL so that the player has a “Being” which is actually part of the level and represents his character into the game. I leveraged a lot of code from OpenArthurianX6 (simplified since there are no animations).
  • Added an initial version of melee combat.

Rainy Day – Day 3

Following the idea of not having a clear plan and instead just having fun, I continued exploring what I wanted to do with the game.

I decided to integrate and adapt the Stygian Abyss level generator (tweaking it for the fixed screen 80×22 levels and the monochrome visuals of the game). Funnily, one of the big things I missed from our Stygian Abyss 2015 7DRL entry was an auto-map or better means to prevent people from getting lost; the combination of the overhead view and the fixed levels seems to work great for that (perhaps centering a game around the notion of how easy it is to get lost isn’t that much of a good idea after all).

Another thing I decided to do was adding a hand-crafted overworld, linking the main dungeon and 2 caves. I initially did it as a I considered integrating part of the random plot generator from Heroes of Noresskia (my not-very-exciting entry for 2019’s challenge). I’m still considering it but for now I think it wouldn’t add much. Of course, I also grabbed inspiration from classic ADoM’s Drakalor Chain (but it’s a valley, similar to my hometown’s).

Right now there are two big things I need to define: first off is the theme, including the bestiary. I don’t think I will be able to come up with a full roster of unique interesting races so I’ll probably just grab the one from Ananias. That brings the second point: the differentiating features of the game which I still don’t have a clear idea about… maybe it won’t be combat focused so the question of the bestiary would be less important.

Dungeon Tetris

With most of the actual game basics squared away, including dungeon generation, menus, UI and events for game-over and whatnot, its finally tetris time!

Last night I implemented the basics, using the SRS rotation algorithm, although I haven’t implemented wall kicks yet and not sure if I will. Wall kicks are where if you attempt a rotation, and there are blocking tiles (for example the side wall) then a few nearby places are checked, i.e. it’s ultimately moved away from the wall so that it can fit.

Anyway here we are! Note the floor tiles change when the blocks move, this is because the floor is just the one underlying terrain type for each base color, and the renderer picks the tile to display depending on the location of the cell within the level. This makes the checkerboard pattern super easy, but also makes it weird when you try to move it around.

The blocks all otherwise move as I would expect, and can even carry enemies/the player. I’ll have to add some checks to detect flying enemies and ignore them. Also if I want tetris blocks to carry torch pillars (I do) then I’ll need to move the light entities too. This might be hard because for wall torches, then light entity is actually in the tile in-front of the wall, so this might result in the light from torches being carried away while the torch stays in place 😛 I’ll figure it out anyway.

Another important thing is that with level generation, I need to ensure connectivity – that is the ability to actually reach the exit. With a tetris field, this isn’t guaranteed, the player might lock themselves away from a critical door. To fix this, I implemented a spawn strategy that creates tetronimoes in places they don’t quite fit, if there’s nowhere else for them to go. Firstly it looks for the least blocking space, then it tries to place as high as possible.

Rainy Day – Day 2

I felt pretty lost at the end of day 1 and was pretty unsure of what to do. Only at the end of the day I decided I was just going to have fun with this.

  • Changes in the display including using a IBM VGA-like font, and making it bigger.
  • Allow using computer to log into roguenet or play SpelunkyRL
  • Disable memory.
  • Allow watching random videos in a TV. (Using Evgenii Petrov’s Theme Generator)
  • Started work in the Mines level generator.
  • Started work in parallel dimension 1.
Create your website with WordPress.com
Get started