Yoshi Block Interaction - brunovalads/yoshisisland-disassembly GitHub Wiki

Block interaction

Block interaction in Yoshi's Island is done with interaction point. That means, instead of using hitboxes to interaect with blocks, a single pixel is checked which gets the tile number and the resulting interaction.

Interaction is, alongside the rest of Yoshi's physics, mostly (but not completely) handled by Super FX. "Not completely" because Yoshi's physics code are handled by a special GSU Init call at $00DECF which either resumes the S-CPU code if r0 is zero, executes a S-CPU code from a list at $00DF28, indexed by a positive value of r0, and return to GSU code or loads from the Map16 table and return to GSU code in case r0 is negative. This is done so to handle codes such changing a Map16 tile which can't be easily done on GSU side.

Initialisation

Let's start with the normal interaction routine located at $0BCF64. It first needs to load the interaction point offset table (which I'll call "interaction table" onwards) to r14 and the following tables are used:

  • $0AEB0E is the table of default interaction points
  • $0AEB20 is the table of interaction points for ducking
  • $0AEB32 is the table of interaction points for swimming

It also loads Yoshi's X and Y position to r9 and r10, respectively, and clears $70007A (ground type), $7000FE (interaction of water), $700100 (interaction of cross section) and $7000B6 (angle of ground). It also sets r2 to zero which gets stored to $7000FC at the end (so it acts as a temporary terrain collision flag).

Now it handles the individual interaction points. The following interaction points exist in Yoshi's Island:

  • Head left side
  • Body left side
  • Head right side
  • Body right side
  • Top left
  • Top right
  • Bottom left
  • Bottom center
  • Bottom right

In total, Yoshi uses nine interaction points, two in each direction except for the bottom ones which use three each. Furthermore, interaction, is split to horizontal and vertical interaction and the latter in head and feet interaction.

Get Map16 Page Info

This is the most important routine since that loads the block in the first place. What it does is to load the two (signed) bytes of the interaction point offsets, get the block on the current offset and its page info.

The routine takes the following registers as the input:

  • r9: Player X position
  • r10: Player Y position
  • r14: Current index to the interaction table

The routine's output are:

  • r0: Page info bytes 1 and 2
  • r5: Next index to the interaction table
  • r6: Map16 number
  • r7: Page info bytes 1 and 2
  • r8: Page info byte 3
  • ($0000): Interaction point X position
  • ($0002): Interaction point Y position

What is the page info? Yoshi's Island determines its block acts like on the page number i.e. the Map16 number high byte with the low byte for some fine tuning. The page info table is located at $0ABB12 and contains three bytes: The block interaction type (which is bitwise), the block special interaction (which are multiples of $08 — it's to prepare jump pointers for special interaction) and the slope type.

These are the following interaction types:

  • Bit 0: Is ledge/platform
  • Bit 1: Is solid
  • Bit 2: Is slope
  • Bit 3: Is water
  • Bit 4: Is lava
  • Bit 5: Is cross section
  • Bits 6 and 7: Unused

The routine is pretty straight forewards, eh?

Except it isn't. I leaved out two special cases: Conditional block interaction and offset per tile.

Let's start with conditional block interaction: There are two blocks whose interaction depend on certain condition: The !-blocks from the !-switches and egg blocks for Super Baby Mario. If the second byte is $78 or $80, it's a !-block and egg block, respectively. So what happens if a !-switch is active or Mario is super? Very simple: The game sets bit 2 of r7 of the corresponding block i.e. mark the block as solid. On a side note: That explains why $701E08 have the bitflags at bits 3 and 4 instead of 0 and 1 because those are multiples of eight (the special interaction value gets subtracted by $70 before they get AND by $701E08).

To handle offset per tile, the routine at $0BD3BE shifts the vertical interaction point at the current collumn so it matches up visually. What it does is to get the difference between the camera X position modulo 8 and the interaction point X position and divide them by 8. If the interaction point is offscreen horizontally, load up the normal offset, else load the value from $701EF0 (OPT camera Y position, essencially the Y offset from the leftmost collumn) if it's the leftmost collumn, otherwise read from the table $1F32, indexed by the aforementioned quotient minus 1. Then get the difference between the camera position and the collumn's offset and add it to the vertical offset.

Horizontal Interaction

