Implementing Gol: Code Reuse For The Win!

Well, dear reader, all our hard work together is paying dividends. I don’t think there was a single problem implementing Gol that wasn’t already solved somewhere else. It was just about putting existing pieces together. Regardless, let’s go over what those pieces were.


Before we continue, I want to apologize for the lack of September blog entries. I admit I got carried away coding through September instead of updating the blog. I’ve been pretty good about bouncing ideas off you until now, but I was in The Zone, where the code just flowed from my fingertips into the computer. This does mean Eggerworld is progressing quicker and better than I anticipated, so hopefully that makes up for it.


Back to Gol! Yes, I’ve run right past the Gol.


Gol is one of the original five monsters introduced in Eggerland Mystery back in 1985, along with Snakey, Medusa, Skull, and Alma. Gols sleep until the last heart frame is collected, at which point they wake up facing one of the four cardinal directions, ready to breathe fire when Lolo walks by.


What a smile, they just look so happy to be doing their job



Gol’s sleeping and waking behavior is remarkably similar to Skull’s. This means we’ve already figured out how to implement it. Skull watches the state of the chest every update, and when the chest opens, Skull wakes up. Gol can use the same strategy as Skull, and in fact Gol can just copy-paste Skull’s wake-up code.


Quick technical aside: the chest's code counts the remaining heart frames every update to determine when to open. The opening and waking mechanics could be more efficient if they used the Observer pattern instead. Specifically, the heart frames could just call out to the chest, Gols, and Skulls the moment the last heart frame is collected. Event-based solutions like this are usually preferable to polling, for many reasons but for performance most of all. However, it would take more infrastructure, more code, and possibly more interdependency, with only a trivial benefit in this particular case. During active development, I often err towards simplicity and readability, especially since modern computers can handle that trade-off. We won't use an Observer now, but optimization will almost certainly happen later, so I've taken these notes on potential performance optimizations.


It's like they're cats, and the chest makes a can-opening sound when the lid pops up



Back to our main task! Let’s say our Gols are now awake. An awoken Gol’s attack is triggered when Lolo enters Gol’s line of sight. Sound familiar? Medusa already performs line of sight checks using the Region class, formerly called Rectangle. Gol now uses this same Lolo-detecting logic, with a Region that spans from Gol’s centermost point to the edge of the puzzle.


Hmm... actually, that's a bit of a lie, because we define this Region to span the length of the entire puzzle, just in case. Most times, this spans beyond the edge of the puzzle, which doesn't hurt anything per se. It does guarantee that Lolo can never be too far away to enter Gol’s threat region even if Gol gets moved around. Medusa already does this too, during its "naive" calculation, so there's no new precedent here. It’s close enough to Medusa’s existing logic to qualify as reuse once again.


The yellow rectangle represents Gol's "threat region", a separate region that moves along with Gol itself


Gol can be egged with magic and then pushed, so we can't just calculate Gol's threat region once and be done. Instead, we added just a bit of code to make sure that Gol keeps its threat region up to date whenever Gol itself gets moved. The shift() method is already part of the Region class, which allows that Region to be shifted in any desired direction, so Gol calls out to that. For the rest, Gol's movement strategy is nearly identical to Snakey's because they both just sit there, waiting to get egged. Gol's collision and eggification once more came down to boring ol’ reuse.


Gol shoots fireballs, though, isn’t that big news? Kind of. It’s definitely the biggest news to come out of Gol’s implementation. Still, Gol’s fireball behaves awfully similar to Lolo’s magic shots. I copy-pasted the MagicShot code and renamed it to GolFireball, and that was 90% of the work. Each Gol can only have one projectile on-screen at a time, just like Lolo, and Gol fireballs use a state machine that includes flying, splashing, and being dead, just like magic shots.


There are two notable differences between fireballs and magic shots. First, fireballs don’t use the magic shot’s weird collision properties. Instead, they have the same collision properties as Alma and Skull, so I specifically copied Alma’s collision details into the fireball code. Second, fireballs move at a different speed from magic shots. Thanks to some foresight, that’s a trivial change, literally changing one number to represent projectile speed. It’s all reuse, but at least in this case it was more of a chimera, a smashed-up combination of parts. Gol all in all is a Frankenstein of Snakey, Medusa, Alma, Skull, and Magic Shots. That’s… something?


Eggerworld Gols, according to the code. We also tried some sounds for Gol, but every sound kept turning into "please end my torment".



Before we call it for this blog entry, there's one more notable topic; Eggerland Gols behave differently from Adventures of Lolo Gols. In The Adventures of Lolo games, a Gol will spew fire as soon as any part of Lolo is in front of it, and that fireball travels fast (two pixels per update, or twice as fast as Lolo). In Eggerland titles like Departure to Creation or Revival! Eggerland, Gol will not fire until Lolo is perfectly in the same column or row as Gol. Eggerland fireballs also only move one pixel per game update, exactly as fast as Lolo.


This same puzzle appears in both Eggerland: Departure to Creation (left) and Adventures of Lolo (right) so a comparison was easy



This raises the question of which Gol behavior is “canon”. We decided that both variants deserve to exist, so we will start with Adventure Gols to match the style in Return to Adventure, but we will eventually implement both kinds and make sure they are cosmetically different enough to tell apart. You might be thinking, “ah, Eggerland Gols qualify as an interesting change, right?”. Good guess, but actually, no; any differences with how Eggerland Gols are implemented can still be accounted for in Medusa's logic. Medusa uses multiple different kinds of sight checks to "freak out" and to attack, so Eggerland Gol’s style of sight check is still just reuse of Medusa's code.


Sorry Gol, ya basic. But that's ideal for software development. Like magic in the Harry Potter universe, it's best when it isn't flashy. Don’t worry though, Rocky and Moby will be a nice one-two punch of complexity to make up for it.



Comments

Popular posts from this blog

Demo is here!

Who Is This For?