Skip to content

Add a new overworld sprite

Grate Oracle Lewot edited this page Mar 1, 2024 · 7 revisions

This tutorial is for how to add a new overworld sprite. As an example, we'll add three different kinds of sprites.

Contents

  1. Know which kind of sprite you need
  2. Define a sprite constant
  3. Update files depending on the kind of sprite

1. Know which kind of sprite you need

There are three kinds of overworld sprites:

  • Regular sprites have their own individual graphics.
  • Pokémon sprites reuse the menu icons for party Pokémon.
  • Variable sprites do not have any graphics, but can appear as any other sprite. (The sprite they appear as can be changed, hence "variable".)

2. Define a sprite constant

Edit constants/sprite_constants.asm:

 ; sprite ids
 ; OverworldSprites indexes (see data/sprites/sprites.asm)
 	const_def
 	const SPRITE_NONE ; 00
 	...
 	const SPRITE_STANDING_YOUNGSTER ; 66
+	const SPRITE_GIOVANNI
 DEF NUM_OVERWORLD_SPRITES EQU const_value - 1

 ; SpriteMons indexes (see data/sprites/sprite_mons.asm)
 	const_def $80
 SPRITE_POKEMON EQU const_value
 	const SPRITE_UNOWN ; 80
 	...
 	const SPRITE_HO_OH ; a2
+	const SPRITE_EGG
 DEF NUM_POKEMON_SPRITES EQU const_value - SPRITE_POKEMON

 ; special GetMonSprite values (see engine/overworld/overworld.asm)
 	const_def $e0
 	const SPRITE_DAY_CARE_MON_1 ; e0
 	const SPRITE_DAY_CARE_MON_2 ; e1

 ; wVariableSprites indexes (see wram.asm)
 	const_def $f0
 DEF SPRITE_VARS EQU const_value
 	const SPRITE_CONSOLE ; f0
 	...
 	const SPRITE_JANINE_IMPERSONATOR ; fc
+	const SPRITE_LOOKER

We've defined three new sprites: SPRITE_GIOVANNI is regular, SPRITE_EGG is a Pokémon, and SPRITE_LOOKER is variable. (The intention is that Looker, the International Police officer, can appear in disguise.)

The Pokémon sprites all come after defining SPRITE_POKEMON, and the variable sprites after SPRITE_VARS. Notice how their exact starting values are set by const_def; the particular values don't matter, so for example, if you need space for more Pokémon sprites you can lower the SPRITE_POKEMON starting value (thus eating into slots which could otherwise have been used for regular sprites).

3. Update files depending on the kind of sprite

Each kind of sprite uses data in different files.

Regular sprite: define graphics and properties

First, create gfx/sprites/giovanni.png:

gfx/sprites/giovanni.png

Then edit gfx/sprites.asm:

+SECTION "Sprites 3", ROMX
+
+GiovanniSpriteGFX:: INCBIN "gfx/sprites/giovanni.2bpp"

It doesn't matter where new sprite graphics go, so you can add them in the existing sections or create new ones. New sections will automatically be placed wherever they'll fit when you run make, so you don't have to deal with bank overflow errors.

Finally, edit data/sprites/sprites.asm:

 OverworldSprites:
 ; entries correspond to SPRITE_* constants
 	table_width NUM_SPRITEDATA_FIELDS, OverworldSprites
 	overworld_sprite ChrisSpriteGFX, 12, WALKING_SPRITE, PAL_OW_RED
 	...
 	overworld_sprite StandingYoungsterSpriteGFX, 12, STANDING_SPRITE, PAL_OW_BLUE
+	overworld_sprite GiovanniSpriteGFX, 12, WALKING_SPRITE, PAL_OW_BROWN
 	assert_table_length NUM_OVERWORLD_SPRITES

