Juggernaut's Skull Bullets


I’m resuming work on the project and would like to share some insights from my reverse engineering study. I haven’t settled on the best format for documentation yet, so I’ll be using my personal notes to describe the discoveries I make. Likewise, I’m starting up with the last feature implemented, the Juggernaut’s Skull Bullets.

Note: In the current release of the game (version 48), only the primary projectile function is simulated (activated by clicking the mouse on the screen). This functionality does not yet extend to the Juggernaut.

Skull Bullet Logic:

  • Main Function: 0x87b0 (fired by 0x86fa)
  • Sprite Priority: 0x300
  • Collision Group: 1 (Kid Projectile)

Graphics Assets

Sprite Sheet and Sprites

The Skull Bullet’s two frames are manually loaded in raw format by function 0x129d6:

  • 0x12a08 - Size: 0x80 - raw format
  • 0x12a8c - Size: 0x80 - raw format

It also utilizes the same explosion sprites and animation as the Maniaxe’s Axes, employing this graphics sheet:

  • 0x992e4 - kid format

Animations

  • 0x8b98 - Skull Looping Animation
  • 0x8bde - Explosion Destroy Animation (same of Maniaxe’s Axe)

Global Variables

  • 0xfffffb70 - ActiveSkullBullets - word - Maintains the number of actively updated Skull Bullets

Local Variables

  • availableHits - word - Number of block hits available before destruction

Behaviour

The Skull Bullet ricochets off floors, ceilings, and walls, capable of bouncing up to four times. When the bullet’s position overlaps with a block, it is adjusted to align with the block’s margin, corresponding to its direction of movement. Upon contacting a wall, the bullet’s horizontal speed is reversed and reduced by 1.0; if this reduction would bring the speed to zero, it is simply reversed with no decrease. When hitting a floor or ceiling, the bullet’s vertical speed is inverted and reduced by 25%.

Starting Up

  • Increments ActiveSkullBullets by 1.
  • After creation, it adopts the position and flipX of the player (saved by the firing function 0x86fa) and subtracts 8 from the y position
  • Sets x velocity +4 (or -4 if flipped).
  • Add to x position +0x1a (or -0x1a if flipped).
  • Sets availableHits to 4 (the bullets can hit blocks exactly 4 times).
  • Checks if there is a solid block (0x6000) at the position. If so, executes the destroy subroutine immediately without looping.
  • Sets the looping animation.

Loop

  • Waits until the next frame.
  • Checks if the bullet sprite has been unloaded from VRAM or if it receives an event (in either case, cancels the loop and proceeds to the Destroy subroutine).
  • Adds 0x1000 to Y Speed (0.0625).
  • Calculates the horizontal movement as follows:
newXPos = xPos + xSpd
# For performance reasons, only checks if the point is within a new block:
if isPointWithinNewBlock(xPos, yPos, newXPos, yPos) and isSolidBlock(newXPos, yPos): 
    # Align the point just outside the block according to direction:
    xPos = alignPointWithBlockHorizontally(xPos, newXPos) 
    availableHits -= 1
    if availableHits == 0:
        # Skips the loop and goes to destroy subroutine:
	return destroy()
    # Decreases horizontal speed by 1.0:
    if abs(xSpd) - 1.0 > 0:
        xSpd = (abs(xSpd) - 1.0) * sign(xSpd) 
	xSpd *= -1.0
	flipX = not flipX
else:
    xPos = newXPos
  • Calculates the vertical movement as follows:
newYPos = yPos + ySpd
# For performance reasons, only checks if the point is within a new block:
if isPointWithinNewBlock(xPos, yPos, xPos, newYPos) and isSolidBlock(xPos, newYPos):
    # Align the point just outside the block according to direction:
    yPos = alignPointWithBlockVertically(yPos, newYPos)
    availableHits -= 1
    if availableHits == 0:
        # Skips the loop and goes to destroy subroutine:
            return destroy()
    # Invert the vertical speed and reduce it by 25%
    ySpd *= -0.75
else:
    yPos = newYPos

Destroy

  • Subtracts 1 from ActiveSkullBullets (if it’s greater than zero).
  • Disables automatic movement updates and prepares the sprite to use the explosion sheet and palette.
  • Plays Sound Effect: 0x24.
  • Sets the animation to 0x8bde.
  • Waits until the animation ends.
  • Clears itself (destroy).

Trajectory Examples

https://colab.research.google.com/drive/1Ozs6OihUcFPTjFIBsqw8ohkNG2TcXV84?usp=sharing

Differences in KidEngine

  • The major difference of the bullet logic in KidEngine is the use of delta time for movement updates so it can be smooth as possible and with slow motion support.
  • Also it can bypass any of the limitations of the original engine, want more bullets? More bouncing? It’s all possible.
  • with some refactor and mod support, it can have better animations and general behaviour modifications. Note that the engine do not supports mods yet.

Leave a comment

Log in with itch.io to leave a comment.