Balleg Devlog Sept 2021

Flying limb sprites, appearing only when the player gets a game over.


Here’s the September gameplay footage. Like with August, there’s nothing terribly interesting to show this month. I spent most of it fixing various small issues with the new room-to-room transfer system. I’m happy to say that I have it mostly worked out now.


September Summary:

  • Wrote a basic GraphicsGale Gal-to-PNG converter and added it to the build system
  • Fixed crouching issues from August
  • Platforming code now allows offsetting of hitbox #1
  • Improved room-to-room transfers, and converted existing prototype levels from large, single maps to multiple smaller maps.
  • Rewrote how checkpoints and level start-points work
  • Level warp cheat improvements:
    • Support for starting from a specific site and start-point (ie not just from the beginning of the level)
    • Now works from the title screen, game over menu, and also via command line arguments
  • Added a brief frozen READY state on level start


GraphicsGale to PNG Converter

The Gale image format supports features that PNG does not, so naturally, when you save such files in GraphicsGale as PNG, data related to those features is silently dropped. This includes layers, frames, and some transparency and background settings. Whenever I would open a spritesheet or tileset, I would have to go into the layer/frame properties and enable transparency again, or mess with the alpha channel, which led to me making more mistakes.

I was encouraged to try writing a converter script in Python after finding Skarik’s OpenGaleFile C++ library. Fortunately, the file format is pretty straightforward. My script is able to import most Gale data, with the exception of 15bpp and 16bpp pixel data (which I never use.) It uses the zlib module to decompress blocks, ElementTree to parse the XML header, and Pillow to write out PNG files.

So I have the script integrated into the build system now. The only issue is that when building the project from scratch (deleting all final files and regenerating them), Tiled may occasionally notice that certain tileset PNG files briefly cease to exist. This makes a prompt appear, requesting the user to locate the missing files. Restarting Tiled works around this.

I was just about to put this script on GitHub, warts and all, but I realized at the last minute that the per-line pixel data contains zero-byte padding when image dimensions are not multiples of 4. I didn’t notice this (and it doesn’t affect my project) because all of my images are power-of-two dimensions at least 256×256 in size. Oops. I’ll have to rewrite how it fetches colors from data blocks to account for that.


Crouching Issue

So I figured out the problem here. It actually had nothing to do with the new crouching logic in the player actor, but with the ‘solidAtXY()’ function call used to detect tilemapped obstacles at points above and around the player’s head. This function would only look at the first room that the player overlaps. My test site had two rooms, with one overlaid on top of the other, and the first room that solidAtXY() would select for probing was mostly empty. Making it check every overlapping room fixed the issues.


Hitbox #1 offsetting

Paying off some technical debt, I went through the platforming code and added the offsets for hitbox #1 that I skipped when initially adding multiple hitbox support to the game. It needs to add the offsets when calculating the actor’s center position, and subtract the offsets when assigning a new position relative to other positions (such as placing the actor next to a wall.) I may have missed a few spots, but they will likely only become apparent with time (probably with the player inexplicably teleporting when touching some terrain type that I neglected to test…)

The platforming code still uses hitbox #1 only for determining environment collisions, and this is unlikely to change, at least for this project.


Site Transfers

The frozen room-to-room transfer sequence was only barely working when I blogged about it at the end of August. Since then, I’ve gotten it stable, and have sped it up so that each transfer event only takes about 1-2 seconds.

I rebuilt the four prototype levels to work with the new transfer system. Before this, every proto level was one singular map file. Now, each of these levels has about six to ten sub-maps. This is unquestionably harder to deal with from a file management perspective, and I can no longer get an easy bird’s-eye-view by zooming out in Tiled. On the other hand, it’s easier to consider each piece of a level individually now, and it’s also easier to resize these chunks without interfering with other parts.

To accommodate future screen-shake effects, every site has a one-tile-wide fringe / perimeter of additional tiles beyond the camera bounds. The fringes at site-to-site junctions have to match, or else the differences will “pop in” during a site transfer.

This is a debug visualizer which shows the locations of each site in the current world. The rectangle with a white outline is the current active site. The massive amounts of overlap are caused by either a need to have additional terrain out of view, or poor cropping of the map in Tiled.


Here’s the same room in Tiled. As you can see, there is plenty of empty space along the top, left and right sides which could be cropped out.

Checkpoints and Start-Points

The checkpoint system needed a rewrite to work with the new world model and site-transfer system. Originally, each level had a start-point marker, and a set of checkpoint markers. Every room in a world can now have a number of start-points, numbered from 1 upwards, and any of them can be assigned as a checkpoint or start-of-level spawn-point for the player. This helps in two areas:

  • Debugging, since now every room in the game can have an arbitrary starting position, even if they aren’t tied to a checkpoint or the actual beginning of a level.
  • In the future, this should assist with setting up branching and merging paths through the game. For example, if level 5 can be entered from two different preceding level 4s, they can both have different starting points.

Given multiple start points, we need to identify which start-point to select when entering a level, and perhaps apply other modifications as well. Merging paths would also require multiple entry points into levels. This information is packed into level header structures which are stored in an array, and the array indexes are referenced internally instead of the user-facing level name or number. When a level is completed, if no header index is specified, then the game will select the next header in the sequence.


Similar to the frozen transfer state, the READY state freezes the action for about a second upon first entering a level or when respawning from a checkpoint. This lets the user get their bearings for a moment before the action starts.


Project Outlook

Yeah, not a whole lot to write about this month. I put a lot of time into polishing transfers, particularly how the player is shepherded between locations. There were many small, yet highly visible issues related to the player’s animation state, placement and velocity which I had to chase down. So even though it was technically working before September, it still took about two thirds of the month to bring it up to an acceptable level.

I also slacked off a bit as I was kind of grumpy about the new transfer system. Non-frozen seamless travel from area to area felt like it was important, and I really didn’t want to give up on it. In practice, though, it wasn’t really compatible with the project’s gameplay. Switching to managed, frozen transfers meant deleting a lot of prior work and admitting that it wasn’t sufficient regardless of the effort put in. Luckily, it wasn’t a total loss, and I was able to salvage and adapt some of the data structures and procedures. The resulting code is a bit of a chimera, and overly complex for what is needed, but whatever, it works.

I don’t have any solid plans for October and I need to do some review tonight. I’ll write another devlog hopefully on or before the 31st.



Codebase Issue Tickets: 45 (-3)
Total LOC: 117727 (+2774)
Core LOC: 35874 (+250)
Estimated Play-through Time: 6:12.22*

(*) August had no time estimate because a change to the map format rendered the prototype levels unplayable. Going by some rough calculations, about 42 seconds of this month’s estimate were taken up by the frozen READY state and room transfer animations. The recently-implemented ‘shot recall’ feature allows for spamming most of the prototype bosses in ways that weren’t possible before, so I’m guessing that made up for some time.






Since you made it to the end, here are some unused starting animations that I had drawn in anticipation of a frozen READY state. They aren’t used for a few reasons:

  • The READY state is shorter than most of these animations
  • In Bolero 2, animations with variable frame-times are a hassle to implement, and I didn’t feel up to the task
  • They’re likely to piss off users who keep dying and respawning at checkpoints

So instead, to draw the user’s eye, the player just blinks a few times during READY.