Disassembly Tutorial 02: Buff Yumbo

Here's a reference for all possible ASM instructions for the Game Boy. Make sure to read it and keep it bookmarked. It is a crucial reference to have when editing the disassembly. https://rgbds.gbdev.io/docs/v0.6.1/gbz80.7

The Yumbo is the first hostile enemy encountered on SR388. It flies back and forth near the ceiling and deals 5 units of damage to Samus when she collides with it. All enemies have an AI routine in “SRC/bank_002.asm”. This AI routine is run every 2 frames when the enemy is on screen. It handles all behavior for that enemy. Back in tutorial 1, it was the enemy AI of the missile refill (enAI_itemOrb) that decided when pickup_missileRefill would be called. The enemy AI routine for the Yumbo is enAI_smallBug. Go find it and let's take a look.

In the main part of the routine, we call enemy_flipSpriteId.now, then call enAI_smallBug.act, and finally return from the routine. There are many helper routines in bank 2 like enemy_flipSpriteId to help the enemy AI routines do common tasks. enemy_flipSpriteId.now toggles the enemy sprite ID's lowest bit when called, which results in changing between two sprites. For some reason, the rest of the enemy AI routine's code was separated from the main routine and put into enAI_smallBug.act. This is unnecessary. When you look at the code in the disassembly, keep an eye out for weirdness like this, because the more you can iron out those kinks, the easier it will become to modify the code. A future tutorial may address this more in-depth.

The address of hEnemy.counter is loaded into hl. Sometimes it's more efficient to use hl to operate on variables, especially if you have many of them. hEnemy.counter is incremented once, then if it is equal to $40, jump to enAI_smallBug.flip, where hEnemy.counter is set back to $00 and the enemy is flipped horizontally via another helper routine, enemy_flipHorizontal. If hEnemy.counter isn't $40, load address of hEnemy.xPos into hl and check the horizontal flip flag in the enemy's attributes. If the flag is set, increment x position once, else, decrement x position once.

Challenge: Can you double the Yumbo's speed in both directions?

Challenge: Can you make it turn around every 32 frames? Remember, the enemy AI runs only every 2 frames.

Variables are defined in “SRC/ram”. For example, metroidCountReal is in “SRC/ram/wram.asm”, where most variables are. hEnemy.counter is in “SRC/ram/hram.asm”, because it is a frequently used variable, and variables in HRAM can be accessed more efficiently with the ldh instructions. An interesting variable is samus_onscreenXPos, the position of Samus relative to the camera. The position of enemies is only defined relative to the camera, contrary to Samus's position which is defined relative to the current map bank. samus_onscreenXPos is calculated using Samus and the camera's position and is used for interactions between Samus and enemies and for drawing Samus's sprite.

I'm going to set the enemy's position to be equal to Samus's position. I erase the movement logic and replace it with the following:

    ld a, [samus_onscreenXPos]
    ldh [hEnemy.xPos], a
ret

Note that ld [hEnemy.xPos], [samus_onscreenXPos] is not possible, so I must use the a register as a middleman. Compile and test.

Extra challenge: Change the Yumbo's AI so that it does not move horizontally at all, and if Samus's y position is greater than the Yumbo's, increment Yumbo's y position by 1.

Ok, let's end things off with talking about enemy properties. The enemy header define some enemy properties, like health, directional shields and sprite ID. Check out “docs/Enemy Headers.md” for a full description of each property. Enemy headers are found in “SRC/data/enemyHeaders.asm”. I encourage you to go mess around with them.

Challenge: Can you make the Yumbo shielded against attacks from below?

Challenge: Can you make the Yumbo invincible to the Screw Attack? Directional shields will not work for this one. Find other properties in the header that are likely to help with defense.

Other properties, like contact damage, are set per sprite, rather than per enemy. You can modify an enemy sprite's properties by editing “SRC/data/enemies.csv”.