Now we start with the actual interaction. The routine which handles horizontal interaction points is located at $0BD287 but needs to be initialised at $0BD282 (which sets r1 and $700106 to zero) at first. It takes r3 as the input to determine the direction (positive values mean left side, negative values mean right side) alongside r1 and r2. The first thing it does is to shift r2 to the left and r1 to the left twice. It then loads the current block and check if it's a solid block. In case it is, set the current interaction point to be blocked, otherwise return. It also checks if Yoshi moves to the same direction as the push speed in r3 and if he does, return. In the case he moves to the same direction he's blocked off or doesn't move, increment r1 as well, store r3 to r4 (which will push Yoshi out of a wall), get the X position within a block (modulo 16), flip the first four bits if r3 is positive and store r3 to the subpixel position if the value is zero.

There also is a special case for skiing which also checks for a slope. If Yoshi is on ground, get the block to the right/left of the interaction point (depending on whether the interaction is to the left or right) and check if the block is a slope. If yes, return, otherwise continue with the normal block interaction. If he is in the air, check if he falls and if so, return, else continue with the normal block interaction.

The routine all converge to a jump to $0BD514 which handles water, cross section and special block interaction.

Post interaction points

After the interaction points run, the game shifts the current X position in r9 by the value in r4. The first thing it does is to preserve the push timer to r3 and then clear it and then check if r1 is not zero and if it is, clear out Yoshi's horizontal speed. If he isn't jumping, check if the player presses on the D-pad the same direction which Yoshi is facing to the wall and increment r3. Handle the push animation.

There also is a special case for pipes: If Yoshi is on the wall, check if he still has got Baby Mario (he can't transition between levels if he has lost the baby) and hasn't his tongue out. Also check if the current block is a pipe and the player presses the controller in the right direction. If those are true, jump to $0BDC36 and handle the pipe entrance. Otherwise handle in those cases the wall push animation.

There also is a special condition for when he isn't on a wall or jumping: Some sprites —such as Chomp Rocks— can be pushed so there is an exception in case he doesn't touch a wall or is in the air: If he pushes a sprite, set the push timer to be at least 0xF.

There are 19 different frames for the push animation and he needs to be on a wall for 16 frames before he starts to push. As such, the push animation cycles from 0x10 to 0x22.

Vertical Interaction

Vertical interaction is slightly different from horizontal interaction. For one, there are notable differences between top and bottom interaction (after all, you accelerate downwards), for the other, it also handles slope interaction.

Let's start with the basics: The routine is located at $0BD3EE and takes r3 as the input for the direction. Positive values mean head interaction, negative values mean feet interaction. It multiplies r2 by two and it also sets $700010, one of the return values, to zero. It next gets the current interaction point and get the tile at that interaction point. Now it gets interesting: Store the interaction point's Y position modulo 16 (essentially the Y position within a block or difference between the interaction point and the ground) to r1 and r12.

Now we get to the slope interaction which we explain in a different section due to its complexity. As such, let's skip it for now and treat none of the blocks as slopes. Let's check for the block's properties and check if it's a solid block (head interaction) but also if it's a platform (feet interaction). If so, mark the interaction point as blocked and negate the Y position within the block (so it's more the difference between the interaction point and the top of the block now). If the head is interacting, jump to the special interaction for the head at $0BD631. The ground interaction is a bit more complicated because platforms are passable from below and they can't too tolerant towards the player. For that, check if Yoshi moves downwards and also check if the interaction point is located at the top half of the block. If those fail, remove the interaction has been blocked flag and jump to special ground interaction at $0BCCBF.

In case Yoshi doesn't touch a solid block or fails to stand on a platform, set r1 to be $20 and jump to the respective special interaction routine.

Slope Interaction

A special case of vertical interaction is slope interaction. If you look at the flow of code, the game checks two blocks. Most notably, Yoshi's Island doesn't use slope assist tiles found in other games such as Super Mario World.

There are two points to consider: Slopes can have two different directions: Rightside-up and upside-down. The former merely requires the block to have bit 2 set, the latter also needs bit 0 to be set (i.e. the block is also marked as a platform). If that is the case, handle the slope interaction, otherwise check if the block below (head interaction) or above (ground interaction) is a slope as well. If not, handle a flat block, otherwise shift r1 upwards or downwards (since we now are in a different block) and do slope magic.

Now we handle slope offsets. For that, the table at $0ABD0E contains a list of values, the first one contains the slope's angle, the second byte the ceiling/ground's height. It is indexed by the slope type, X position within the block and type of the interaction point. Now get the difference between the ground height and the Y position within the block and check if the Y position is located above (head) or below (feet) the slope. If so, store the difference to r1 as the new position within the block, store the slope's angle to $700010 and mark the interaction point as blocked.

Similar to straight ground, there is a special interaction handling for feet interaction due to gravity and slope platforms: Get the angle's absolute value, divide it by 16, add it with 9 and r1 and check if the value is negative (once again, that's done so to check if Yoshi's feet are high enough). If that's the case, he isn't on the slope, otherwise check if he is falling or not in the air and go to special ground interaction if so (thus confirming he is on a slope). If not, shift Yoshi upwards if the current interaction point wasn't on a slope last frame and he moves to the same direction where the slope moves upwards (i.e. to the left when the slope goes upwards to the left).

