Skip to content

June Retrospective

Last updated on 25/06/2019

June is over, or will be shortly. It's time to look back on the months project. At the outset, let me say I'm pretty happy with the outcome. You may recall, but let me remind you, that , "In June, I plan on running through a whole feature for the game, the turn based combat system. The dirty secret is that the combat system is mostly built: I implemented the whole thing in python earlier this year to do balancing via Monte Carlo simulations. There’s still a lot to figure out when bolting the system onto the game itself.."

Famous last words, those. Actually porting over the combat equations was pretty trivial. The scripting language inside of gamemaker2 is pretty much source compatible with python. There's not a lot of difficult translation going on. Even things like class and dictionary structure are pretty easy to bring across. That said, some things that you'd expect to be trivial are surprisingly difficult. For example, sorting a list by a ranking function isn't supported. It's not a huge problem to have to look up a sorting algorithm and port it over, but it's surprising that you would have to.

Beyond enriching the weirdly focused standard library to include things you're used to programming languages having, you might be tempted to think that specifying the to-hit and damage rolls is like 90% of the work. I thought myself beyond such hubris and said "well, maybe that's about half of the work". No, 10%, tops. For me at least, the difficult work in this sprint was orchestrating the interactions between elements on the screen to drive those to-hit rolls and damage displays. Swinging the sword was easy, but letting the user decide to take a swing, pick a target, and communicating those things to the right place was a pain.

Whole lot of pointing going on

Consider the image above. The pointer below, the one that points at the combat action list, causes the pointer on the combat field, the target select, to be created. Only after a target is selected do we want to roll the combat dice. It turns out that skills work similarly, but have an intermediate step where a skill is selected, same with items. Even so, the combat target selector has the same general function: pick some battler on the field, and then do something.

Do something takes the form of script_execute here, and it's how I'd been using my pointers since I created them for menu navigation. Early on, pointers only ever needed to know about the script they were supposed to execute when activated, and they passed in as a single argument the target they were pointing at. If I'd had partial function application available, I could have lived with that for the combat as well. I'd have just applied one or two arguments at every step of the combat process, returning a function that would be executed later.

Gamemaker2's scripting language doesn't let you do that, so I had to come up with an alternative. There are a few likely candidates:

  • Store the partial function arguments in the battle scene control object, as actingCharacter, selectedAction, selectedSkill, etc
  • Do that, but in global scope rather than in the scope of a battle scene object
  • supply pointers with a supplemental argument array and change the doAction signature to take the pointed-at object and an array of supplemental args.

I went with that first bullet at first, and you can make that work out just fine. However, I also need to be able to apply skills and items outside of combat, say from the main menu. I thought having two paths to skill execution was bad because it wouldn't be maintainable. You could work around that by decoupling those storage variables and putting them in global scope, as the second bullet suggests. That approach is also bad because it pollutes the global name space. Ultimately I settled on the third option, which was just enriching pointers to make them more powerful.

Death animations via alpha blending

Another big step forward this sprint was roughing in an animation system for combat. There are animations on every attack and damage roll, there are animations when an enemy combatant dies, and I added in wipes for getting to and from combat. Now, again, when I say I added these things, I mean I added the ur-class for combat animations and wipes and then implemented a single example. I'm looking to make sure that the animations play well with the other system interactions. I'll worry about pretty and variety at a later date.

White Block Wipe

The animations are basic, but they work. I'm just a little bit unhappy with how they work, because they control things which aren't just drawing. The combat damage animation controls the end of the combat turn. After the animation is a certain part complete, combat stops waiting on the animation and resumes. Specifically, at the point that the damage type animation ends and the number bounce is in full swing, combat resumes. I'm trying to reduce the amount of idle time there is in any part of the game, and sitting through the tail end of an animation when I could be casting a spell or something seems like a good place to win back some time for the player.

That said, I really don't like the animation controlling game state with a callback like that. It seems like a violation of separation of concerns. The alternative is polling the state of the animation from within the combat control object. You don't have to relinquish any control to the animation if you go down that avenue. However, I found wipes way harder to do that way. I definitely want to change the room over at a single point on a wipe: when the wipe covers the screen. Polling might miss the exact point I want to do the changeover, and I think the only reasonable way to capture it, aside from pausing the wipe at it's zenith, is to do the screen transitions inside of the wipe by callback.

There is balm is Bassead

To get the item portion of combat working, I had to rough in a player inventory system. I knew I needed one, but I hadn't designed it or built it yet. I had been semi-intentionally stalling this particular event because I didn't know how the inventory was going to interact with the world. Once I got working on the combat system, some things clarified. I didn't want an inventory mixed of equipment and consumable items, as in FFIV.

There's some cool richness that comes from using equipped items for effects in the final fantasy series, but two things stopped me from going that way: those effects are almost always available on consumable items anyway, smaller inventories mean the player can select faster. So, waiting until the combat system was being put in changed how I implemented the inventory system, and I think in a positive way. If I'd just sat down and implemented the inventory system as a pile of all items at first, I would have probably used that directly here. Instead I've built separate inventories for equipment and consumables, and I think I'm happy with it.

Noticing outstanding changes good, no contextual actions bad!

I found myself struggling with my tools more than usual this month. To put not too fine a point on it, GM2 has a git plugin, and that plugin is a trap. It provides *just* enough functionality to make you think you can use it, but not enough to actually be useful. It's ok, and let me be clear it is just ok, for pushing code back and forth between two machines using a git repo as an intermediary. However, selecting files to commit, diffing files, and reverting files in the repo are all difficult to do or totally unsupported. Branching is right out. I don't know about other people, but I use branching, merging, and reverting pretty frequently in most of my work. They feel noticeably missing in the plugin. Going forward, I'm probably going to use an external tool for repo management, but that feels weird when you're doing the rest of your work in a modern IDE.

Published inGame DevelopmentSoftware Development