The overworld_sprite macro takes four arguments:

  • pointer: The label of the sprite graphics.
  • length: How many tiles the sprite uses, not counting alternate walking frames. A single frame is 16x16 pixels and 2x2 = 4 tiles; a standing or walking sprite has three basic frames, so 12 tiles total.
  • type: Either WALKING_SPRITE, STANDING_SPRITE, or STILL_SPRITE.
  • palette: The default color palette for overworld events that don't assign a particular one.

Note that BigSnorlaxSpriteGFX, BigLaprasSpriteGFX, and BigOnixSpriteGFX do not have the usual three-frame structure (facing down, up, and to the side), but they still use the STANDING_SPRITE type. This is because the type just affects how many tiles get loaded when the sprite is used. NPC events in map scripts that use these sprites will also use the movement functions SPRITEMOVEDATA_BIGDOLL, SPRITEMOVEDATA_BIGDOLLSYM (for the symmetrical Snorlax or Lapras), or SPRITEMOVEDATA_BIGDOLLASYM (for the asymmetrical Onix). Different movement functions can make display the sprites' tiles in different ways than the usual 2-by-2 NPC structure. (Designing your own nonstandard sprites is beyond the scope of this tutorial.)

Pokémon sprite: define corresponding Pokémon

Edit data/sprites/sprite_mons.asm:

 SpriteMons:
 ; entries correspond to SPRITE_* constants past SPRITE_POKEMON
 	table_width 1, SpriteMons
 	db UNOWN
 	...
 	db HO_OH
+	db EGG
 	assert_table_length NUM_POKEMON_SPRITES

This means that SPRITE_EGG will use the same menu icon as EGG, just like SPRITE_UNOWN uses the icon for UNOWN, etc.

There's one problem: EGG doesn't actually have a defined menu icon, since the party menu code treats Eggs as a special case. So edit data/pokemon/menu_icons.asm:

 MonMenuIcons:
 	table_width 1, MonMenuIcons
 	db ICON_BULBASAUR   ; BULBASAUR
 	...
 	db ICON_HUMANSHAPE  ; CELEBI
+	db ICON_MONSTER     ; MON_FC
+	db ICON_EGG         ; EGG
+	db ICON_MONSTER     ; MON_FE
 	assert_table_length NUM_POKEMON

Now EGG (and the two unused Pokémon slots) has its menu icon defined in the usual way, so SPRITE_EGG will appear correctly.

Variable sprite: initialize appearance

Edit engine/events/std_scripts.asm:

 InitializeEventsScript:
 	...
 	variablesprite SPRITE_WEIRD_TREE, SPRITE_SUDOWOODO
 	...
 	variablesprite SPRITE_JANINE_IMPERSONATOR, SPRITE_LASS
+	variablesprite SPRITE_LOOKER, SPRITE_ROCKET

InitializeEventsScript is called once at the very start of the game. It sets a lot of events, since they're all unset by default, and initializes how variable sprites will look.

The variable sprites' appearances are stored in wVariableSprites in ram/wram.asm. We don't need to edit that, since it's automatically sized to fit all the variable sprites.

Note that if you change the const_def before SPRITE_VARS to fit more (or fewer) variable sprites, the size of wVariableSprites will change, and it might get too big to fit in its bank. Then you'll have to find unused space in wram.asm to delete (like the ds 40 a few lines above wVariableSprites).

When event scripts use variable sprites, they use the variablesprite command to assign a new appearance, followed by special LoadUsedSpritesGFX if a sprites needs to visibly change its appearance. For example, in maps/FuchsiaPokecenter1F.asm:

	variablesprite SPRITE_JANINE_IMPERSONATOR, SPRITE_JANINE
	special LoadUsedSpritesGFX
	...
	variablesprite SPRITE_JANINE_IMPERSONATOR, SPRITE_LASS
	special LoadUsedSpritesGFX

Anyway, that's all we need to do. Now the new SPRITE_* constants can be used just like any others.

Screenshot

See also: Improve the outdoor sprite system

Clone this wiki locally