Lastly, the regular ground interaction runs when the interaction point's first take doesn't detected a slope and the second one does but it otherwise fails to handle the slope.

Post interaction points (head)

Because of notable differences (again, gravity), I split the post interaction points for head and feet in separate sections.

After the interaction points run, check if any of the interaction point has been blocked and Yoshi moves upwards. Clear out his speed, reset his flutter chain, play the head bop sound and set the animation timer for the head pop animation. This is usually over except for pipes which, once again, have got a special case.

Unlike sideways pipes, which have got their own special interaction routine, the special interaction for pipes is handled past the interaction points. As such, the right interaction point handles pipes since that is processed the last. Beyond requiring the player to have Mario on Yoshi's back, only run the code if the special interaction type is $A0. Now get the pipe type: The pipe table at $0AEBBC contains the different types of pipes and check if the pipe can be entered from below. Get the position within the block but add 16 to it if it's the right side. That value is stored to $70010E to get the distance between the pipe and Yoshi. You can't enter the pipe from any position so the position within the pipe must be between 0x0A and 0x1A. Lastly, check if the player presses up and Yoshi don't have his tongue out and let's then go into the pipe! Entering the pipe also prevents to play the head bop sound and run the head bop animation.

If Yoshi doesn't take a pipe, shift his Y position downwards (for some reason).

Post interaction points (feet)

Ground interaction is complex. Like, really complex. For starters: After an interaction point runs, the game preserves the following data: Difference to the surface (negative Y position within a block on a flat surface, added with the slope height if on a slope), slope angle, interaction point X and Y position and block information, for each interaction point. That one is necessary to check which interaction point is located the highest and also for a second set of special interaction.

Next up, there is a bit going on when Yoshi isn't on the ground: If he is on a platform sprite, clear out the angle of the ground (since he isn't on a slope), load his vertical speed to r6 and skip a good portion of the ground interaction, otherwise set him to fall if he isn't jumping and skip the ground interaction entirely.

If he is on the ground, the height between the interaction points are compared and the highest interaction point is taken: The left interaction point is stored to r0, the centre one to r5 and the right one to r3. If r0 is bigger than r5, r0 is below r5 and store r5 to r0, then compare if r3 is larger than r0 and store r0 to r3. Shift Yoshi's Y position upwards by the value in r3. Now we get to the most interesting part about the ground interaction: There is a second set of special block interaction, one for each interaction point (which is why the block number and page info has been preserved for each interaction point) but with the twist that the corresponding special interaction only run for the highest interaction point. The routine at $0BDB1D is for the outer and $0BDA99 for the center interaction point.

Being on ground also means to clear the vertical speed (but still preserve to r6) and set his vertical subpixel position to 0xFF. This doesn't happen if he's swimming, though.

Now we reach the point where the solid sprite interaction and ground interaction converge: Store the angle of the heighest interaction point to $7000B6 and clear out the individual interaction point's angles. If he is in air, mark him on ground, transfer his previous speed to the current speed, set the animation timer for landing and go to the subroutine $0BDF2D. In the case Mode 2 is active, get the collumn offset at Yoshi's position and store that to $7001B4 (since he might stand on a moving platform).

Now we handle the entrance. No, not a vertical pipe (that's handled in the special inner feet interaction), it's the sewer pipes which basically act as doors. Yoshi will enter a sewer pipe if his X position is between 0x04 and 0x0D, hasn't got the tongue out, has got Baby Mario on his back and the player pressed up.

Post interaction

There still are some things to be done: First of all, store Yoshi's blocked state and Yoshi's position in r9 and r10 to SRAM ($70008C, $700090 and $7000FC) and check if he has touched a block which hurts him (e.g. a spiky stake). If so, handle the SNES code at $00E0E6 To finish terrain interaction, handle cross sections. For that, check if all of Yoshi's interaction points (except his feet) is within a cross section (this includes blocked interaction points to handle walls and ceilings). If he is already in a cross section, check if any of Yoshi's interaction point (except, again, his feet) are located outside of a cross section. If those are true, go to the SNES code $00E0D7.

Water/Cross Section Interaction

After each interaction point (but before any special interaction) runs, $0BD4EC is called. That one checks if a block has the water or cross section flag enabled and mark the interaction point as in water / in cross section accordingly. There is, however, also a special case for water which checks if the interaction point is below $7001BC which is the water height for the current level (think of layer 3 water).