summaryrefslogtreecommitdiff
path: root/engine/overworld/map_sprites.asm
diff options
context:
space:
mode:
Diffstat (limited to 'engine/overworld/map_sprites.asm')
-rwxr-xr-xengine/overworld/map_sprites.asm440
1 files changed, 440 insertions, 0 deletions
diff --git a/engine/overworld/map_sprites.asm b/engine/overworld/map_sprites.asm
new file mode 100755
index 00000000..a734319d
--- /dev/null
+++ b/engine/overworld/map_sprites.asm
@@ -0,0 +1,440 @@
+; Loads tile patterns for map's sprites.
+; For outside maps, it loads one of several fixed sets of sprites.
+; For inside maps, it loads each sprite picture ID used in the map header.
+; This is also called after displaying text because loading
+; text tile patterns overwrites half of the sprite tile pattern data.
+; Note on notation:
+; $C1X* and $C2X* are used to denote $C100-$C1FF and $C200-$C2FF sprite slot
+; fields, respectively, within loops. The X is the loop index.
+; If there is an inner loop, Y is the inner loop index, i.e. $C1Y* and $C2Y*
+; denote fields of the sprite slots interated over in the inner loop.
+InitMapSprites: ; 1785b (5:785b)
+ call InitOutsideMapSprites
+ ret c ; return if the map is an outside map (already handled by above call)
+; if the map is an inside map (i.e. mapID >= $25)
+ ld hl,wSpriteStateData1
+ ld de,$c20d
+; Loop to copy picture ID's from $C1X0 to $C2XD for LoadMapSpriteTilePatterns.
+.copyPictureIDLoop
+ ld a,[hl] ; $C1X0 (picture ID)
+ ld [de],a ; $C2XD
+ ld a,$10
+ add e
+ ld e,a
+ ld a,$10
+ add l
+ ld l,a
+ jr nz,.copyPictureIDLoop
+
+; This is used for both inside and outside maps, since it is called by
+; InitOutsideMapSprites.
+; Loads tile pattern data for sprites into VRAM.
+LoadMapSpriteTilePatterns: ; 17871 (5:7871)
+ ld a,[W_NUMSPRITES]
+ and a ; are there any sprites?
+ jr nz,.spritesExist
+ ret
+.spritesExist
+ ld c,a ; c = [W_NUMSPRITES]
+ ld b,$10 ; number of sprite slots
+ ld hl,$c20d
+ xor a
+ ld [$ff8e],a ; 4-tile sprite counter
+.copyPictureIDLoop ; loop to copy picture ID from $C2XD to $C2XE
+ ld a,[hli] ; $C2XD (sprite picture ID)
+ ld [hld],a ; $C2XE
+ ld a,l
+ add a,$10
+ ld l,a
+ dec b
+ jr nz,.copyPictureIDLoop
+ ld hl,$c21e
+.loadTilePatternLoop
+ ld de,$c21d
+; Check if the current picture ID has already had its tile patterns loaded.
+; This done by looping through the previous sprite slots and seeing if any of
+; their picture ID's match that of the current sprite slot.
+.checkIfAlreadyLoadedLoop
+ ld a,e
+ and a,$f0
+ ld b,a ; b = offset of the wSpriteStateData2 sprite slot being checked against
+ ld a,l
+ and a,$f0 ; a = offset of current wSpriteStateData2 sprite slot
+ cp b ; done checking all previous sprite slots?
+ jr z,.notAlreadyLoaded
+ ld a,[de] ; picture ID of the wSpriteStateData2 sprite slot being checked against
+ cp [hl] ; do the picture ID's match?
+ jp z,.alreadyLoaded
+ ld a,e
+ add a,$10
+ ld e,a
+ jr .checkIfAlreadyLoadedLoop
+.notAlreadyLoaded
+ ld de,$c20e
+ ld b,$01
+; loop to find the highest tile pattern VRAM slot (among the first 10 slots) used by a previous sprite slot
+; this is done in order to find the first free VRAM slot available
+.findNextVRAMSlotLoop
+ ld a,e
+ add a,$10
+ ld e,a
+ ld a,l
+ cp e ; reached current slot?
+ jr z,.foundNextVRAMSlot
+ ld a,[de] ; $C2YE (VRAM slot)
+ cp a,11 ; is it one of the first 10 slots?
+ jr nc,.findNextVRAMSlotLoop
+ cp b ; compare the slot being checked to the current max
+ jr c,.findNextVRAMSlotLoop ; if the slot being checked is less than the current max
+; if the slot being checked is greater than or equal to the current max
+ ld b,a ; store new max VRAM slot
+ jr .findNextVRAMSlotLoop
+.foundNextVRAMSlot
+ inc b ; increment previous max value to get next VRAM tile pattern slot
+ ld a,b ; a = next VRAM tile pattern slot
+ push af
+ ld a,[hl] ; $C2XE (sprite picture ID)
+ ld b,a ; b = current sprite picture ID
+ cp a,SPRITE_BALL ; is it a 4-tile sprite?
+ jr c,.notFourTileSprite
+ pop af
+ ld a,[$ff8e] ; 4-tile sprite counter
+ add a,11
+ jr .storeVRAMSlot
+.notFourTileSprite
+ pop af
+.storeVRAMSlot
+ ld [hl],a ; store VRAM slot at $C2XE
+ ld [$ff8d],a ; used to determine if it's 4-tile sprite later
+ ld a,b ; a = current sprite picture ID
+ dec a
+ add a
+ add a
+ push bc
+ push hl
+ ld hl,SpriteSheetPointerTable
+ jr nc,.noCarry
+ inc h
+.noCarry
+ add l
+ ld l,a
+ jr nc,.noCarry2
+ inc h
+.noCarry2
+ push hl
+ call ReadSpriteSheetData
+ push af
+ push de
+ push bc
+ ld hl,$8000 ; VRAM base address
+ ld bc,$c0 ; number of bytes per VRAM slot
+ ld a,[$ff8d]
+ cp a,11 ; is it a 4-tile sprite?
+ jr nc,.fourTileSpriteVRAMAddr
+ ld d,a
+ dec d
+; Equivalent to multiplying $C0 (number of bytes in 12 tiles) times the VRAM
+; slot and adding the result to $8000 (the VRAM base address).
+.calculateVRAMAddrLoop
+ add hl,bc
+ dec d
+ jr nz,.calculateVRAMAddrLoop
+ jr .loadStillTilePattern
+.fourTileSpriteVRAMAddr
+ ld hl,$87c0 ; address for second 4-tile sprite
+ ld a,[$ff8e] ; 4-tile sprite counter
+ and a ; is it the first 4-tile sprite?
+ jr nz,.loadStillTilePattern
+; if it's the first 4-tile sprite
+ ld hl,$8780 ; address for first 4-tile sprite
+ inc a
+ ld [$ff8e],a ; 4-tile sprite counter
+.loadStillTilePattern
+ pop bc
+ pop de
+ pop af
+ push hl
+ push hl
+ ld h,d
+ ld l,e
+ pop de
+ ld b,a
+ ld a,[$cfc4]
+ bit 0,a ; reloading upper half of tile patterns after displaying text?
+ jr nz,.skipFirstLoad ; if so, skip loading data into the lower half
+ ld a,b
+ ld b,0
+ call FarCopyData2 ; load tile pattern data for sprite when standing still
+.skipFirstLoad
+ pop de
+ pop hl
+ ld a,[$ff8d]
+ cp a,11 ; is it a 4-tile sprite?
+ jr nc,.skipSecondLoad ; if so, there is no second block
+ push de
+ call ReadSpriteSheetData
+ push af
+ ld a,$c0
+ add e
+ ld e,a
+ jr nc,.noCarry3
+ inc d
+.noCarry3
+ ld a,[$cfc4]
+ bit 0,a ; reloading upper half of tile patterns after displaying text?
+ jr nz,.loadWhileLCDOn
+ pop af
+ pop hl
+ set 3,h ; add $800 to hl
+ push hl
+ ld h,d
+ ld l,e
+ pop de
+ call FarCopyData2 ; load tile pattern data for sprite when walking
+ jr .skipSecondLoad
+; When reloading the upper half of tile patterns after diplaying text, the LCD
+; will be on, so CopyVideoData (which writes to VRAM only during V-blank) must
+; be used instead of FarCopyData2.
+.loadWhileLCDOn
+ pop af
+ pop hl
+ set 3,h ; add $800 to hl
+ ld b,a
+ swap c
+ call CopyVideoData ; load tile pattern data for sprite when walking
+.skipSecondLoad
+ pop hl
+ pop bc
+ jr .nextSpriteSlot
+.alreadyLoaded ; if the current picture ID has already had its tile patterns loaded
+ inc de
+ ld a,[de] ; a = VRAM slot for the current picture ID (from $C2YE)
+ ld [hl],a ; store VRAM slot in current wSpriteStateData2 sprite slot (at $C2XE)
+.nextSpriteSlot
+ ld a,l
+ add a,$10
+ ld l,a
+ dec c
+ jp nz,.loadTilePatternLoop
+ ld hl,$c20d
+ ld b,$10
+; the pictures ID's stored at $C2XD are no longer needed, so zero them
+.zeroStoredPictureIDLoop
+ xor a
+ ld [hl],a ; $C2XD
+ ld a,$10
+ add l
+ ld l,a
+ dec b
+ jr nz,.zeroStoredPictureIDLoop
+ ret
+
+; reads data from SpriteSheetPointerTable
+; INPUT:
+; hl = address of sprite sheet entry
+; OUTPUT:
+; de = pointer to sprite sheet
+; bc = length in bytes
+; a = ROM bank
+ReadSpriteSheetData: ; 17971 (5:7971)
+ ld a,[hli]
+ ld e,a
+ ld a,[hli]
+ ld d,a
+ ld a,[hli]
+ ld c,a
+ xor a
+ ld b,a
+ ld a,[hli]
+ ret
+
+; Loads sprite set for outside maps (cities and routes) and sets VRAM slots.
+; sets carry if the map is a city or route, unsets carry if not
+InitOutsideMapSprites: ; 1797b (5:797b)
+ ld a,[W_CURMAP]
+ cp a,REDS_HOUSE_1F ; is the map a city or a route (map ID less than $25)?
+ ret nc ; if not, return
+ ld hl,MapSpriteSets
+ add l
+ ld l,a
+ jr nc,.noCarry
+ inc h
+.noCarry
+ ld a,[hl] ; a = spriteSetID
+ cp a,$f0 ; does the map have 2 sprite sets?
+ call nc,GetSplitMapSpriteSetID ; if so, choose the appropriate one
+ ld b,a ; b = spriteSetID
+ ld a,[$cfc4]
+ bit 0,a ; reloading upper half of tile patterns after displaying text?
+ jr nz,.loadSpriteSet ; if so, forcibly reload the sprite set
+ ld a,[W_SPRITESETID]
+ cp b ; has the sprite set ID changed?
+ jr z,.skipLoadingSpriteSet ; if not, don't load it again
+.loadSpriteSet
+ ld a,b
+ ld [W_SPRITESETID],a
+ dec a
+ ld b,a
+ sla a
+ ld c,a
+ sla a
+ sla a
+ add c
+ add b ; a = (spriteSetID - 1) * 11
+ ld de,SpriteSets
+; add a to de to get offset of sprite set
+ add e
+ ld e,a
+ jr nc,.noCarry2
+ inc d
+.noCarry2
+ ld hl,$c20d
+ ld a,SPRITE_RED
+ ld [hl],a
+ ld bc,W_SPRITESET
+; Load the sprite set into RAM.
+; This loop also fills $C2XD (sprite picture ID) where X is from $0 to $A
+; with picture ID's. This is done so that LoadMapSpriteTilePatterns will
+; load tile patterns for all sprite pictures in the sprite set.
+.loadSpriteSetLoop
+ ld a,$10
+ add l
+ ld l,a
+ ld a,[de] ; sprite picture ID from sprite set
+ ld [hl],a ; $C2XD (sprite picture ID)
+ ld [bc],a
+ inc de
+ inc bc
+ ld a,l
+ cp a,$bd ; reached 11th sprite slot?
+ jr nz,.loadSpriteSetLoop
+ ld b,4 ; 4 remaining sprite slots
+.zeroRemainingSlotsLoop ; loop to zero the picture ID's of the remaining sprite slots
+ ld a,$10
+ add l
+ ld l,a
+ xor a
+ ld [hl],a ; $C2XD (sprite picture ID)
+ dec b
+ jr nz,.zeroRemainingSlotsLoop
+ ld a,[W_NUMSPRITES]
+ push af ; save number of sprites
+ ld a,11 ; 11 sprites in sprite set
+ ld [W_NUMSPRITES],a
+ call LoadMapSpriteTilePatterns
+ pop af
+ ld [W_NUMSPRITES],a ; restore number of sprites
+ ld hl,$c21e
+ ld b,$0f
+; The VRAM tile pattern slots that LoadMapSpriteTilePatterns set are in the
+; order of the map's sprite set, not the order of the actual sprites loaded
+; for the current map. So, they are not needed and are zeroed by this loop.
+.zeroVRAMSlotsLoop
+ xor a
+ ld [hl],a ; $C2XE (VRAM slot)
+ ld a,$10
+ add l
+ ld l,a
+ dec b
+ jr nz,.zeroVRAMSlotsLoop
+.skipLoadingSpriteSet
+ ld hl,$c110
+; This loop stores the correct VRAM tile pattern slots according the sprite
+; data from the map's header. Since the VRAM tile pattern slots are filled in
+; the order of the sprite set, in order to find the VRAM tile pattern slot
+; for a sprite slot, the picture ID for the sprite is looked up within the
+; sprite set. The index of the picture ID within the sprite set plus one
+; (since the Red sprite always has the first VRAM tile pattern slot) is the
+; VRAM tile pattern slot.
+.storeVRAMSlotsLoop
+ ld c,0
+ ld a,[hl] ; $C1X0 (picture ID) (zero if sprite slot is not used)
+ and a ; is the sprite slot used?
+ jr z,.skipGettingPictureIndex ; if the sprite slot is not used
+ ld b,a ; b = picture ID
+ ld de,W_SPRITESET
+; Loop to find the index of the sprite's picture ID within the sprite set.
+.getPictureIndexLoop
+ inc c
+ ld a,[de]
+ inc de
+ cp b ; does the picture ID match?
+ jr nz,.getPictureIndexLoop
+ inc c
+.skipGettingPictureIndex
+ push hl
+ inc h
+ ld a,$0e
+ add l
+ ld l,a
+ ld a,c ; a = VRAM slot (zero if sprite slot is not used)
+ ld [hl],a ; $C2XE (VRAM slot)
+ pop hl
+ ld a,$10
+ add l
+ ld l,a
+ and a
+ jr nz,.storeVRAMSlotsLoop
+ scf
+ ret
+
+; Chooses the correct sprite set ID depending on the player's position within
+; the map for maps with two sprite sets.
+GetSplitMapSpriteSetID: ; 17a1a (5:7a1a)
+ cp a,$f8
+ jr z,.route20
+ ld hl,SplitMapSpriteSets
+ and a,$0f
+ dec a
+ sla a
+ sla a
+ add l
+ ld l,a
+ jr nc,.noCarry
+ inc h
+.noCarry
+ ld a,[hli] ; determines whether the map is split East/West or North/South
+ cp a,$01
+ ld a,[hli] ; position of dividing line
+ ld b,a
+ jr z,.eastWestDivide
+.northSouthDivide
+ ld a,[W_YCOORD]
+ jr .compareCoord
+.eastWestDivide
+ ld a,[W_XCOORD]
+.compareCoord
+ cp b
+ jr c,.loadSpriteSetID
+; if in the East side or South side
+ inc hl
+.loadSpriteSetID
+ ld a,[hl]
+ ret
+; Uses sprite set $01 for West side and $0A for East side.
+; Route 20 is a special case because the two map sections have a more complex
+; shape instead of the map simply being split horizontally or vertically.
+.route20
+ ld hl,W_XCOORD
+ ld a,[hl]
+ cp a,$2b
+ ld a,$01
+ ret c
+ ld a,[hl]
+ cp a,$3e
+ ld a,$0a
+ ret nc
+ ld a,[hl]
+ cp a,$37
+ ld b,$08
+ jr nc,.next
+ ld b,$0d
+.next
+ ld a,[W_YCOORD]
+ cp b
+ ld a,$0a
+ ret c
+ ld a,$01
+ ret
+
+INCLUDE "data/sprite_sets.asm"