Refactoring My Own Code, Part II - Legacy Code Debt


Ugh.

I talked about this in a post a while back, but this time it's even worse; the code that is being rewritten is integrated into every part of the game. I am referring to the game's inventory, spell, and feat system.  I'll give you a taste of what horrors this game has been running on since development began:









Macros? What are those? Constructors and structs? No clue what those are. Apparently the only way to implement this is was with nested global arrays all containing global variables. Want to change anything? Add new items? Good luck. I've known about this tech debt for a long time now, but only recently have I figured out exactly how it should change, and what to do to make it happen. The solution lies with constructors and structs, the former being something I've never used in Gamemaker before and the latter only having been used in the game's save system thus far.

The only thing I can give myself is that when the game's development first began, I was a lot less skilled in programming overall. I hadn't coded in Gamemaker in over five years, and I had not yet begun my job as a software developer. I've learned a *lot* over the last year and a half both on and off the job, and this especially applies to software design principles. Flashing back to October 2022 when development first began, I almost had to learn from the ground up again. Getting an inventory and dialogue system working was something that my game needed, but I was still unsure of how to implement them. As I discussed in a past post, this is not the first time I've worked on this game. Part of getting my 2018 prototype up and running meant having a dialogue and inventory system up and running early on. What was the most logical choice at the time? Scouring the internet for anything that looked like what I had in mind, of course.

The issue, of course, is that the code from that demo was terrible. I will fully fess up and admit that I found the implementation of those two features from elsewhere on the internet - where exactly I have long since forgotten. I only recall that they were posted freely for anybody to use. I made a few small changes here and there (such as adding in different item properties), but by and large it wasn't my original code. But it worked, and that was all that 2018 and 2022 me cared about. Tech debt was future Bynary's problem. But future me is here - and by waiting this long to make these needed changes, I have made the process so much more painful for myself.


The dialogue system was totally rewritten a few months ago - very little of the original code exists, and the functionality was greatly expanded to make it a general, all-purpose system for both dialogue and cutscenes. If you want to have interactivity such as with a menu or branched dialogue, that's also possible as well. None of this was attainable with my coding ability a year and a half ago, and certainly not in 2018. The result of these changes is that adding in new dialogue and NPCs is so easy now - and there are no more bugs resulting from modifying copy/pasted code incorrectly now! I was also able to consolidate several objects containing duplicated code, which has made life so much easier for me since then.

One issue with the way the inventory system was constructed is that Gamemaker didn't have structs and constructors at the time. Most people used DS lists and maps, but you're funny if you think I had any clue how they worked. Arrays were the one thing I did know, and by god I was determined to make the system work with them! What you saw is the end result of somebody who had little coding talent, no concept of scope and didn't know what a macro or enum was (or that they were compiled at runtime and global in scope). It worked...but at a high technical cost. Another downside of using somebody else's code is that I didn't actually understand how it worked. I couldn't wrap my head around how the nested arrays actually worked, I only know that it did what I wanted it to do. The cost of that decision is that I couldn't modify the system if I wanted to, nor could I extend its functionality. I've since written a lot of extra (original!) functionality around that code, but the base always remained the same.

As I mentioned in the game's roadmap to completion of the demo, one of the features that will be included in the future is the Forge. I also mentioned that the game's current code state is going to prevent implementation of that feature, and that these changes need to be made in order to clear the path. Now that v0.040 has been released and the demo is nearly feature-complete, now is as good a time as ever to do this job and clean up my own mess. The spell system has been reworked completely to use a new constructor-based system; nearly all of the global variables have been replaced with macros since they were just constants, and the Feat system has also received some similar updates as well. The result is that the code that calls these scripts is noticeably easier to read and modify.

What the Inventory system needs is the same type of update - elimination of all the global variables and replacing them with macros, elimination of the nested array and replacing it with just a single global array containing the player's inventory, and writing a few additional helper scripts to make the Forge functionality easier to implement. Items will be re-done so that their values are contained within a single struct rather than multiple entries in a 2-D array. The actual logic of the game won't change so much, but the details of implementation will all change dramatically. There are so many scripts that must be changed, and there will be many days of testing and debugging. I am not looking forward to any of this, but it must be done.

-------------

Ultimately, these are the type of challenges that make me a better programmer. I learned several useful things starting this process, and it has become more apparent than ever the value of working out system design details BEFORE implementation begins, and not after. I will never again make these mistakes - and I know from a few ideas swirling around in my head that what I learn here will make prototyping future game ideas so much easier and faster. There are still many changes yet that will need to be made, but from what I understand this is the last major refactoring project that needed to be undertaken - what remaining code there is either isn't worth changing or is small potatoes by comparison. It continues to be true that recognizing how something needs to be refactored isn't always apparent. It's easy to say that code is poorly written, but your first question should be: how would you change it and why? If you can't answer that, then you've got some more work to do before you're ready to start changing things.

One area that is ripe for changes are how sprite animations and NPC emotions are handled - I'll need to write some shaders and get a bit creative with the code, but I think there's a way to eliminate quite a few of the sprites that had to be made in order to achieve the kind of emotive responses NPCs give when you talk to them. There's not a lot of coding involved, but it's something that's also wading into new territory for me - shaders aren't something I have much experience with. But that's future Bynary's problem.

Refactoring - it's an incremental process.

Get Battle Tower Royale

Leave a comment

Log in with itch.io to leave a comment.