ref: 40ea6a5f757bbbfdb4296e5acf644f6679a03a14
dir: /engine/overworld/map_sprites.asm/
; 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 wSpriteStateData1-wSpriteStateData1 + $ff and wSpriteStateData2 + $00-wSpriteStateData2 + $ff 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,wSpriteStateData2 + $0d ; 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,wSpriteStateData2 + $0d 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,wSpriteStateData2 + $1e .loadTilePatternLoop ld de,wSpriteStateData2 + $1d ; 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,wSpriteStateData2 + $0e 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,vNPCSprites ; 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,vSprites + $7c0 ; 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,vSprites + $780 ; 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,[wFontLoaded] 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,[wFontLoaded] 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,wSpriteStateData2 + $0d 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,[wFontLoaded] 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,wSpriteStateData2 + $0d 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,wSpriteStateData2 + $1e 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,wSpriteStateData1 + $10 ; 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"