Sunday Show Out - 12/21/2025
All I wanted to do was code a simple combat system.
But then I noticed a weird bug.
That bug became a coding hydra whose heads would not let me rest until they were all removed.
And once that hydra was slain, its final head sprouted into a new, unconsidered hydra that was somehow even more mean-spirited.

What did I do?
When Zel hooks an enemy and reels it in, the enemy is in a temporary stun state thanks to a timer attached to the Enemy node. When the timer elapses, the enemy is supposed to either attack (if Zel is in range) or continue its patrol (if Zel is out of range). However, the enemy wasn't staying stunned: it was resuming the chase state on contact with Zel. That was relatively easy to fix: it was a mix-up between velocity/speed on my part.
Now the enemy was staying stunned! But they weren't entering the correct state when the stun timer timed out. Instead, they were entering either the attack or patrol state based on whether Zel was in their chase radius before they were hooked. The culprit was an if statement in the function that handles what happens when Zel enters the enemy's chase radius: it kept the enemy's position (relative to Zel) from updating and forced the timeout function to enter the state before the enemy was hooked.
Great! That's all sorted. Except. . .
The enemy starts moving again when it contacts Zel. The stun timer doesn't stop (print statements are the lifeblood of debugging). The function that determines what to do when Zel enters the chase radius was determining what to do if the enemy was in a stunned state too early: it was being resolved upon contact. So, I had to change that logic and, barring all catastrophe, it works. The hook system works!
I'm still relatively new to coding, but I understand that there's rarely a situation where one bugfix makes everything work. It's an iterative process. Fixing one issue often reveals one (or two, or fifty) issues that were being obfuscated by the original bug. It can be slow progress, but it's still progress, and I'll count that as a victory.
Would the enemy attack state be as difficult to code?
It turns out, nope! That was relatively straight-forward, including a cooldown timer so Zel wasn't immediately murdered before she could attack/flee. I need to set a few variables as export variables so I can edit them in the visual inspector, but overall, things are looking better.
One-sided combat wouldn't be particularly fair, or fun, so I needed to give Zel her own attack state. After she hooks an enemy and reels it in, she can perform a melee attack (right now, I'm thinking steel-toed boots or a knife) and knock it back. I'd like to expand the state later to include a combo system but, for right now, I decided to focus on just the melee attack.
The tutorials/resources I've found online suggest making a hurtbox that's persistent, but only active during specific frames of an attack animation. However, I didn't have an animated sprite for Zel. I have this:

I supposed now was as good a time as any to dig into both Aseprite and the Aseprite Wizard addon for Godot. After about a half-hour of work, I had this, animated and imported into Godot:

Now, Zel walked around the world and animated properly. At this point, I had:
- enemies that patrolled, chased, and attacked;
- a hook system that drew enemies in and temporarily stunned them;
- a simple melee attack that could hurt Zel; and
- a naïve hope that I would finally be able to code the attack state which was the only thing I wanted to do this week and it's Saturday as I'm typing this and the devlog posts tomorrow and please let this be it I'm so tired.
And then, after days of doing everything I needed to do before I code the attack state, I finally did it. It was almost anticlimactic: apart from some bugs with the attack animations (turn off looping in Godot's SpriteFrames resource for attack animations) and Zel's hitbox not recognizing the enemy's hurtbox (make sure no other CollisionShape2Ds can interact with the hurtbox by creating two different enemy collision layers/masks), I was able to code a simple attack state. Finally, Zel could kill an enemy, an enemy could kill Zel, and I had a very rudimentary, but working, combat system.

What did I learn?
There are few straight-forward tasks in coding, and that appears to be particularly true for game development. To create a working combat system, I had to code an attack state for Zel. To do that, I needed to (well, not needed, but it was helpful) create a sprite sheet for Zel with idle, movement, and attack in the four cardinal directions. I also had to suss out and fix bugs in the enemy's states that interacted with Zel. And then, finally, I had to fix the bugs in Zel's attack. And that's just for a simple system. I need to further iterate to add features such as cooldown, knockback, and combos, not to mention making sure the sprites interact well together and that the whole system has timing that feels good.
As far as debugging goes, I found that print statements are a lifesaver. Most of my bugs occurred when Zel/enemies didn't enter, or leave, the correct state. Print statements allowed me to figure out the source of bugs. And most bugs were either a result of timing issues or my still-novice knowledge of Godot's signal system.
I was frustrated along the way this week, but I didn't give up. I knew what my goal was and, keeping that in mind, I made progress every day towards that goal. It was a small goal, relative to the overall game, and that helped me realize it was achievable. I think most of us fare better when we have small goals.
What's next?
Christmas is coming and we have a pretty full week of fun planned. I'm going to take a few days off from coding and come back to it to code cooldown and knockback (I didn't get to that this week, and that's okay). Until then, however, I'm going to begin learning FL Studio and, hopefully, create a looping track. That should be possible (technically, it's also possible I'd join the NBA tomorrow). We'll see!