Sprite objects

Sprite objects are general use spritemaps, driven by an instruction set. Generally, they are utilized as 'particle effects', such as dust, explosions, or flashes, but they are also utilized by certain enemies to allow for additional sprite-maps to be drawn simultaneously without requiring additional enemies. If you're programming a custom item, enemy, or other such thing, sprite objects are a great, simple way to add graphical effects to your custom code. Spawning a Sprite Object

To spawn a sprite object, you first need to set 4 addresses in ram with parameters for that sprite object:

$12: The X position to spawn your sprite object $14: The Y position to spawn your sprite object $16: the Sprite object ID (see table below) $18: The base tile number and palette bits to spawn your sprite object with

To actually spawn the sprite object, you need to call the routine in bank $B4 which does so, using the following operation:

JSL $B4BC26

For any sprite object that uses graphics from the game's uncompressed, general use sprite graphic set, you're most likely going to need to store zero to $18. In cases where sprite objects need to use different palettes or graphics sets, the value stored to $18 will vary.

In order to delete a sprite object, the instruction list of that sprite object needs to run the instruction $BD07. Otherwise, you can clear all of the sprite objects by calling the routine at $B4BD97. Sprite object list Sprite Object ID Description Base tile number / palette bits Notes 00 Grapple Flash #$0000 01 Charge beam sparks #$0000 02 Charge beam sparks + flash #$0000 03 Super missile explosion #$0000 04 Bomb explosion #$0000 05 Ice beam particles #$0000 06 Dud shot #$0000 07 Power bomb #$0000 08 Some sort of energy shot, only 1 frame #$0000 09 Smoke plume #$0000 0A Dust 1 #$0000 0B Dust 2 #$0000 0C Small explosion from the large enemy death AI #$0000 0D Small health pickup #$0000 0E Large health pickup #$0000 0F Morph bomb #$0000 10 Slower small health pickup #$0000 11 Screw attack kill particles #$0000 12 Large, fast dust #$0000 13 Large, fast dust with a glitchy end frame #$0000 14 Large, fast dust with an extended glitchy end frame #$0000 15 Large, slow dust #$0000 16 Glitched space pirate shot? #$0000 17 Glitched space pirate shot? #$0000 18 Bubbles #$0000 19 Save station electricity? #$0000 1A Slowly extending vertical shutter #$0000 1B Somewhat quickly contracting vertical shutter #$0000 1C Elevator platform, permanent #$0000 1D Large enemy explosion #$0000 32 Metroid electricity $0F96,X : ORA $0F98,X Enemy DD7F must be allowed 34 Metroid shell $0F96,X : ORA $0F98,X Enemy DD7F must be allowed Creating Custom Sprite Objects

Within bank $B4, where the routines which create and clear sprite objects reside, you can also find the actual sprite object data. This data includes:

  $B4BDA8 - A table of Sprite Object pointers
  $B4BE24 - The instruction lists for each sprite object; additional custom instruction lists can be placed anywhere in the bank.
  $B4C630 - Sprite Object Spritemaps; these can also be placed anywhere in the bank.

Repointing the Sprite Object Table

In order to create additional sprite objects without overwriting any data, you will need to repoint the Sprite Object pointer table. In order to do this:

  Copy $7C bytes starting at the address $B4BDA8
  Locate freespace at the end of bank $B4. In the vanilla rom, this is located at $B4F4B8.
  Paste the $7C bytes you copied to freespace. If desired, you can now utilize the old pointer table's location as $7C bytes of freespace.
  Modify the LDA operation at $B4BC69 to point to your new pointer table. In a hex editor, this can be done like so: (B9 A8 BD → B9 <new pointer> )

At this point, all additional sprite objects and other modifications to your Sprite Object pointer table should be done to the newly repointed table. To avoid confusion, I suggest that you mark the old table as freespace by filling it with $FF

Please note that Sprite Object pointers are not called by their address in the rom, but are instead called by their index in the Sprite Object pointer table. You can find the index by dividing their offset from the start of the table by 2. For instance, a Sprite Object who's pointer is $64 bytes from the start of the table would be spawned using the index $32. Creating and modifying Sprite Object instructions

Sprite Object instructions are functionally identical to just about any other instruction formats in the rest of the rom. To put it simply, instruction lists are broken up into 4 or 3 byte chunks, containing 2 byte instructions and 1 byte arguments. These chunks follow two formats:

DW $TTTT, $PPPP ;wait $TTTT frames, then set the spritemap to $PPPP. $TTTT must be < $8000, or it will be parsed as a pointer.

DW $PPPP, $AA ;run the code at $PPPP with the parameter $AA. $PPPP must be >= $8000, or it will be parsed as a frame delay.

This might seem complex at first, but in practice, it can be quite simple, especially considering that Sprite Objects rarely need to run code. This instruction list, for example, would run a simple animation:

DW $0004, $F800 ;Wait 4 frames and then set the spritemap to $F800 DW $0004, $F807 ;Wait 4 frames and then set the spritemap to $F807 DW $0002, $F80E ;Wait 2 frames and then set the spritemap to $F80E DW $0001, $F815 ;Wait 1 frame and then set the spritemap to $F815 DW $0002, $F80E ;Wait 2 frames and then set the spritemap to $F80E DW $0004, $F807 ;Wait 4 frames and then set the spritemap to $F807 DW $BD07 ;Run the code at $BD07, no argument. (This points to the 'delete Sprite Object' routine.)

Note: All relevant Sprite Object instructions can be found in PJ's bank log.

Custom instruction lists can be placed anywhere within the bank. In order to call these instruction lists with a Sprite Object, you must add a new pointer to your Sprite Object pointerlist, which points to the start of your new instruction list. Creating and modifying Spritemaps

Sprite objects are not limited to single sprite tiles, but instead consist of several spritemaps, which are called by instructions and set to frame delays in order to create animated effects. In order to create visually unique sprite objects, you will need to create new spritemaps, or modify existing ones.

Within Super Metroid, spritemaps are formatted like so:

$NNNN ;Number of sprite entries within this spritemap $XX, $SS, $YY, $TT, $AA ;Sprite entry 0 $XX, $SS, $YY, $TT, $AA ;Sprite entry 1 … ;Sprite entry…

$XX ;X offset from the center of the spritemap $SS ;Size - +80 to set to 16×16, +01 to reverse the X offset. $YY ;Y offset from the center of the spritemap $TT ;Tile number. Counts from left to right, top to bottom. $AA ;Attributes. Starts at 20, +10 for layer priority, +40 for X flip, +80 for Y flip

Its important to note that spritemaps have NO terminator. Instead, the engine determines where and when to stop reading based on the initial 2 bytes of the spritemap.

Custom spritemaps for sprite objects can be placed anywhere within the bank. Assuming you really know what you're doing, you could also potentially write a custom instruction to load a spritemap from a long address, but without doing so, spritemap load instructions can only be preformed using short addresses. Finishing and using your new sprite objects

Once you have assembled all of your desired spritemaps, and have written an instruction list which utilizes them, all that is left to do is add the entry to your Sprite Object Pointer table, and call it ingame. In order to add the new Sprite Object to your pointer table, simply add the address of your instruction list to the pointer table. Make note of this new pointer's location within the table, as you will need to use this index within the table to spawn your sprite object. Once again, this index can be calculated by dividing the new pointer's offset from the start of the table by 2.

In summary: Sprite Objects are driven and controlled by their Instruction Lists, which point to and animate their spritemaps.