-
Notifications
You must be signed in to change notification settings - Fork 822
Add a new overworld sprite
This tutorial is for how to add a new overworld sprite. As an example, we'll add three different kinds of sprites.
- Know which kind of sprite you need
- Define a sprite constant
- Update files depending on the kind of sprite
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".)
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).
Each kind of sprite uses data in different files.
First, create 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
, orSTILL_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.)
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.
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.
See also: Improve the outdoor sprite system