diff options
Diffstat (limited to 'engine/overworld')
40 files changed, 3260 insertions, 768 deletions
diff --git a/engine/overworld/advance_player_sprite.asm b/engine/overworld/advance_player_sprite.asm new file mode 100644 index 00000000..6b4a0cbb --- /dev/null +++ b/engine/overworld/advance_player_sprite.asm @@ -0,0 +1,241 @@ +_AdvancePlayerSprite:: + ld a,[wSpriteStateData1 + 3] ; delta Y + ld b,a + ld a,[wSpriteStateData1 + 5] ; delta X + ld c,a + ld hl,wWalkCounter ; walking animation counter + dec [hl] + jr nz,.afterUpdateMapCoords +; if it's the end of the animation, update the player's map coordinates + ld hl, wPikachuOverworldStateFlags + res 5, [hl] + ld a,[wYCoord] + add b + ld [wYCoord],a + ld a,[wXCoord] + add c + ld [wXCoord],a +.afterUpdateMapCoords + ld a,[wWalkCounter] ; walking animation counter + cp a,$07 + jp nz,.scrollBackgroundAndSprites +; if this is the first iteration of the animation + ld a,c + cp a,$01 + jr nz,.checkIfMovingWest +; moving east + ld a,[wMapViewVRAMPointer] + ld e,a + and $e0 + ld d,a + ld a,e + add $02 + and $1f + or d + ld [wMapViewVRAMPointer],a + jr .adjustXCoordWithinBlock +.checkIfMovingWest + cp a,$ff + jr nz,.checkIfMovingSouth +; moving west + ld a,[wMapViewVRAMPointer] + ld e,a + and a,$e0 + ld d,a + ld a,e + sub $02 + and $1f + or d + ld [wMapViewVRAMPointer],a + jr .adjustXCoordWithinBlock +.checkIfMovingSouth + ld a,b + cp a,$01 + jr nz,.checkIfMovingNorth +; moving south + ld a,[wMapViewVRAMPointer] + add $40 + ld [wMapViewVRAMPointer],a + jr nc,.adjustXCoordWithinBlock + ld a,[wMapViewVRAMPointer + 1] + inc a + and $03 + or $98 + ld [wMapViewVRAMPointer + 1],a + jr .adjustXCoordWithinBlock +.checkIfMovingNorth + cp a,$ff + jr nz,.adjustXCoordWithinBlock +; moving north + ld a,[wMapViewVRAMPointer] + sub $40 + ld [wMapViewVRAMPointer],a + jr nc,.adjustXCoordWithinBlock + ld a,[wMapViewVRAMPointer + 1] + dec a + and $03 + or $98 + ld [wMapViewVRAMPointer + 1],a +.adjustXCoordWithinBlock + ld a,c + and a + jr z,.pointlessJump ; mistake? +.pointlessJump + ld hl,wXBlockCoord + ld a,[hl] + add c + ld [hl],a + cp $02 + jr nz,.checkForMoveToWestBlock +; moved into the tile block to the east + xor a + ld [hl],a + ld hl,wXOffsetSinceLastSpecialWarp + inc [hl] + ld de,wCurrentTileBlockMapViewPointer + call MoveTileBlockMapPointerEast + jr .updateMapView +.checkForMoveToWestBlock + cp a,$ff + jr nz,.adjustYCoordWithinBlock +; moved into the tile block to the west + ld a,$1 + ld [hl],a + ld hl,wXOffsetSinceLastSpecialWarp + dec [hl] + ld de,wCurrentTileBlockMapViewPointer + call MoveTileBlockMapPointerWest + jr .updateMapView +.adjustYCoordWithinBlock + ld hl,wYBlockCoord + ld a,[hl] + add b + ld [hl],a + cp $2 + jr nz,.checkForMoveToNorthBlock +; moved into the tile block to the south + xor a + ld [hl],a + ld hl,wYOffsetSinceLastSpecialWarp + inc [hl] + ld de,wCurrentTileBlockMapViewPointer + ld a,[wCurMapWidth] + call MoveTileBlockMapPointerSouth + jr .updateMapView +.checkForMoveToNorthBlock + cp a,$ff + jr nz,.updateMapView +; moved into the tile block to the north + ld a,$1 + ld [hl],a + ld hl,wYOffsetSinceLastSpecialWarp + dec [hl] + ld de,wCurrentTileBlockMapViewPointer + ld a,[wCurMapWidth] + call MoveTileBlockMapPointerNorth +.updateMapView + call LoadCurrentMapView + ld a,[wSpriteStateData1 + 3] ; delta Y + cp $1 + jr nz,.checkIfMovingNorth2 +; if moving south + call ScheduleSouthRowRedraw + jr .scrollBackgroundAndSprites +.checkIfMovingNorth2 + cp $ff + jr nz,.checkIfMovingEast2 +; if moving north + call ScheduleNorthRowRedraw + jr .scrollBackgroundAndSprites +.checkIfMovingEast2 + ld a,[wSpriteStateData1 + 5] ; delta X + cp $1 + jr nz,.checkIfMovingWest2 +; if moving east + call ScheduleEastColumnRedraw + jr .scrollBackgroundAndSprites +.checkIfMovingWest2 + cp $ff + jr nz,.scrollBackgroundAndSprites +; if moving west + call ScheduleWestColumnRedraw +.scrollBackgroundAndSprites + ld a,[wSpriteStateData1 + 3] ; delta Y + add a + ld b,a + ld a,[wSpriteStateData1 + 5] ; delta X + add a + ld c,a +; shift all the sprites in the direction opposite of the player's motion +; so that the player appears to move relative to them + ld hl,wSpriteStateData1 + $14 + ld e,15 +.spriteShiftLoop + ld a,[hl] + sub b + ld [hli],a + inc l + ld a,[hl] + sub c + ld [hl],a + ld a,$0e + add l + ld l,a + dec e + jr nz,.spriteShiftLoop +.done + ld a,[hSCY] + add b + ld [hSCY],a ; update background scroll Y + ld a,[hSCX] + add c + ld [hSCX],a ; update background scroll X + ret + +MoveTileBlockMapPointerEast:: + ld a,[de] + add $1 + ld [de],a + ret nc + inc de + ld a,[de] + inc a + ld [de],a + ret + +MoveTileBlockMapPointerWest:: + ld a,[de] + sub $1 + ld [de],a + ret nc + inc de + ld a,[de] + dec a + ld [de],a + ret + +MoveTileBlockMapPointerSouth:: + add $6 + ld b,a + ld a,[de] + add b + ld [de],a + ret nc + inc de + ld a,[de] + inc a + ld [de],a + ret + +MoveTileBlockMapPointerNorth:: + add $6 + ld b,a + ld a,[de] + sub b + ld [de],a + ret nc + inc de + ld a,[de] + dec a + ld [de],a + ret diff --git a/engine/overworld/boulders.asm b/engine/overworld/boulders.asm new file mode 100644 index 00000000..669b7b83 --- /dev/null +++ b/engine/overworld/boulders.asm @@ -0,0 +1,94 @@ +CheckForCollisionWhenPushingBoulder: + call GetTileTwoStepsInFrontOfPlayer + call IsTilePassable + jr c, .done + ld hl, TilePairCollisionsLand + call CheckForTilePairCollisions2 + ld a, $ff + jr c, .done ; if there is an elevation difference between the current tile and the one two steps ahead + ld a, [wTileInFrontOfBoulderAndBoulderCollisionResult] + cp $15 ; stairs tile + ld a, $ff + jr z, .done ; if the tile two steps ahead is stairs + call CheckForBoulderCollisionWithSprites +.done + ld [wTileInFrontOfBoulderAndBoulderCollisionResult], a + ret + +; sets a to $ff if there is a collision and $00 if there is no collision +CheckForBoulderCollisionWithSprites: + ld a, [wBoulderSpriteIndex] + dec a + swap a + ld d, 0 + ld e, a + ld hl, wSpriteStateData2 + $14 + add hl, de + ld a, [hli] ; map Y position + ld [$ffdc], a + ld a, [hl] ; map X position + ld [$ffdd], a + ld a, [wNumSprites] + ld c, a + ld de, $f + ld hl, wSpriteStateData2 + $14 + ld a, [$ffdb] + and $3 ; facing up or down? + jr z, .pushingHorizontallyLoop +.pushingVerticallyLoop + inc hl + ld a, [$ffdd] + cp [hl] + jr nz, .nextSprite1 ; if X coordinates don't match + dec hl + ld a, [hli] + ld b, a + ld a, [$ffdb] + rrca + jr c, .pushingDown +; pushing up + ld a, [$ffdc] + dec a + jr .compareYCoords +.pushingDown + ld a, [$ffdc] + inc a +.compareYCoords + cp b + jr z, .failure +.nextSprite1 + dec c + jr z, .success + add hl, de + jr .pushingVerticallyLoop +.pushingHorizontallyLoop + ld a, [hli] + ld b, a + ld a, [$ffdc] + cp b + jr nz, .nextSprite2 + ld b, [hl] + ld a, [$ffdb] + bit 2, a + jr nz, .pushingLeft +; pushing right + ld a, [$ffdd] + inc a + jr .compareXCoords +.pushingLeft + ld a, [$ffdd] + dec a +.compareXCoords + cp b + jr z, .failure +.nextSprite2 + dec c + jr z, .success + add hl, de + jr .pushingHorizontallyLoop +.failure + ld a, $ff + ret +.success + xor a + ret diff --git a/engine/overworld/cable_club_npc.asm b/engine/overworld/cable_club_npc.asm index 70b499a0..e3ce8e8d 100755 --- a/engine/overworld/cable_club_npc.asm +++ b/engine/overworld/cable_club_npc.asm @@ -1,9 +1,12 @@ CableClubNPC: ld hl, CableClubNPCWelcomeText call PrintText + call CheckPikachuFollowingPlayer + jr nz, .asm_7048 CheckEvent EVENT_GOT_POKEDEX jp nz, .receivedPokedex ; if the player hasn't received the pokedex +.asm_7048 ld c, 60 call DelayFrames ld hl, CableClubNPCMakingPreparationsText @@ -107,7 +110,61 @@ CableClubNPC: xor a ld [hld], a ld [hl], a - jpab LinkMenu + ld a, [wLetterPrintingDelayFlags] + push af + callab LinkMenu + pop af + ld [wLetterPrintingDelayFlags], a + ret + +; seems to be similar of Serial_SyncAndExchangeNybble +Serial_SyncAndExchangeNybbleDouble: + ld a, $ff + ld [wSerialExchangeNybbleReceiveData], a +.loop + call Serial_ExchangeNybble + call DelayFrame + push hl + ld hl, wUnknownSerialCounter + 1 + dec [hl] + jr nz, .next + dec hl + dec [hl] + jr nz, .next + pop hl + jr .setUnknownSerialCounterToFFFF +.next + pop hl + ld a, [wSerialExchangeNybbleReceiveData] + inc a + jr z, .loop + call DelayFrame + ld a, $ff + ld [wSerialExchangeNybbleReceiveData], a + call Serial_ExchangeNybble + ld a, [wSerialExchangeNybbleReceiveData] + inc a + jr z, .loop + ld b, 10 +.syncLoop1 + call DelayFrame + call Serial_ExchangeNybble + dec b + jr nz, .syncLoop1 + ld b, 10 +.syncLoop2 + call DelayFrame + call Serial_SendZeroByte + dec b + jr nz, .syncLoop2 + ld a, [wSerialExchangeNybbleReceiveData] + ld [wSerialSyncAndExchangeNybbleReceiveData], a + ret +.setUnknownSerialCounterToFFFF + ld a, $ff + ld [wUnknownSerialCounter], a + ld [wUnknownSerialCounter + 1], a + ret CableClubNPCAreaReservedFor2FriendsLinkedByCableText: TX_FAR _CableClubNPCAreaReservedFor2FriendsLinkedByCableText diff --git a/engine/overworld/card_key.asm b/engine/overworld/card_key.asm index 61e512de..e1fc9160 100755 --- a/engine/overworld/card_key.asm +++ b/engine/overworld/card_key.asm @@ -8,7 +8,8 @@ PrintCardKeyText: ret z cp b jr nz, .silphCoMapListLoop - predef GetTileAndCoordsInFrontOfPlayer +; does not check for tile in front of player. This might be buggy + ;predef GetTileAndCoordsInFrontOfPlayer ld a, [wTileInFrontOfPlayer] cp $18 jr z, .cardKeyDoorInFrontOfPlayer @@ -25,12 +26,12 @@ PrintCardKeyText: ld b, CARD_KEY call IsItemInBag jr z, .noCardKey - call GetCoordsInFrontOfPlayer - push de + xor a + ld [wPlayerMovingDirection], a tx_pre_id CardKeySuccessText ld [hSpriteIndexOrTextID], a call PrintPredefTextID - pop de + call GetCoordsInFrontOfPlayer srl d ld a, d ld b, a @@ -73,7 +74,7 @@ SilphCoMapList: CardKeySuccessText: TX_FAR _CardKeySuccessText1 - db $0b + TX_SFX_ITEM TX_FAR _CardKeySuccessText2 db "@" @@ -88,7 +89,7 @@ GetCoordsInFrontOfPlayer: ld d, a ld a, [wXCoord] ld e, a - ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction + ld a, [wPlayerFacingDirection] ; player's sprite facing direction and a jr nz, .notFacingDown ; facing down diff --git a/engine/overworld/check_player_state.asm b/engine/overworld/check_player_state.asm new file mode 100644 index 00000000..5fad4fc5 --- /dev/null +++ b/engine/overworld/check_player_state.asm @@ -0,0 +1,236 @@ +; only used for setting bit 2 of wd736 upon entering a new map +IsPlayerStandingOnWarp: + ld a, [wNumberOfWarps] + and a + ret z + ld c, a + ld hl, wWarpEntries +.loop + ld a, [wYCoord] + cp [hl] + jr nz, .nextWarp1 + inc hl + ld a, [wXCoord] + cp [hl] + jr nz, .nextWarp2 + inc hl + ld a, [hli] ; target warp + ld [wDestinationWarpID], a + ld a, [hl] ; target map + ld [$ff8b], a + ld hl, wd736 + set 2, [hl] ; standing on warp flag + ret +.nextWarp1 + inc hl +.nextWarp2 + inc hl + inc hl + inc hl + dec c + jr nz, .loop + ret + +CheckForceBikeOrSurf: + ld hl, wd732 + bit 5, [hl] + ret nz + ld hl, ForcedBikeOrSurfMaps + ld a, [wYCoord] + ld b, a + ld a, [wXCoord] + ld c, a + ld a, [wCurMap] + ld d, a +.loop + ld a, [hli] + cp $ff + ret z ;if we reach FF then it's not part of the list + cp d ;compare to current map + jr nz, .incorrectMap + ld a, [hli] + cp b ;compare y-coord + jr nz, .incorrectY + ld a, [hli] + cp c ;compare x-coord + jr nz, .loop ; incorrect x-coord, check next item + ld a, [wCurMap] + cp SEAFOAM_ISLANDS_4 + ld a, $2 + ld [wSeafoamIslands4CurScript], a + jr z, .forceSurfing + ld a, [wCurMap] + cp SEAFOAM_ISLANDS_5 + ld a, $2 + ld [wSeafoamIslands5CurScript], a + jr z, .forceSurfing + ;force bike riding + ld hl, wd732 + set 5, [hl] + ld a, $1 + ld [wWalkBikeSurfState], a + ld [wWalkBikeSurfStateCopy], a + call ForceBikeOrSurf + ret +.incorrectMap + inc hl +.incorrectY + inc hl + jr .loop +.forceSurfing + ld a, $2 + ld [wWalkBikeSurfState], a + ld [wWalkBikeSurfStateCopy], a + call ForceBikeOrSurf + ret + +INCLUDE "data/force_bike_surf.asm" + +IsPlayerFacingEdgeOfMap: + push hl + push de + push bc + ld a, [wPlayerFacingDirection] ; player sprite's facing direction + srl a + ld c, a + ld b, $0 + ld hl, .functionPointerTable + add hl, bc + ld a, [hli] + ld h, [hl] + ld l, a + ld a, [wYCoord] + ld b, a + ld a, [wXCoord] + ld c, a + ld de, .returnaddress + push de + jp [hl] +.returnaddress + pop bc + pop de + pop hl + ret + +.functionPointerTable + dw .facingDown + dw .facingUp + dw .facingLeft + dw .facingRight + +.facingDown + ld a, [wCurMapHeight] + add a + dec a + cp b + jr z, .setCarry + jr .resetCarry + +.facingUp + ld a, b + and a + jr z, .setCarry + jr .resetCarry + +.facingLeft + ld a, c + and a + jr z, .setCarry + jr .resetCarry + +.facingRight + ld a, [wCurMapWidth] + add a + dec a + cp c + jr z, .setCarry + jr .resetCarry +.resetCarry + and a + ret +.setCarry + scf + ret + +IsWarpTileInFrontOfPlayer: + push hl + push de + push bc + call _GetTileAndCoordsInFrontOfPlayer + ld a, [wCurMap] + cp SS_ANNE_5 + jr z, .ssAnne5 + ld a, [wPlayerFacingDirection] ; player sprite's facing direction + srl a + ld c, a + ld b, 0 + ld hl, .warpTileListPointers + add hl, bc + ld a, [hli] + ld h, [hl] + ld l, a + ld a, [wTileInFrontOfPlayer] + ld de, $1 + call IsInArray +.done + pop bc + pop de + pop hl + ret + +.warpTileListPointers: + dw .facingDownWarpTiles + dw .facingUpWarpTiles + dw .facingLeftWarpTiles + dw .facingRightWarpTiles + +.facingDownWarpTiles + db $01,$12,$17,$3D,$04,$18,$33,$FF + +.facingUpWarpTiles + db $01,$5C,$FF + +.facingLeftWarpTiles + db $1A,$4B,$FF + +.facingRightWarpTiles + db $0F,$4E,$FF + +.ssAnne5 + ld a, [wTileInFrontOfPlayer] + cp $15 + jr nz, .notSSAnne5Warp + scf + jr .done +.notSSAnne5Warp + and a + jr .done + +IsPlayerStandingOnDoorTileOrWarpTile: + push hl + push de + push bc + callba IsPlayerStandingOnDoorTile ; 6:6785 + jr c, .done + ld a, [wCurMapTileset] + add a + ld c, a + ld b, $0 + ld hl, WarpTileIDPointers + add hl, bc + ld a, [hli] + ld h, [hl] + ld l, a + ld de, $1 + aCoord 8, 9 + call IsInArray + jr nc, .done + ld hl, wd736 + res 2, [hl] +.done + pop bc + pop de + pop hl + ret + +INCLUDE "data/warp_tile_ids.asm" diff --git a/engine/overworld/clear_loadmapdata_vars.asm b/engine/overworld/clear_loadmapdata_vars.asm new file mode 100644 index 00000000..c5dc21fa --- /dev/null +++ b/engine/overworld/clear_loadmapdata_vars.asm @@ -0,0 +1,20 @@ +ClearVariablesAfterLoadingMapData: + ld a, $90 + ld [hWY], a + ld [rWY], a + xor a + ld [H_AUTOBGTRANSFERENABLED], a + ld [wStepCounter], a + ld [wLoneAttackNo], a ; wGymLeaderNo + ld [hJoyPressed], a + ld [hJoyReleased], a + ld [hJoyHeld], a + ld [wActionResultOrTookBattleTurn], a + ld [wUnusedD5A3], a + ld hl, wCardKeyDoorY + ld [hli], a + ld [hl], a + ld hl, wUnusedCD3D + ld bc, wStandingOnWarpPadOrHole - wUnusedCD3D + call FillMemory + ret diff --git a/engine/overworld/cut.asm b/engine/overworld/cut.asm index 2f13dfba..462e3e8e 100755 --- a/engine/overworld/cut.asm +++ b/engine/overworld/cut.asm @@ -74,8 +74,9 @@ UsedCutText: InitCutAnimOAM: xor a ld [wWhichAnimationOffsets], a - ld a, $e4 + ld a, %11100100 ld [rOBP1], a + call UpdateGBCPal_OBP1 ld a, [wCutTile] cp $52 jr z, .grass @@ -123,8 +124,8 @@ WriteCutOrBoulderDustAnimationOAMBlock: jp WriteOAMBlock CutOrBoulderDustAnimationTilesAndAttributes: - db $FC,$10,$FD,$10 - db $FE,$10,$FF,$10 + db $FC,$14,$FD,$14 + db $FE,$14,$FF,$14 GetCutOrBoulderDustAnimationOffsets: ld hl, wSpriteStateData1 + 4 @@ -187,7 +188,7 @@ ReplaceTreeTileBlock: ld h, [hl] ld l, a add hl, bc - ld a, [wSpriteStateData1 + 9] ; player sprite's facing direction + ld a, [wPlayerFacingDirection] ; player sprite's facing direction and a jr z, .down cp SPRITE_FACING_UP diff --git a/engine/overworld/cut2.asm b/engine/overworld/cut2.asm index f16fed66..37490f95 100755 --- a/engine/overworld/cut2.asm +++ b/engine/overworld/cut2.asm @@ -18,6 +18,7 @@ AnimCut: ld a, [rOBP1] xor $64 ld [rOBP1], a + call UpdateGBCPal_OBP1 call DelayFrame pop bc dec c @@ -68,6 +69,7 @@ AnimCutGrass_UpdateOAMEntries: ld a, [rOBP1] xor $64 ld [rOBP1], a + call UpdateGBCPal_OBP1 call DelayFrame pop bc dec c diff --git a/engine/overworld/daycare_exp.asm b/engine/overworld/daycare_exp.asm new file mode 100644 index 00000000..dbe4023a --- /dev/null +++ b/engine/overworld/daycare_exp.asm @@ -0,0 +1,18 @@ +IncrementDayCareMonExp: + ld a, [wDayCareInUse] + and a + ret z + ld hl, wDayCareMonExp + 2 + inc [hl] + ret nz + dec hl + inc [hl] + ret nz + dec hl + inc [hl] + ld a, [hl] + cp $50 + ret c + ld a, $50 + ld [hl], a + ret diff --git a/engine/overworld/doors.asm b/engine/overworld/doors.asm index c39e096d..8bde8600 100755 --- a/engine/overworld/doors.asm +++ b/engine/overworld/doors.asm @@ -39,6 +39,7 @@ DoorTileIDPointers: dbw LAB, LabDoorTileIDs dbw FACILITY, FacilityDoorTileIDs dbw PLATEAU, PlateauDoorTileIDs + dbw INTERIOR, InteriorDoorTileIDs db $ff OverworldDoorTileIDs: @@ -73,3 +74,6 @@ FacilityDoorTileIDs: PlateauDoorTileIDs: db $3b,$1b,$00 + +InteriorDoorTileIDs: + db $04,$15,$00 diff --git a/engine/overworld/dungeon_warps.asm b/engine/overworld/dungeon_warps.asm new file mode 100644 index 00000000..f47dfb01 --- /dev/null +++ b/engine/overworld/dungeon_warps.asm @@ -0,0 +1,15 @@ +IsPlayerOnDungeonWarp: + xor a + ld [wWhichDungeonWarp], a + ld a, [wd72d] + bit 4, a + ret nz + call ArePlayerCoordsInArray + ret nc + ld a, [wCoordIndex] + ld [wWhichDungeonWarp], a + ld hl, wd72d + set 4, [hl] + ld hl, wd732 + set 4, [hl] + ret diff --git a/engine/overworld/elevator.asm b/engine/overworld/elevator.asm index 4ec34922..cd7bf5ba 100755 --- a/engine/overworld/elevator.asm +++ b/engine/overworld/elevator.asm @@ -4,8 +4,7 @@ ShakeElevator: ld de, SCREEN_HEIGHT * $20 call ShakeElevatorRedrawRow call Delay3 - ld a, $ff - call PlaySound + call StopAllMusic ld a, [hSCY] ld d, a ld e, $1 @@ -27,14 +26,13 @@ ShakeElevator: jr nz, .shakeLoop ld a, d ld [hSCY], a - ld a, $ff - call PlaySound + call StopAllMusic ld c, BANK(SFX_Safari_Zone_PA) ld a, SFX_SAFARI_ZONE_PA call PlayMusic .musicLoop ld a, [wChannelSoundIDs + CH4] - cp $b9 + cp SFX_SAFARI_ZONE_PA jr z, .musicLoop call UpdateSprites jp PlayDefaultMusic @@ -56,7 +54,7 @@ ShakeElevatorRedrawRow: add hl, de ld a, h and $3 - or $98 + or vBGMap0 / $100 ld d, a ld a, l pop hl diff --git a/engine/overworld/emotion_bubbles.asm b/engine/overworld/emotion_bubbles.asm index 4df8b6f6..7c7f5e7d 100755 --- a/engine/overworld/emotion_bubbles.asm +++ b/engine/overworld/emotion_bubbles.asm @@ -1,13 +1,16 @@ EmotionBubble: ld a, [wWhichEmotionBubble] + and $f + swap a ld c, a - ld b, 0 - ld hl, EmotionBubblesPointerTable + ld b, $0 + ld hl, EmotionBubbles + add hl, bc ; each emotion bubble is 16 bytes, so calculate the offset directly instead of with a pointer table add hl, bc add hl, bc - ld e, [hl] - inc hl - ld d, [hl] + add hl, bc + ld e, l + ld d, h ld hl, vChars1 + $780 lb bc, BANK(EmotionBubbles), $04 call CopyVideoData @@ -17,11 +20,11 @@ EmotionBubble: ld [wUpdateSpritesEnabled], a ld a, [wd736] bit 6, a ; are the last 4 OAM entries reserved for a shadow or fishing rod? - ld hl, wOAMBuffer + $8f - ld de, wOAMBuffer + $9f + ld hl, wOAMBuffer + 4 * 35 + $3 ; $8f + ld de, wOAMBuffer + 4 * 39 + $3 ; $9f jr z, .next - ld hl, wOAMBuffer + $7f - ld de, wOAMBuffer + $8f + ld hl, wOAMBuffer + 4 * 31 + $3 ; $7f + ld de, wOAMBuffer + 4 * 35 + $3 ; $8f ; Copy OAM data 16 bytes forward to make room for emotion bubble OAM data at the ; start of the OAM buffer. @@ -59,12 +62,9 @@ EmotionBubble: pop af ld [wUpdateSpritesEnabled], a call DelayFrame - jp UpdateSprites - -EmotionBubblesPointerTable: - dw EmotionBubbles - dw EmotionBubbles + $40 - dw EmotionBubbles + $80 + call UpdateSprites + ret + ; jp UpdateSprites EmotionBubblesOAM: db $F8,$00,$F9,$00 diff --git a/engine/overworld/get_coords_tile_in_front_of_player.asm b/engine/overworld/get_coords_tile_in_front_of_player.asm new file mode 100644 index 00000000..e8bbc660 --- /dev/null +++ b/engine/overworld/get_coords_tile_in_front_of_player.asm @@ -0,0 +1,87 @@ +GetTileAndCoordsInFrontOfPlayer: + call GetPredefRegisters + +_GetTileAndCoordsInFrontOfPlayer: + ld a, [wYCoord] + ld d, a + ld a, [wXCoord] + ld e, a + ld a, [wPlayerFacingDirection] ; player's sprite facing direction + and a ; cp SPRITE_FACING_DOWN + jr nz, .notFacingDown +; facing down + aCoord 8, 11 + inc d + jr .storeTile +.notFacingDown + cp SPRITE_FACING_UP + jr nz, .notFacingUp +; facing up + aCoord 8, 7 + dec d + jr .storeTile +.notFacingUp + cp SPRITE_FACING_LEFT + jr nz, .notFacingLeft +; facing left + aCoord 6, 9 + dec e + jr .storeTile +.notFacingLeft + cp SPRITE_FACING_RIGHT + jr nz, .storeTile +; facing right + aCoord 10, 9 + inc e +.storeTile + ld c, a + ld [wTileInFrontOfPlayer], a + ret + +GetTileTwoStepsInFrontOfPlayer: + xor a + ld [$ffdb], a + ld hl, wYCoord + ld a, [hli] + ld d, a + ld e, [hl] + ld a, [wPlayerFacingDirection] ; player's sprite facing direction + and a ; cp SPRITE_FACING_DOWN + jr nz, .notFacingDown +; facing down + ld hl, $ffdb + set 0, [hl] + aCoord 8, 13 + inc d + jr .storeTile +.notFacingDown + cp SPRITE_FACING_UP + jr nz, .notFacingUp +; facing up + ld hl, $ffdb + set 1, [hl] + aCoord 8, 5 + dec d + jr .storeTile +.notFacingUp + cp SPRITE_FACING_LEFT + jr nz, .notFacingLeft +; facing left + ld hl, $ffdb + set 2, [hl] + aCoord 4, 9 + dec e + jr .storeTile +.notFacingLeft + cp SPRITE_FACING_RIGHT + jr nz, .storeTile +; facing right + ld hl, $ffdb + set 3, [hl] + aCoord 12, 9 + inc e +.storeTile + ld c, a + ld [wTileInFrontOfBoulderAndBoulderCollisionResult], a + ld [wTileInFrontOfPlayer], a + ret diff --git a/engine/overworld/healing_machine.asm b/engine/overworld/healing_machine.asm index 38a44cfb..1dc74e2d 100755 --- a/engine/overworld/healing_machine.asm +++ b/engine/overworld/healing_machine.asm @@ -1,5 +1,5 @@ AnimateHealingMachine: - ld de, PokeCenterFlashingMonitorAndHealBall + ld de, PokeCenterFlashingMonitorAndHealBall ; $44b7 ld hl, vChars0 + $7c0 lb bc, BANK(PokeCenterFlashingMonitorAndHealBall), $03 ; loads one too many tiles call CopyVideoData @@ -11,52 +11,50 @@ AnimateHealingMachine: ld a, [rOBP1] push af ld a, $e0 - ld [rOBP1], a + ld [rOBP1], a ; $ff49 + call UpdateGBCPal_OBP1 ld hl, wOAMBuffer + $84 - ld de, PokeCenterOAMData + ld de, PokeCenterOAMData ; $44d7 call CopyHealingMachineOAM ld a, 4 ld [wAudioFadeOutControl], a - ld a, $ff - ld [wNewSoundID], a - call PlaySound + call StopAllMusic .waitLoop ld a, [wAudioFadeOutControl] - and a ; is fade-out finished? - jr nz, .waitLoop ; if not, check again - ld a, [wPartyCount] + and a + jr nz, .waitLoop + ld a, [wPartyCount] ; wPartyCount ld b, a .partyLoop call CopyHealingMachineOAM - ld a, SFX_HEALING_MACHINE + ld a, $9e ; (SFX_02_4a - SFX_Headers_02) / 3 call PlaySound ld c, 30 call DelayFrames dec b jr nz, .partyLoop ld a, [wAudioROMBank] - cp BANK(Audio3_UpdateMusic) + cp $1f ld [wAudioSavedROMBank], a jr nz, .next - ld a, $ff - ld [wNewSoundID], a - call PlaySound - ld a, BANK(Music_PkmnHealed) + call StopAllMusic + ld a, $2 ; BANK(Music_PkmnHealed) ld [wAudioROMBank], a .next - ld a, MUSIC_PKMN_HEALED + ld a, $e8 ; MUSIC_PKMN_HEALED ld [wNewSoundID], a call PlaySound ld d, $28 call FlashSprite8Times .waitLoop2 ld a, [wChannelSoundIDs] - cp MUSIC_PKMN_HEALED ; is the healed music still playing? - jr z, .waitLoop2 ; if so, check gain + cp $e8 ; MUSIC_PKMN_HEALED + jr z, .waitLoop2 ld c, 32 call DelayFrames pop af - ld [rOBP1], a + ld [rOBP1], a ; $ff49 + call UpdateGBCPal_OBP1 pop hl pop af ld [hl], a @@ -66,13 +64,13 @@ PokeCenterFlashingMonitorAndHealBall: INCBIN "gfx/pokecenter_ball.2bpp" PokeCenterOAMData: - db $24,$34,$7C,$10 ; heal machine monitor - db $2B,$30,$7D,$10 ; pokeballs 1-6 - db $2B,$38,$7D,$30 - db $30,$30,$7D,$10 - db $30,$38,$7D,$30 - db $35,$30,$7D,$10 - db $35,$38,$7D,$30 + db $24,$34,$7C,$14 ; heal machine monitor + db $2B,$30,$7D,$14 ; pokeballs 1-6 + db $2B,$38,$7D,$34 + db $30,$30,$7D,$14 + db $30,$38,$7D,$34 + db $35,$30,$7D,$14 + db $35,$38,$7D,$34 ; d = value to xor with palette FlashSprite8Times: @@ -81,6 +79,7 @@ FlashSprite8Times: ld a, [rOBP1] xor d ld [rOBP1], a + call UpdateGBCPal_OBP1 ld c, 10 call DelayFrames dec b diff --git a/engine/overworld/hidden_items.asm b/engine/overworld/hidden_items.asm index 11e6ad55..15082847 100755 --- a/engine/overworld/hidden_items.asm +++ b/engine/overworld/hidden_items.asm @@ -9,7 +9,7 @@ HiddenItems: predef FlagActionPredef ld a, c and a - ret nz + jr nz, .itemAlreadyFound call EnableAutoTextBoxDrawing ld a, 1 ld [wDoNotWaitForButtonPressAfterDisplayingText], a @@ -18,6 +18,11 @@ HiddenItems: call GetItemName tx_pre_jump FoundHiddenItemText +.itemAlreadyFound + ld a, $ff + ld [hItemAlreadyFound], a + ret + INCLUDE "data/hidden_item_coords.asm" FoundHiddenItemText: @@ -27,7 +32,7 @@ FoundHiddenItemText: ld b, a ld c, 1 call GiveItem - jr nc, .BagFull + jr nc, .bagFull ld hl, wObtainedHiddenItemsFlags ld a, [wHiddenItemOrCoinsIndex] ld c, a @@ -37,7 +42,7 @@ FoundHiddenItemText: call PlaySoundWaitForCurrent call WaitForSoundToFinish jp TextScriptEnd -.BagFull +.bagFull call WaitForTextScrollButtonPress ; wait for button press xor a ld [wDoNotWaitForButtonPressAfterDisplayingText], a @@ -54,7 +59,7 @@ HiddenCoins: predef GetQuantityOfItemInBag ld a, b and a - ret z + jr z, .doNotPickUpCoins ld hl, HiddenCoinCoords call FindHiddenItemOrCoinsIndex ld [wHiddenItemOrCoinsIndex], a @@ -65,7 +70,7 @@ HiddenCoins: predef FlagActionPredef ld a, c and a - ret nz + jr nz, .doNotPickUpCoins xor a ld [hUnusedCoinsByte], a ld [hCoins], a @@ -77,24 +82,30 @@ HiddenCoins: cp 20 jr z, .bcd20 cp 40 - jr z, .bcd20 + jr z, .bcd20 ; should be bcd40 jr .bcd100 + +.doNotPickUpCoins + ld a, $ff + ld [hItemAlreadyFound], a + ret + .bcd10 ld a, $10 ld [hCoins + 1], a - jr .bcddone + jr .bcdDone .bcd20 ld a, $20 ld [hCoins + 1], a - jr .bcddone + jr .bcdDone .bcd40 ; due to a typo, this is never used ld a, $40 ld [hCoins + 1], a - jr .bcddone + jr .bcdDone .bcd100 ld a, $1 ld [hCoins], a -.bcddone +.bcdDone ld de, wPlayerCoins + 1 ld hl, hCoins + 1 ld c, $2 @@ -107,13 +118,13 @@ HiddenCoins: call EnableAutoTextBoxDrawing ld a, [wPlayerCoins] cp $99 - jr nz, .RoomInCoinCase + jr nz, .roomInCoinCase ld a, [wPlayerCoins + 1] cp $99 - jr nz, .RoomInCoinCase + jr nz, .roomInCoinCase tx_pre_id DroppedHiddenCoinsText jr .done -.RoomInCoinCase +.roomInCoinCase tx_pre_id FoundHiddenCoinsText .done jp PrintPredefTextID diff --git a/engine/overworld/hidden_objects.asm b/engine/overworld/hidden_objects.asm index dcdf8537..9a81dcfc 100755 --- a/engine/overworld/hidden_objects.asm +++ b/engine/overworld/hidden_objects.asm @@ -1,43 +1,17 @@ -IsPlayerOnDungeonWarp: - xor a - ld [wWhichDungeonWarp], a - ld a, [wd72d] - bit 4, a - ret nz - call ArePlayerCoordsInArray - ret nc - ld a, [wCoordIndex] - ld [wWhichDungeonWarp], a - ld hl, wd72d - set 4, [hl] - ld hl, wd732 - set 4, [hl] - ret - -; if a hidden object was found, stores $00 in [$ffee], else stores $ff +; if a hidden object was found, stores $00 in [hDidntFindAnyHiddenObject], else stores $ff CheckForHiddenObject: - ld hl, $ffeb + ld hl, hItemAlreadyFound xor a ld [hli], a ld [hli], a ld [hli], a ld [hl], a - ld de, $0 ld hl, HiddenObjectMaps -.hiddenMapLoop - ld a, [hli] - ld b, a - cp $ff - jr z, .noMatch + ld de, 3 ld a, [wCurMap] - cp b - jr z, .foundMatchingMap - inc de - inc de - jr .hiddenMapLoop -.foundMatchingMap - ld hl, HiddenObjectPointers - add hl, de + call IsInArray + jr nc, .noMatch + inc hl ld a, [hli] ld h, [hl] ld l, a @@ -81,13 +55,13 @@ CheckForHiddenObject: ret .noMatch ld a, $ff - ld [$ffee], a + ld [hDidntFindAnyHiddenObject], a ret ; checks if the coordinates in front of the player's sprite match Y in b and X in c ; [hCoordsInFrontOfPlayerMatch] = $00 if they match, $ff if they don't match CheckIfCoordsInFrontOfPlayerMatch: - ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction + ld a, [wPlayerFacingDirection] ; player's sprite facing direction cp SPRITE_FACING_UP jr z, .facingUp cp SPRITE_FACING_LEFT diff --git a/engine/overworld/ledges.asm b/engine/overworld/ledges.asm index 342540b2..e7874637 100755 --- a/engine/overworld/ledges.asm +++ b/engine/overworld/ledges.asm @@ -6,7 +6,7 @@ HandleLedges: and a ; OVERWORLD ret nz predef GetTileAndCoordsInFrontOfPlayer - ld a, [wSpriteStateData1 + 9] + ld a, [wPlayerFacingDirection] ld b, a aCoord 8, 9 ld c, a @@ -71,10 +71,13 @@ LoadHoppingShadowOAM: ld de, LedgeHoppingShadow lb bc, BANK(LedgeHoppingShadow), (LedgeHoppingShadowEnd - LedgeHoppingShadow) / $8 call CopyVideoDataDouble - ld a, $9 - lb bc, $54, $48 ; b, c = y, x coordinates of shadow - ld de, LedgeHoppingShadowOAM - call WriteOAMBlock + ld hl, LedgeHoppingShadowOAM + ld de, wOAMBuffer + 36 * 4 + ld bc, LedgeHoppingShadowOAMEnd - LedgeHoppingShadowOAM + call CopyData + ld a, $a0 + ld [wOAMBuffer + 38 * 4], a + ld [wOAMBuffer + 39 * 4], a ret LedgeHoppingShadow: @@ -82,5 +85,6 @@ LedgeHoppingShadow: LedgeHoppingShadowEnd: LedgeHoppingShadowOAM: - db $FF,$10,$FF,$20 - db $FF,$40,$FF,$60 + db $58,$48,$FF,$00 + db $58,$50,$FF,$20 +LedgeHoppingShadowOAMEnd: diff --git a/engine/overworld/load_tileset_header.asm b/engine/overworld/load_tileset_header.asm new file mode 100644 index 00000000..05061651 --- /dev/null +++ b/engine/overworld/load_tileset_header.asm @@ -0,0 +1,51 @@ +LoadTilesetHeader: + call GetPredefRegisters + push hl + ld d, 0 + ld a, [wCurMapTileset] + add a + add a + ld e, a + ld hl, Tilesets + add hl, de + add hl, de + add hl, de + ld de, wTilesetBank + ld bc, $b + call CopyData + ld a, [hl] + ld [hTilesetType], a + xor a + ld [$ffd8], a + pop hl + ld a, [wCurMapTileset] + push hl + push de + ld hl, DungeonTilesets + ld de, $1 + call IsInArray + pop de + pop hl + jr c, .notDungeonTileset + ld a, [wCurMapTileset] + ld b, a + ld a, [hPreviousTileset] + cp b + jr z, .done +.notDungeonTileset + ld a, [wDestinationWarpID] + cp $ff + jr z, .done + call LoadDestinationWarpPosition + ld a, [wYCoord] + and $1 + ld [wYBlockCoord], a + ld a, [wXCoord] + and $1 + ld [wXBlockCoord], a +.done + ret + +INCLUDE "data/dungeon_tilesets.asm" + +INCLUDE "data/tileset_headers.asm" diff --git a/engine/overworld/load_wild_data.asm b/engine/overworld/load_wild_data.asm new file mode 100644 index 00000000..6444ab7e --- /dev/null +++ b/engine/overworld/load_wild_data.asm @@ -0,0 +1,33 @@ +LoadWildData: + ld hl,WildDataPointers + ld a,[wCurMap] + + ; get wild data for current map + ld c,a + ld b,0 + add hl,bc + add hl,bc + ld a,[hli] + ld h,[hl] + ld l,a ; hl now points to wild data for current map + ld a,[hli] + ld [wGrassRate],a + and a + jr z,.NoGrassData ; if no grass data, skip to surfing data + push hl + ld de,wGrassMons ; otherwise, load grass data + ld bc,$0014 + call CopyData + pop hl + ld bc,$0014 + add hl,bc +.NoGrassData + ld a,[hli] + ld [wWaterRate],a + and a + ret z ; if no water data, we're done + ld de,wWaterMons ; otherwise, load surfing data + ld bc,$0014 + jp CopyData + +INCLUDE "data/wild_mons.asm" diff --git a/engine/overworld/map_sprite_functions1.asm b/engine/overworld/map_sprite_functions1.asm new file mode 100644 index 00000000..f0a718bd --- /dev/null +++ b/engine/overworld/map_sprite_functions1.asm @@ -0,0 +1,390 @@ +_UpdateSprites: + ld h, wSpriteStateData1 / $100 + inc h + ld a, $e ; (wSpriteStateData2 + $0e) & $ff +.spriteLoop + ld l, a + sub $e + ld c, a + ld [H_CURRENTSPRITEOFFSET], a + ld a, [hl] + and a + jr z, .skipSprite ; tests $c2Xe + push hl + push de + push bc + call .updateCurrentSprite + pop bc + pop de + pop hl +.skipSprite + ld a, l + add $10 ; move to next sprite + cp $e ; test for overflow (back at $0e) + jr nz, .spriteLoop + ret +.updateCurrentSprite ; 4bd7 (1:4bd7) + ld a, [H_CURRENTSPRITEOFFSET] + and a + jp z, UpdatePlayerSprite + cp $f0 ; pikachu + jp z, SpawnPikachu + ld a, [hl] + +UpdateNonPlayerSprite: + dec a + swap a + ld [$ff93], a ; $10 * sprite# + ld a, [wNPCMovementScriptSpriteOffset] ; some sprite offset? + ld b, a + ld a, [H_CURRENTSPRITEOFFSET] + cp b + jr nz, .unequal + jp DoScriptedNPCMovement +.unequal + jp UpdateNPCSprite + +; This detects if the current sprite (whose offset is at H_CURRENTSPRITEOFFSET) +; is going to collide with another sprite by looping over the other sprites. +; The current sprite's offset will be labelled with i (e.g. $c1i0). +; The loop sprite's offset will labelled with j (e.g. $c1j0). +; +; Note that the Y coordinate of the sprite (in [$c1k4]) is one of the following +; 9 values when the sprite is aligned with the grid: $fc, $0c, $1c, $2c, ..., $7c. +; The reason that 4 is added below to the coordinate is to make it align with a +; multiple of $10 to make comparisons easier. +DetectCollisionBetweenSprites: + ; nop + + ld h, wSpriteStateData1 / $100 + ld a, [H_CURRENTSPRITEOFFSET] + ld l, a + + ld a, [hl] ; a = [$c1i0] (picture) (0 if slot is unused) + and a ; is this sprite slot slot used? + ret z ; return if not used + + ld a, l + add 3 + ld l, a + + ld a, [hli] ; a = [$c1i3] (delta Y) (-1, 0, or 1) + call SetSpriteCollisionValues + + ld a, [hli] ; a = [$C1i4] (Y screen coordinate) + add 4 ; align with multiple of $10 + +; The effect of the following 3 lines is to +; add 7 to a if moving south or +; subtract 7 from a if moving north. + add b + and $f0 + or c + + ld [$ff90], a ; store Y coordinate adjusted for direction of movement + + ld a, [hli] ; a = [$c1i5] (delta X) (-1, 0, or 1) + call SetSpriteCollisionValues + ld a, [hl] ; a = [$C1i6] (X screen coordinate) + +; The effect of the following 3 lines is to +; add 7 to a if moving east or +; subtract 7 from a if moving west. + add b + and $f0 + or c + + ld [$ff91], a ; store X coordinate adjusted for direction of movement + + ld a, l + add 7 + ld l, a + + xor a + ld [hld], a ; zero [$c1id] XXX what's [$c1id] for? + ld [hld], a ; zero [$c1ic] (directions in which collisions occurred) + + ld a, [$ff91] + ld [hld], a ; [$c1ib] = adjusted X coordinate + ld a, [$ff90] + ld [hl], a ; [$c1ia] = adjusted Y coordinate + + xor a ; zero the loop counter + +.loop + ld [$ff8f], a ; store loop counter + swap a + ld e, a + ld a, [H_CURRENTSPRITEOFFSET] + cp e ; does the loop sprite match the current sprite? + jp z, .next ; go to the next sprite if they match + + ld d, h + ld a, [de] ; a = [$c1j0] (picture) (0 if slot is unused) + and a ; is this sprite slot slot used? + jp z, .next ; go the next sprite if not used + + inc e + inc e + ld a, [de] ; a = [$c1j2] ($ff means the sprite is offscreen) + inc a + jp z, .next ; go the next sprite if offscreen + + ld a, [H_CURRENTSPRITEOFFSET] + add 10 + ld l, a + + inc e + ld a, [de] ; a = [$c1j3] (delta Y) + call SetSpriteCollisionValues + + inc e + ld a, [de] ; a = [$C1j4] (Y screen coordinate) + add 4 ; align with multiple of $10 + +; The effect of the following 3 lines is to +; add 7 to a if moving south or +; subtract 7 from a if moving north. + add b + and $f0 + or c + + sub [hl] ; subtract the adjusted Y coordinate of sprite i ([$c1ia]) from that of sprite j + +; calculate the absolute value of the difference to get the distance + jr nc, .noCarry1 + cpl + inc a +.noCarry1 + ld [$ff90], a ; store the distance between the two sprites' adjusted Y values + +; Use the carry flag set by the above subtraction to determine which sprite's +; Y coordinate is larger. This information is used later to set [$c1ic], +; which stores which direction the collision occurred in. +; The following 5 lines set the lowest 2 bits of c, which are later shifted left by 2. +; If sprite i's Y is larger, set lowest 2 bits of c to 10. +; If sprite j's Y is larger or both are equal, set lowest 2 bits of c to 01. + push af + rl c + pop af + ccf + rl c + +; If sprite i's delta Y is 0, then b = 7, else b = 9. + ld b, 7 + ld a, [hl] ; a = [$c1ia] (adjusted Y coordinate) + and $f + jr z, .next1 + ld b, 9 + +.next1 + ld a, [$ff90] ; a = distance between adjusted Y coordinates + sub b + ld [$ff92], a ; store distance adjusted using sprite i's direction + ld a, b + ld [$ff90], a ; store 7 or 9 depending on sprite i's delta Y + jr c, .checkXDistance + +; If sprite j's delta Y is 0, then b = 7, else b = 9. + ld b, 7 + dec e + ld a, [de] ; a = [$c1j3] (delta Y) + inc e + and a + jr z, .next2 + ld b, 9 + +.next2 + ld a, [$ff92] ; a = distance adjusted using sprite i's direction + sub b ; adjust distance using sprite j's direction + jr z, .checkXDistance + jr nc, .next ; go to next sprite if distance is still positive after both adjustments + +.checkXDistance + inc e + inc l + ld a, [de] ; a = [$c1j5] (delta X) + + push bc + + call SetSpriteCollisionValues + inc e + ld a, [de] ; a = [$c1j6] (X screen coordinate) + +; The effect of the following 3 lines is to +; add 7 to a if moving east or +; subtract 7 from a if moving west. + add b + and $f0 + or c + + pop bc + + sub [hl] ; subtract the adjusted X coordinate of sprite i ([$c1ib]) from that of sprite j + +; calculate the absolute value of the difference to get the distance + jr nc, .noCarry2 + cpl + inc a +.noCarry2 + ld [$ff91], a ; store the distance between the two sprites' adjusted X values + +; Use the carry flag set by the above subtraction to determine which sprite's +; X coordinate is larger. This information is used later to set [$c1ic], +; which stores which direction the collision occurred in. +; The following 5 lines set the lowest 2 bits of c. +; If sprite i's X is larger, set lowest 2 bits of c to 10. +; If sprite j's X is larger or both are equal, set lowest 2 bits of c to 01. + push af + rl c + pop af + ccf + rl c + +; If sprite i's delta X is 0, then b = 7, else b = 9. + ld b, 7 + ld a, [hl] ; a = [$c1ib] (adjusted X coordinate) + and $f + jr z, .next3 + ld b, 9 + +.next3 + ld a, [$ff91] ; a = distance between adjusted X coordinates + sub b + ld [$ff92], a ; store distance adjusted using sprite i's direction + ld a, b + ld [$ff91], a ; store 7 or 9 depending on sprite i's delta X + jr c, .collision + +; If sprite j's delta X is 0, then b = 7, else b = 9. + ld b, 7 + dec e + ld a, [de] ; a = [$c1j5] (delta X) + inc e + and a + jr z, .next4 + ld b, 9 + +.next4 + ld a, [$ff92] ; a = distance adjusted using sprite i's direction + sub b ; adjust distance using sprite j's direction + jr z, .collision + jr nc, .next ; go to next sprite if distance is still positive after both adjustments + +.collision + ld a, l + and $f0 ; collision with pikachu? + jr nz, .asm_4cd9 + xor a + ld [wd434], a + ld a, [$ff8f] + cp $f + jr nz, .asm_4cd9 + call Func_4d0a + jr .asm_4cef +.asm_4cd9 + ld a, [$ff91] ; a = 7 or 9 depending on sprite i's delta X + ld b, a + ld a, [$ff90] ; a = 7 or 9 depending on sprite i's delta Y + inc l + +; If delta X isn't 0 and delta Y is 0, then b = %0011, else b = %1100. +; (note that normally if delta X isn't 0, then delta Y must be 0 and vice versa) + cp b + jr c, .next5 + ld b, %1100 + jr .next6 +.next5 + ld b, %0011 + +.next6 + ld a, c ; c has 2 bits set (one of bits 0-1 is set for the X axis and one of bits 2-3 for the Y axis) + and b ; we select either the bit in bits 0-1 or bits 2-3 based on the calculation immediately above + or [hl] ; or with existing collision direction bits in [$c1ic] + ld [hl], a ; store new value + ld a, c ; useless code because a is overwritten before being used again + +; set bit in [$c1ie] or [$c1if] to indicate which sprite the collision occurred with + inc l + inc l +.asm_4cef + ld a, [$ff8f] ; a = loop counter + ld de, SpriteCollisionBitTable + add a + add e + ld e, a + jr nc, .noCarry3 + inc d +.noCarry3 + ld a, [de] + or [hl] + ld [hli], a + inc de + ld a, [de] + or [hl] + ld [hl], a + +.next + ld a, [$ff8f] ; a = loop counter + inc a + cp $10 + jp nz, .loop + ret + +; takes delta X or delta Y in a +; b = delta X/Y +; c = 0 if delta X/Y is 0 +; c = 7 if delta X/Y is 1 +; c = 9 if delta X/Y is -1 +Func_4d0a: + ld a, [$ff91] + ld b, a + ld a, [$ff90] + inc l + cp b + jr c, .asm_4d17 + ld b, %1100 + jr .asm_4d19 +.asm_4d17 + ld b, %11 +.asm_4d19 + ld a, c + and b + ld [wd434], a + ld a, c + inc l + inc l + ret + +SetSpriteCollisionValues: + and a + ld b, 0 + ld c, 0 + jr z, .done + ld c, 9 + cp -1 + jr z, .ok + ld c, 7 + ld a, 0 +.ok + ld b, a +.done + ret + +SpriteCollisionBitTable: + db %00000000,%00000001 + db %00000000,%00000010 + db %00000000,%00000100 + db %00000000,%00001000 + db %00000000,%00010000 + db %00000000,%00100000 + db %00000000,%01000000 + db %00000000,%10000000 + db %00000001,%00000000 + db %00000010,%00000000 + db %00000100,%00000000 + db %00001000,%00000000 + db %00010000,%00000000 + db %00100000,%00000000 + db %01000000,%00000000 + db %10000000,%00000000 diff --git a/engine/overworld/map_sprites.asm b/engine/overworld/map_sprites.asm index 05588321..43b44946 100755 --- a/engine/overworld/map_sprites.asm +++ b/engine/overworld/map_sprites.asm @@ -8,433 +8,361 @@ ; 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: +_InitMapSprites: 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: - ld a,[wNumSprites] - and a ; are there any sprites? - jr nz,.spritesExist - ret -.spritesExist - ld c,a ; c = [wNumSprites] - ld b,$10 ; number of sprite slots - ld hl,wSpriteStateData2 + $0d - xor a - ld [hFourTileSpriteCount],a -.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,[hFourTileSpriteCount] - add a,11 - jr .storeVRAMSlot -.notFourTileSprite - pop af -.storeVRAMSlot - ld [hl],a ; store VRAM slot at $C2XE - ld [hVRAMSlot],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,[hVRAMSlot] - 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,[hFourTileSpriteCount] - and a - jr nz,.loadStillTilePattern -; if it's the first 4-tile sprite - ld hl,vSprites + $780 ; address for first 4-tile sprite - inc a - ld [hFourTileSpriteCount],a -.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,[hVRAMSlot] - 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: - 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] + call LoadSpriteSetFromMapHeader + call LoadMapSpriteTilePatterns + call Func_14150 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: - ld a,[wCurMap] - cp a,REDS_HOUSE_1F ; is the map a city or a route (map ID less than $25)? + ld a, [wCurMap] + 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,[wSpriteSetID] + call 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, [wSpriteSetID] cp b ; has the sprite set ID changed? - jr z,.skipLoadingSpriteSet ; if not, don't load it again + jr z, .skipLoadingSpriteSet ; if not, don't load it again .loadSpriteSet - ld a,b - ld [wSpriteSetID],a + ld a, b + ld [wSpriteSetID], 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,wSpriteSet -; 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,[wNumSprites] - push af ; save number of sprites - ld a,11 ; 11 sprites in sprite set - ld [wNumSprites],a + ld c, a + ld b, 0 + ld a, (wSpriteSetID - wSpriteSet) + ld hl, SpriteSets + call AddNTimes ; get sprite set offset + ld de, wSpriteSet + ld bc, (wSpriteSetID - wSpriteSet) + call CopyData ; copy it to wSpriteSet call LoadMapSpriteTilePatterns - pop af - ld [wNumSprites],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 + call Func_14150 + scf + ret + +LoadSpriteSetFromMapHeader: ; 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. +; sprite set. The index of the picture ID within the sprite set plus two +; (since the Red sprite always has the first VRAM tile pattern slot and the +; Pikachu sprite reserves the second slot) is the VRAM tile pattern slot. + ld hl, wSpriteSet + ld bc, (wSpriteSetID - wSpriteSet) + xor a + call FillMemory + ld a, SPRITE_PIKACHU ; load Pikachu separately + ld [wSpriteSet], a + ld hl, wSprite01SpriteStateData1 + ld a, 14 .storeVRAMSlotsLoop - ld c,0 - ld a,[hl] ; $C1X0 (picture ID) (zero if sprite slot is not used) + push af + 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,wSpriteSet -; Loop to find the index of the sprite's picture ID within the sprite set. -.getPictureIndexLoop - inc c - ld a,[de] + jr z, .continue ; if the sprite slot is not used + ld c, a + call CheckForFourTileSprite ; is this a four tile sprite? + jr nc, .isFourTileSprite +; loop through the space reserved for four tile picture IDs + ld de, wSpriteSet + 9 + ld b, 2 + call CheckIfPictureIDAlreadyLoaded + jr .continue + +.isFourTileSprite +; loop through the space reserved for regular picture IDs + ld de, wSpriteSet + ld b, 9 + call CheckIfPictureIDAlreadyLoaded +.continue + ld de, wSprite02SpriteStateData1 - wSprite01SpriteStateData1 + add hl, de + pop af + dec a + jr nz, .storeVRAMSlotsLoop + ret + +CheckIfPictureIDAlreadyLoaded: +; 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. +.loop + ld a, [de] + and a ; is sprite set slot not taken up yet? + jr z, .spriteSlotNotTaken ; if so, load it as it signifies we've reached + ; the end of data for the last sprite set + + cp c ; is the tile pattern already loaded? + ret z ; don't redundantly load + dec b ; have we reached the end of the sprite set? + jr z, .spriteNotAlreadyLoaded ; if so, we're done here 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 + jr .loop + +.spriteSlotNotTaken + ld a, c + ld [de], a + ret +.spriteNotAlreadyLoaded + scf + ret + +CheckForFourTileSprite: +; Checks for a sprite added in yellow +; Returns no carry if the sprite is Pikachu, as its sprite is handled separately +; Else, returns carry if the sprite uses 4 tiles + cp SPRITE_PIKACHU ; is this the Pikachu Sprite? + ret z ; return if yes + + cp SPRITE_BALL ; is this a four tile sprite? + jr nc, .notYellowSprite ; set carry if yes +; regular sprite and a - jr nz,.storeVRAMSlotsLoop + ret + +.notYellowSprite scf ret +LoadMapSpriteTilePatterns: + ld a, 0 +.loop + ld [hVRAMSlot], a + cp 9 + jr nc, .fourTileSprite + call LoadStillTilePattern + call LoadWalkingTilePattern + jr .continue + +.fourTileSprite + call LoadStillTilePattern +.continue + ld a, [hVRAMSlot] + inc a + cp 11 + jr nz, .loop + ret + +ReloadWalkingTilePatterns: + xor a +.loop + ld [hVRAMSlot], a + cp 9 + jr nc, .fourTileSprite + call LoadWalkingTilePattern +.fourTileSprite + ld a, [hVRAMSlot] + inc a + cp 11 + jr nz, .loop + ret + +LoadStillTilePattern: + ld a, [wFontLoaded] + bit 0, a ; reloading upper half of tile patterns after displaying text? + ret nz ; if so, skip loading data into the lower half + call ReadSpriteSheetData + ret nc + call GetSpriteVRAMAddress + call CopyVideoDataAlternate ; new yellow function + ret + +LoadWalkingTilePattern: + call ReadSpriteSheetData + ret nc + ld hl, $c0 + add hl, de + ld d, h + ld e, l + call GetSpriteVRAMAddress + set 3, h ; add $800 to hl + call CopyVideoDataAlternate + ret + +GetSpriteVRAMAddress: + push bc + ld a, [hVRAMSlot] + ld c, a + ld b, 0 + ld hl, SpriteVRAMAddresses + add hl, bc + add hl, bc + ld a, [hli] + ld h, [hl] + ld l, a + pop bc + ret + +SpriteVRAMAddresses: +; Equivalent to multiplying $C0 (number of bytes in 12 tiles) times the VRAM +; slot and adding the result to $8000 (the VRAM base address). + dw vChars0 + $0c0 + dw vChars0 + $180 + dw vChars0 + $240 + dw vChars0 + $300 + dw vChars0 + $3c0 + dw vChars0 + $480 + dw vChars0 + $540 + dw vChars0 + $600 + dw vChars0 + $6c0 + dw vChars0 + $780 ; 4-tile sprites + dw vChars0 + $7c0 ; 4-tile sprites + +ReadSpriteSheetData: + ld a, [hVRAMSlot] + ld e, a + ld d, 0 + ld hl, wSpriteSet + add hl, de + ld a, [hl] + and a + ret z + + dec a + ld l, a + ld h, 0 + add hl, hl + add hl, hl + ld de, SpriteSheetPointerTable + add hl, de + ld e, [hl] + inc hl + ld d, [hl] + inc hl + ld c, [hl] + swap c ; get the number of tiles, not the raw byte length + ; this is because of the use of CopyVideoDataAlternate + inc hl + ld b, [hl] + inc hl + scf + ret + +Func_14150: + ld a, $1 + ld [wPlayerSpriteImageBaseOffset], a ; vram slot for player + ld a, $2 + ld [wPikachuSpriteImageBaseOffset], a ; vram slot for Pikachu + ld a, $e + ld hl, wSprite01SpriteStateData1 +.loop + ld [hVRAMSlot], a ; store current sprite set slot as a counter + ld a, [hl] ; $c1x0 (picture ID) + and a ; is the sprite unused? + jr z, .spriteUnused + call Func_14179 + push hl + ld de, (wPlayerSpriteImageBaseOffset) - (wSpriteStateData1) ; $10e + add hl, de ; get $c2xe (sprite image base offset) + ld [hl], a ; write offset + pop hl +.spriteUnused + ld de, wSprite02SpriteStateData1 - wSprite01SpriteStateData1 + add hl, de + ld a, [hVRAMSlot] + dec a + jr nz, .loop + ret + +Func_14179: + push de + push bc + ld c, a ; c = picture ID + ld b, 11 + ld de, wSpriteSet +.findSpriteImageBaseOffsetLoop + ld a, [de] ; a = sprite set picture ID + cp c ; have we found a match? + jr z, .foundSpritePictureID ; if so, get the sprite image base offset and return + inc de + dec b ; have we looped through all entries in wSpriteSet? + jr nz, .findSpriteImageBaseOffsetLoop ; continue looping if not + ld a, $1 ; assume slot one if this ever happens + jr .done +.foundSpritePictureID + ld a, 13 + sub b ; get sprite image base offset +.done + pop bc + pop de + ret + +GetSplitMapSpriteSetID: + ld e, a + ld d, 0 + ld hl, MapSpriteSets + add hl, de + ld a, [hl] ; a = spriteSetID + cp a, $f0 ; does the map have 2 sprite sets? + ret c ; Chooses the correct sprite set ID depending on the player's position within ; the map for maps with two sprite sets. -GetSplitMapSpriteSetID: - cp a,$f8 - jr z,.route20 - ld hl,SplitMapSpriteSets - and a,$0f + cp a, $f8 + jr z, .route20 + ld hl, SplitMapSpriteSets + and a, $0f dec a - sla a - sla a + add a + add a add l - ld l,a - jr nc,.noCarry + 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 + 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,[wYCoord] + ld a, [wYCoord] jr .compareCoord + .eastWestDivide - ld a,[wXCoord] + ld a, [wXCoord] .compareCoord cp b - jr c,.loadSpriteSetID + jr c, .loadSpriteSetID ; if in the East side or South side inc hl .loadSpriteSetID - ld a,[hl] + 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,wXCoord - ld a,[hl] - cp a,$2b - ld a,$01 + ld hl, wXCoord + ld a, [hl] + cp a, $2b + ld a, $01 ret c - ld a,[hl] - cp a,$3e - ld a,$0a + 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 + ld a, [hl] + cp a, $37 + ld b, $08 + jr nc, .next + ld b, $0d .next - ld a,[wYCoord] + ld a, [wYCoord] cp b - ld a,$0a + ld a, $0a ret c - ld a,$01 + ld a, $01 ret INCLUDE "data/sprite_sets.asm" diff --git a/engine/overworld/missable_objects.asm b/engine/overworld/missable_objects.asm new file mode 100644 index 00000000..dd601451 --- /dev/null +++ b/engine/overworld/missable_objects.asm @@ -0,0 +1,212 @@ +MarkTownVisitedAndLoadMissableObjects: + ld a, [wCurMap] + cp ROUTE_1 + jr nc, .notInTown + ld c, a + ld b, FLAG_SET + ld hl, wTownVisitedFlag ; mark town as visited (for flying) + predef FlagActionPredef +.notInTown + ld hl, MapHSPointers + ld a, [wCurMap] + ld b, $0 + ld c, a + add hl, bc + add hl, bc + ld a, [hli] ; load missable objects pointer in hl + ld h, [hl] + ; fall through + +; LoadMissableObjects: +; seems to not exist in yellow (predef replaced with something near TryPushingBoulder) + ld l, a + push hl + ld a, l + sub MapHS00 & $ff ; calculate difference between out pointer and the base pointer + ld l, a + ld a, h + sbc MapHS00 / $100 + ld h, a + ld a, h + ld [H_DIVIDEND], a + ld a, l + ld [H_DIVIDEND + 1], a + xor a + ld [H_DIVIDEND + 2], a + ld [H_DIVIDEND + 3], a + ld a, $3 + ld [H_DIVISOR], a + ld b, $2 + call Divide ; divide difference by 3, resulting in the global offset (number of missable items before ours) + ld a, [wCurMap] + ld b, a + ld a, [H_DIVIDEND + 3] + ld c, a ; store global offset in c + ld de, wMissableObjectList + pop hl +.writeMissableObjectsListLoop + ld a, [hli] + cp $ff + jr z, .done ; end of list + cp b + jr nz, .done ; not for current map anymore + ld a, [hli] + inc hl + ld [de], a ; write (map-local) sprite ID + inc de + ld a, c + inc c + ld [de], a ; write (global) missable object index + inc de + jr .writeMissableObjectsListLoop +.done + ld a, $ff + ld [de], a ; write sentinel + ret + +InitializeMissableObjectsFlags: + ld hl, wMissableObjectFlags + ld bc, wMissableObjectFlagsEnd - wMissableObjectFlags + xor a + call FillMemory ; clear missable objects flags + ld hl, MapHS00 + xor a + ld [wMissableObjectCounter], a +.missableObjectsLoop + ld a, [hli] + cp $ff ; end of list + ret z + push hl + inc hl + ld a, [hl] + cp Hide + jr nz, .skip + ld hl, wMissableObjectFlags + ld a, [wMissableObjectCounter] + ld c, a + ld b, FLAG_SET + call MissableObjectFlagAction ; set flag if Item is hidden +.skip + ld hl, wMissableObjectCounter + inc [hl] + pop hl + inc hl + inc hl + jr .missableObjectsLoop + +; tests if current sprite is a missable object that is hidden/has been removed +IsObjectHidden: + ld a, [H_CURRENTSPRITEOFFSET] + swap a + ld b, a + ld hl, wMissableObjectList +.loop + ld a, [hli] + cp $ff + jr z, .notHidden ; not missable -> not hidden + cp b + ld a, [hli] + jr nz, .loop + ld c, a + ld b, FLAG_TEST + ld hl, wMissableObjectFlags + call MissableObjectFlagAction + ld a, c + and a + jr nz, .hidden +.notHidden + xor a +.hidden + ld [$ffe5], a + ret + +; adds missable object (items, leg. pokemon, etc.) to the map +; [wMissableObjectIndex]: index of the missable object to be added (global index) +ShowObject: +ShowObject2: + ld hl, wMissableObjectFlags + ld a, [wMissableObjectIndex] + ld c, a + ld b, FLAG_RESET + call MissableObjectFlagAction ; reset "removed" flag + jp UpdateSprites + +; removes missable object (items, leg. pokemon, etc.) from the map +; [wMissableObjectIndex]: index of the missable object to be removed (global index) +HideObject: + ld hl, wMissableObjectFlags + ld a, [wMissableObjectIndex] + ld c, a + ld b, FLAG_SET + call MissableObjectFlagAction ; set "removed" flag + jp UpdateSprites + +MissableObjectFlagAction: +; identical to FlagAction + + push hl + push de + push bc + + ; bit + ld a, c + ld d, a + and 7 + ld e, a + + ; byte + ld a, d + srl a + srl a + srl a + add l + ld l, a + jr nc, .ok + inc h +.ok + + ; d = 1 << e (bitmask) + inc e + ld d, 1 +.shift + dec e + jr z, .shifted + sla d + jr .shift +.shifted + + ld a, b + and a + jr z, .reset + cp 2 + jr z, .read + +.set + ld a, [hl] + ld b, a + ld a, d + or b + ld [hl], a + jr .done + +.reset + ld a, [hl] + ld b, a + ld a, d + xor $ff + and b + ld [hl], a + jr .done + +.read + ld a, [hl] + ld b, a + ld a, d + and b + +.done + pop bc + pop de + pop hl + ld c, a + ret diff --git a/engine/overworld/movement.asm b/engine/overworld/movement.asm index e60f820a..ad4515ff 100644 --- a/engine/overworld/movement.asm +++ b/engine/overworld/movement.asm @@ -20,7 +20,13 @@ UpdatePlayerSprite: ld [wSpriteStateData1 + 2], a ret .lowerLeftTileIsMapTile + ld a, [wUpdateSpritesEnabled] + push af + ld a, $ff + ld [wUpdateSpritesEnabled], a call DetectCollisionBetweenSprites + pop af + ld [wUpdateSpritesEnabled], a ld h, wSpriteStateData1 / $100 ld a, [wWalkCounter] and a @@ -46,42 +52,24 @@ UpdatePlayerSprite: jr z, .notMoving ld a, SPRITE_FACING_RIGHT jr .next +.next + ld [wPlayerFacingDirection], a ; facing direction + ld a, [wFontLoaded] + bit 0, a + jr z, .moving .notMoving ; zero the animation counters xor a ld [wSpriteStateData1 + 7], a ld [wSpriteStateData1 + 8], a - jr .calcImageIndex -.next - ld [wSpriteStateData1 + 9], a ; facing direction - ld a, [wFontLoaded] - bit 0, a - jr nz, .notMoving + call Func_4e32 + jr .skipSpriteAnim .moving ld a, [wd736] bit 7, a ; is the player sprite spinning due to a spin tile? jr nz, .skipSpriteAnim - ld a, [H_CURRENTSPRITEOFFSET] - add $7 - ld l, a - ld a, [hl] - inc a - ld [hl], a - cp 4 - jr nz, .calcImageIndex - xor a - ld [hl], a - inc hl - ld a, [hl] - inc a - and $3 - ld [hl], a -.calcImageIndex - ld a, [wSpriteStateData1 + 8] - ld b, a - ld a, [wSpriteStateData1 + 9] - add b - ld [wSpriteStateData1 + 2], a + call Func_5274 + call Func_4e32 .skipSpriteAnim ; If the player is standing on a grass tile, make the player's sprite have ; lower priority than the background so that it's partially obscured by the @@ -95,18 +83,15 @@ UpdatePlayerSprite: jr nz, .next2 ld a, $80 .next2 - ld [wSpriteStateData2 + 7], a + ld [wSpriteStateData2 + $07], a ret -UnusedReadSpriteDataFunction: - push bc - push af - ld a, [H_CURRENTSPRITEOFFSET] - ld c, a - pop af - add c - ld l, a - pop bc +Func_4e32: + ld a, [wSpriteStateData1 + 8] + ld b, a + ld a, [wPlayerFacingDirection] + add b + ld [wSpriteStateData1 + 2], a ret UpdateNPCSprite: @@ -119,7 +104,7 @@ UpdateNPCSprite: ld l, a ld a, [hl] ; read movement byte 2 ld [wCurSpriteMovement2], a - ld h, $c1 + ld h, wSpriteStateData1 / $100 ld a, [H_CURRENTSPRITEOFFSET] ld l, a inc l @@ -128,7 +113,7 @@ UpdateNPCSprite: jp z, InitializeSpriteStatus call CheckSpriteAvailability ret c ; if sprite is invisible, on tile >=$60, in grass or player is currently walking - ld h, $c1 + ld h, wSpriteStateData1 / $100 ld a, [H_CURRENTSPRITEOFFSET] ld l, a inc l @@ -144,19 +129,21 @@ UpdateNPCSprite: jp z, UpdateSpriteMovementDelay ; c1x1 == 2 cp $3 jp z, UpdateSpriteInWalkingAnimation ; c1x1 == 3 + cp $4 + jp z, Func_5357 ld a, [wWalkCounter] and a ret nz ; don't do anything yet if player is currently moving (redundant, already tested in CheckSpriteAvailability) call InitializeSpriteScreenPosition - ld h, $c2 + ld h, wSpriteStateData2 / $100 ld a, [H_CURRENTSPRITEOFFSET] add $6 ld l, a ld a, [hl] ; c2x6: movement byte 1 inc a - jr z, .randomMovement ; value $FF + jp z, .randomMovement ; value $FF inc a - jr z, .randomMovement ; value $FE + jp z, .randomMovement ; value $FE ; scripted movement dec a ld [hl], a ; increment movement byte 1 (movement data index) @@ -181,12 +168,18 @@ UpdateNPCSprite: ret .next cp WALK - jr nz, .determineDirection + jr nz, .asm_4ecb ; current NPC movement data is $fe. this seems buggy ld [hl], $1 ; set movement byte 1 to $1 ld de, wNPCMovementDirections call LoadDEPlusA ; a = [wNPCMovementDirections + $fe] (?) - jr .determineDirection +.asm_4ecb + push af + call Func_5288 + pop bc + ld a, b + jr nc, .determineDirection + ret .randomMovement call GetTileSpriteStandsOn call Random @@ -261,59 +254,25 @@ ChangeFacingDirection: ; set carry on failure, clears carry on success TryWalking: push hl - ld h, $c1 - ld a, [H_CURRENTSPRITEOFFSET] - add $9 - ld l, a - ld [hl], c ; c1x9 (update facing direction) - ld a, [H_CURRENTSPRITEOFFSET] - add $3 - ld l, a - ld [hl], d ; c1x3 (update Y movement delta) - inc l - inc l - ld [hl], e ; c1x5 (update X movement delta) + call Func_5337 pop hl push de - ld c, [hl] ; read tile to walk onto + ld c, [hl] call CanWalkOntoTile pop de - ret c ; cannot walk there (reinitialization of delay values already done) - ld h, $c2 - ld a, [H_CURRENTSPRITEOFFSET] - add $4 - ld l, a - ld a, [hl] ; c2x4: Y position - add d - ld [hli], a ; update Y position - ld a, [hl] ; c2x5: X position - add e - ld [hl], a ; update X position + ret c + call Func_5349 ld a, [H_CURRENTSPRITEOFFSET] ld l, a - ld [hl], $10 ; c2x0=16: walk animation counter + ld [hl], $10 ; c1x9 (update facing direction) dec h inc l - ld [hl], $3 ; c1x1: set movement status to walking + ld [hl], $3 jp UpdateSpriteImage ; update the walking animation parameters for a sprite that is currently walking UpdateSpriteInWalkingAnimation: - ld a, [H_CURRENTSPRITEOFFSET] - add $7 - ld l, a - ld a, [hl] ; c1x7 (counter until next walk animation frame) - inc a - ld [hl], a ; c1x7 += 1 - cp $4 - jr nz, .noNextAnimationFrame - xor a - ld [hl], a ; c1x7 = 0 - inc l - ld a, [hl] ; c1x8 (walk animation frame) - inc a - and $3 - ld [hl], a ; advance to next animation frame every 4 ticks (16 ticks total for one step) + call Func_5274 .noNextAnimationFrame ld a, [H_CURRENTSPRITEOFFSET] add $3 @@ -393,7 +352,7 @@ UpdateSpriteMovementDelay: ld l, a ld [hl], $1 ; c1x1 = 1 (mark as ready to move) notYetMoving: - ld h, $c1 + ld h, wSpriteStateData1 / $100 ld a, [H_CURRENTSPRITEOFFSET] add $8 ld l, a @@ -408,7 +367,6 @@ MakeNPCFacePlayer: ld a, [wd72d] bit 5, a jr nz, notYetMoving - res 7, [hl] ld a, [wPlayerDirection] bit PLAYER_DIR_BIT_UP, a @@ -445,11 +403,12 @@ InitializeSpriteStatus: ld a, $8 ld [hli], a ; $c2x2: set Y displacement to 8 ld [hl], a ; $c2x3: set X displacement to 8 + call InitializeSpriteScreenPosition ; could have done fallthrough here ret ; calculates the spprite's scrren position form its map position and the player position InitializeSpriteScreenPosition: - ld h, $c2 + ld h, wSpriteStateData2 / $100 ld a, [H_CURRENTSPRITEOFFSET] add $4 ld l, a @@ -457,7 +416,7 @@ InitializeSpriteScreenPosition: ld b, a ld a, [hl] ; c2x4 (Y position + 4) sub b ; relative to player position - swap a ; * 16 + call Func_5033 sub $4 ; - 4 dec h ld [hli], a ; c1x4 (screen Y position) @@ -466,18 +425,30 @@ InitializeSpriteScreenPosition: ld b, a ld a, [hli] ; c2x6 (X position + 4) sub b ; relative to player position - swap a ; * 16 + call Func_5033 dec h ld [hl], a ; c1x6 (screen X position) ret +Func_5033: + jr nc, .asm_503c + cpl + inc a + swap a + cpl + inc a + ret +.asm_503c + swap a + ret + ; tests if sprite is off screen or otherwise unable to do anything CheckSpriteAvailability: predef IsObjectHidden ld a, [$ffe5] and a jp nz, .spriteInvisible - ld h, $c2 + ld h, wSpriteStateData2 / $100 ld a, [H_CURRENTSPRITEOFFSET] add $6 ld l, a @@ -525,7 +496,7 @@ CheckSpriteAvailability: cp d jr c, .spriteVisible ; standing on tile with ID >=$60 (top right tile) .spriteInvisible - ld h, $c1 + ld h, wSpriteStateData1 / $100 ld a, [H_CURRENTSPRITEOFFSET] add $2 ld l, a @@ -579,7 +550,7 @@ UpdateSpriteImage: ; e: X movement delta (-1, 0 or 1) ; set carry on failure, clears carry on success CanWalkOntoTile: - ld h, $c2 + ld h, wSpriteStateData2 / $100 ld a, [H_CURRENTSPRITEOFFSET] add $6 ld l, a @@ -590,24 +561,16 @@ CanWalkOntoTile: and a ret .notScripted - ld a, [wTileSetCollisionPtr] - ld l, a - ld a, [wTileSetCollisionPtr+1] - ld h, a -.tilePassableLoop - ld a, [hli] - cp $ff - jr z, .impassable - cp c - jr nz, .tilePassableLoop - ld h, $c2 + call _IsTilePassable + jr c, .impassable + ld h, wSpriteStateData2 / $100 ld a, [H_CURRENTSPRITEOFFSET] add $6 ld l, a ld a, [hl] ; $c2x6 (movement byte 1) inc a jr z, .impassable ; if $ff, no movement allowed (however, changing direction is) - ld h, $c1 + ld h, wSpriteStateData1 / $100 ld a, [H_CURRENTSPRITEOFFSET] add $4 ld l, a @@ -623,17 +586,23 @@ CanWalkOntoTile: jr nc, .impassable ; don't walk off screen push de push bc + ld a, [wUpdateSpritesEnabled] + push af + ld a, $ff + ld [wUpdateSpritesEnabled], a call DetectCollisionBetweenSprites + pop af + ld [wUpdateSpritesEnabled], a pop bc pop de - ld h, $c1 + ld h, wSpriteStateData1 / $100 ld a, [H_CURRENTSPRITEOFFSET] add $c ld l, a ld a, [hl] ; c1xc (directions in which sprite collision would occur) and b ; check against chosen direction (1,2,4 or 8) jr nz, .impassable ; collision between sprites, don't go there - ld h, $c2 + ld h, wSpriteStateData2 / $100 ld a, [H_CURRENTSPRITEOFFSET] add $2 ld l, a @@ -642,7 +611,7 @@ CanWalkOntoTile: jr nz, .upwards add d cp $5 - jr c, .impassable ; if c2x2+d < 5, don't go ;bug: this tests probably were supposed to prevent sprites + ;jr c, .impassable (bugfix) ; if c2x2+d < 5, don't go ;bug: this tests probably were supposed to prevent sprites jr .checkHorizontal ; from walking out too far, but this line makes sprites get stuck .upwards ; whenever they walked upwards 5 steps sub $1 ; on the other hand, the amount a sprite can walk out to the @@ -664,7 +633,7 @@ CanWalkOntoTile: and a ; clear carry (marking success) ret .impassable - ld h, $c1 + ld h, wSpriteStateData1 / $100 ld a, [H_CURRENTSPRITEOFFSET] inc a ld l, a @@ -690,13 +659,13 @@ CanWalkOntoTile: ; this is always the lower left tile of the 2x2 tile blocks all sprites are snapped to ; hl: output pointer GetTileSpriteStandsOn: - ld h, $c1 + ld h, wSpriteStateData1 / $100 ld a, [H_CURRENTSPRITEOFFSET] add $4 ld l, a ld a, [hli] ; c1x4: screen Y position add $4 ; align to 2*2 tile blocks (Y position is always off 4 pixels to the top) - and $f0 ; in case object is currently moving + and $f8 ; in case object is currently moving (XXX why changed to $f8?) srl a ; screen Y tile * 4 ld c, a ld b, $0 @@ -862,20 +831,235 @@ AnimScriptedNPCMovement: ret AdvanceScriptedNPCAnimFrameCounter: + call Func_5274 + ld h, wSpriteStateData1 / $100 ld a, [H_CURRENTSPRITEOFFSET] - add $7 + add $8 ld l, a ld a, [hl] ; intra-animation frame counter + and $3 + ld [hSpriteAnimFrameCounter], a + ret + +Func_5274: + ld a, [H_CURRENTSPRITEOFFSET] + add $7 + ld l, a + ld h, wSpriteStateData1 / $100 + ld a, [hl] ; c1x7 (counter until next walk animation frame) + inc a + and $3 + ld [hl], a ; c1x7 += 1 + ret nz ; c1x7 = 0 + inc l + ld a, [hl] ; c1x8 (walk animation frame) inc a + and $3 + ld [hl], a ; advance to next animation frame every 4 ticks (16 ticks total for one step) + ret + +Func_5288: +; nice lookup table +; a is supposedly [wNPCMovementDirections + $fe] + cp $5 + jr z, .asm_52af + cp $4 + jr z, .asm_52aa + cp $6 + jr z, .asm_52b4 + cp $7 + jr z, .asm_52b9 + cp $11 + jr z, .asm_52c3 + cp $12 + jr z, .asm_52be + cp $13 + jr z, .asm_52c8 + cp $14 + jr z, .asm_52cd + xor a + ret +; set 1? +.asm_52aa + call Func_531f + jr .asm_52e6 +.asm_52af + call Func_5325 + jr .asm_52e6 +.asm_52b4 + call Func_5331 + jr .asm_52e6 +.asm_52b9 + call Func_532b + jr .asm_52e6 +; set 2? +.asm_52be + call Func_531f + jr .asm_52fa +.asm_52c3 + call Func_5325 + jr .asm_52fa +.asm_52c8 + call Func_5331 + jr .asm_52fa +.asm_52cd + call Func_532b + jr .asm_52fa +; set 3? (unused) +.asm_52d2 + call Func_531f + jr .asm_530b +.asm_52d7 + call Func_5325 + jr .asm_530b +.asm_52dc + call Func_5331 + jr .asm_530b +.asm_52e1 + call Func_532b + jr .asm_530b + +.asm_52e6 + call Func_5337 + call Func_5349 + ld a, [H_CURRENTSPRITEOFFSET] + ld l, a + ld [hl], $8 + dec h + inc l + ld [hl], $4 + call UpdateSpriteImage + scf + ret + +.asm_52fa + call Func_5337 + ld a, [H_CURRENTSPRITEOFFSET] + ld l, a + ld [hl], $8 + dec h + inc l + ld [hl], $3 + call UpdateSpriteImage + scf + ret + +.asm_530b + call Func_5337 + call Func_5349 + ld a, [H_CURRENTSPRITEOFFSET] + ld l, a + ld [hl], $8 + dec h + inc l + ld [hl], $3 + call UpdateSpriteImage + scf + ret + +Func_531f: + lb de, 1, 0 + ld c, SPRITE_FACING_DOWN + ret + +Func_5325: + lb de, -1, 0 + ld c, SPRITE_FACING_UP + ret + +Func_532b: + lb de, 0, 1 + ld c, SPRITE_FACING_RIGHT + ret + +Func_5331: + lb de, 0, -1 + ld c, SPRITE_FACING_LEFT + ret + +Func_5337: + ld a, [H_CURRENTSPRITEOFFSET] + add $9 + ld l, a + ld h, wSpriteStateData1 / $100 + ld [hl], c ; c1x9 (update facing direction) + ld a, [H_CURRENTSPRITEOFFSET] + add $3 + ld l, a + ld [hl], d ; c1x3 (update Y movement delta) + inc l + inc l + ld [hl], e ; c1x5 (update X movement delta) + ret + +Func_5349: + ld h, wSpriteStateData2 / $100 + ld a, [H_CURRENTSPRITEOFFSET] + add $4 + ld l, a + ld a, [hl] ; c2x4: Y position + add d + ld [hli], a ; update Y position + ld a, [hl] ; c2x5: X position + add e + ld [hl], a ; update X position + ret + +Func_5357: + call Func_5274 + ld a, [H_CURRENTSPRITEOFFSET] + add $3 + ld l, a + ld h, wSpriteStateData1 / $100 + ld a, [hli] + add a + ld b, a + ld a, [hl] + add b + ld [hli], a + ld a, [hli] + add a + ld b, a + ld a, [hl] + add b ld [hl], a - cp 4 + ld a, [H_CURRENTSPRITEOFFSET] + ld l, a + ld h, wSpriteStateData2 / $100 + dec [hl] ret nz + ld a, $6 + add l + ld l, a + ld a, [hl] + cp $fe + jr nc, .asm_5386 + ld a, [H_CURRENTSPRITEOFFSET] + inc a + ld l, a + ld h, wSpriteStateData1 / $100 + ld [hl], $1 + ret +.asm_5386 + call Random + ld a, [H_CURRENTSPRITEOFFSET] + add $8 + ld l, a + ld h, wSpriteStateData2 / $100 + ld a, [hRandomAdd] + and $7f + ld [hl], a + dec h + ld a, [H_CURRENTSPRITEOFFSET] + inc a + ld l, a + ld [hl], $2 + inc l + inc l xor a - ld [hl], a ; reset intra-animation frame counter + ld b, [hl] + ld [hli], a inc l - ld a, [hl] ; animation frame counter - inc a - and $3 + ld c, [hl] ld [hl], a - ld [hSpriteAnimFrameCounter], a ret diff --git a/engine/overworld/npc_movement.asm b/engine/overworld/npc_movement.asm index 98d1b7a7..333779fa 100755 --- a/engine/overworld/npc_movement.asm +++ b/engine/overworld/npc_movement.asm @@ -37,8 +37,8 @@ _EndNPCMovementScript: res 1, [hl] xor a ld [wNPCMovementScriptSpriteOffset], a - ld [wNPCMovementScriptPointerTableNum], a ld [wNPCMovementScriptFunctionNum], a + ld [wNPCMovementScriptPointerTableNum], a ld [wWastedByteCD3A], a ld [wSimulatedJoypadStatesIndex], a ld [wSimulatedJoypadStatesEnd], a @@ -79,6 +79,10 @@ PalletMovementScript_OakMoveLeft: ld a, $3 ld [wNPCMovementScriptFunctionNum], a .done + ld a, BANK(Music_MuseumGuy) + ld c, a + ld a, MUSIC_MUSEUM_GUY + call PlayMusic ld hl, wFlags_D733 set 1, [hl] ld a, $fc @@ -127,8 +131,9 @@ PalletMovementScript_WalkToLab: ld [wNPCMovementScriptFunctionNum], a ret + RLEList_ProfOakWalkToLab: - db NPC_MOVEMENT_DOWN, $05 + db NPC_MOVEMENT_DOWN, $06 ; differs from red db NPC_MOVEMENT_LEFT, $01 db NPC_MOVEMENT_DOWN, $05 db NPC_MOVEMENT_RIGHT, $03 @@ -141,7 +146,7 @@ RLEList_PlayerWalkToLab: db D_RIGHT, $03 db D_DOWN, $05 db D_LEFT, $01 - db D_DOWN, $06 + db D_DOWN, $07 ; differs from red db $FF PalletMovementScript_Done: @@ -163,11 +168,9 @@ PewterMuseumGuyMovementScriptPointerTable: PewterMovementScript_WalkToMuseum: ld a, BANK(Music_MuseumGuy) - ld [wAudioROMBank], a - ld [wAudioSavedROMBank], a + ld c, a ld a, MUSIC_MUSEUM_GUY - ld [wNewSoundID], a - call PlaySound + call PlayMusic ld a, [wSpriteIndex] swap a ld [wNPCMovementScriptSpriteOffset], a @@ -179,7 +182,7 @@ PewterMovementScript_WalkToMuseum: ld [wSimulatedJoypadStatesIndex], a xor a ld [wWhichPewterGuy], a - predef PewterGuys + call PewterGuys ld hl, wNPCMovementDirections2 ld de, RLEList_PewterMuseumGuy call DecodeRLEList @@ -219,11 +222,9 @@ PewterGymGuyMovementScriptPointerTable: PewterMovementScript_WalkToGym: ld a, BANK(Music_MuseumGuy) - ld [wAudioROMBank], a - ld [wAudioSavedROMBank], a + ld c, a ld a, MUSIC_MUSEUM_GUY - ld [wNewSoundID], a - call PlaySound + call PlayMusic ld a, [wSpriteIndex] swap a ld [wNPCMovementScriptSpriteOffset], a @@ -236,7 +237,7 @@ PewterMovementScript_WalkToGym: ld [wSimulatedJoypadStatesIndex], a ld a, 1 ld [wWhichPewterGuy], a - predef PewterGuys + call PewterGuys ld hl, wNPCMovementDirections2 ld de, RLEList_PewterGymGuy call DecodeRLEList @@ -266,27 +267,4 @@ RLEList_PewterGymGuy: db NPC_MOVEMENT_RIGHT, $03 db $FF -FreezeEnemyTrainerSprite: - ld a, [wCurMap] - cp POKEMONTOWER_7 - ret z ; the Rockets on Pokemon Tower 7F leave after battling, so don't freeze them - ld hl, RivalIDs - ld a, [wEngagedTrainerClass] - ld b, a -.loop - ld a, [hli] - cp $ff - jr z, .notRival - cp b - ret z ; the rival leaves after battling, so don't freeze him - jr .loop -.notRival - ld a, [wSpriteIndex] - ld [H_SPRITEINDEX], a - jp SetSpriteMovementBytesToFF - -RivalIDs: - db OPP_SONY1 - db OPP_SONY2 - db OPP_SONY3 - db $ff +INCLUDE "engine/overworld/pewter_guys.asm" diff --git a/engine/overworld/npc_movement_2.asm b/engine/overworld/npc_movement_2.asm new file mode 100755 index 00000000..06ee9319 --- /dev/null +++ b/engine/overworld/npc_movement_2.asm @@ -0,0 +1,24 @@ +FreezeEnemyTrainerSprite: + ld a, [wCurMap] + cp POKEMONTOWER_7 + ret z ; the Rockets on Pokemon Tower 7F leave after battling, so don't freeze them + ld hl, RivalIDs + ld a, [wEngagedTrainerClass] + ld b, a +.loop + ld a, [hli] + cp $ff + jr z, .notRival + cp b + ret z ; the rival leaves after battling, so don't freeze him + jr .loop +.notRival + ld a, [wSpriteIndex] + ld [H_SPRITEINDEX], a + jp SetSpriteMovementBytesToFF + +RivalIDs: + db OPP_SONY1 + db OPP_SONY2 + db OPP_SONY3 + db $ff diff --git a/engine/overworld/npc_pathfinding.asm b/engine/overworld/npc_pathfinding.asm new file mode 100644 index 00000000..f3d23b7c --- /dev/null +++ b/engine/overworld/npc_pathfinding.asm @@ -0,0 +1,201 @@ +FindPathToPlayer: + xor a + ld hl, hFindPathNumSteps + ld [hli], a ; hFindPathNumSteps + ld [hli], a ; hFindPathFlags + ld [hli], a ; hFindPathYProgress + ld [hl], a ; hFindPathXProgress + ld hl, wNPCMovementDirections2 + ld de, $0 +.loop + ld a, [hFindPathYProgress] + ld b, a + ld a, [hNPCPlayerYDistance] ; Y distance in steps + call CalcDifference + ld d, a + and a + jr nz, .asm_f76a + ld a, [hFindPathFlags] + set 0, a ; current end of path matches the player's Y coordinate + ld [hFindPathFlags], a +.asm_f76a + ld a, [hFindPathXProgress] + ld b, a + ld a, [hNPCPlayerXDistance] ; X distance in steps + call CalcDifference + ld e, a + and a + jr nz, .asm_f77c + ld a, [hFindPathFlags] + set 1, a ; current end of path matches the player's X coordinate + ld [hFindPathFlags], a +.asm_f77c + ld a, [hFindPathFlags] + cp $3 ; has the end of the path reached the player's position? + jr z, .done +; Compare whether the X distance between the player and the current of the path +; is greater or if the Y distance is. Then, try to reduce whichever is greater. + ld a, e + cp d + jr c, .yDistanceGreater +; x distance is greater + ld a, [hNPCPlayerRelativePosFlags] + bit 1, a + jr nz, .playerIsLeftOfNPC + ld d, NPC_MOVEMENT_RIGHT + jr .next1 +.playerIsLeftOfNPC + ld d, NPC_MOVEMENT_LEFT +.next1 + ld a, [hFindPathXProgress] + add 1 + ld [hFindPathXProgress], a + jr .storeDirection +.yDistanceGreater + ld a, [hNPCPlayerRelativePosFlags] + bit 0, a + jr nz, .playerIsAboveNPC + ld d, NPC_MOVEMENT_DOWN + jr .next2 +.playerIsAboveNPC + ld d, NPC_MOVEMENT_UP +.next2 + ld a, [hFindPathYProgress] + add 1 + ld [hFindPathYProgress], a +.storeDirection + ld a, d + ld [hli], a + ld a, [hFindPathNumSteps] + inc a + ld [hFindPathNumSteps], a + jp .loop +.done + ld [hl], $ff + ret + +CalcPositionOfPlayerRelativeToNPC: + xor a + ld [hNPCPlayerRelativePosFlags], a + ld a, [wSpriteStateData1 + 4] ; player's sprite screen Y position in pixels + ld d, a + ld a, [wSpriteStateData1 + 6] ; player's sprite screen X position in pixels + ld e, a + ld hl, wSpriteStateData1 + ld a, [hNPCSpriteOffset] + add l + add $4 + ld l, a + jr nc, .noCarry + inc h +.noCarry + ld a, d + ld b, a + ld a, [hli] ; NPC sprite screen Y position in pixels + call CalcDifference + jr nc, .NPCSouthOfOrAlignedWithPlayer +.NPCNorthOfPlayer + push hl + ld hl, hNPCPlayerRelativePosFlags + bit 0, [hl] + set 0, [hl] + pop hl + jr .divideYDistance +.NPCSouthOfOrAlignedWithPlayer + push hl + ld hl, hNPCPlayerRelativePosFlags + bit 0, [hl] + res 0, [hl] + pop hl +.divideYDistance + push hl + ld hl, hDividend2 + ld [hli], a + ld a, 16 + ld [hli], a + call DivideBytes ; divide Y absolute distance by 16 + ld a, [hl] ; quotient + ld [hNPCPlayerYDistance], a + pop hl + inc hl + ld b, e + ld a, [hl] ; NPC sprite screen X position in pixels + call CalcDifference + jr nc, .NPCEastOfOrAlignedWithPlayer +.NPCWestOfPlayer + push hl + ld hl, hNPCPlayerRelativePosFlags + bit 1, [hl] + set 1, [hl] + pop hl + jr .divideXDistance +.NPCEastOfOrAlignedWithPlayer + push hl + ld hl, hNPCPlayerRelativePosFlags + bit 1, [hl] + res 1, [hl] + pop hl +.divideXDistance + ld [hDividend2], a + ld a, 16 + ld [hDivisor2], a + call DivideBytes ; divide X absolute distance by 16 + ld a, [hQuotient2] + ld [hNPCPlayerXDistance], a + ld a, [hNPCPlayerRelativePosPerspective] + and a + ret z + ld a, [hNPCPlayerRelativePosFlags] + cpl + and $3 + ld [hNPCPlayerRelativePosFlags], a + ret + +ConvertNPCMovementDirectionsToJoypadMasks: + ld a, [hNPCMovementDirections2Index] + ld [wNPCMovementDirections2Index], a + dec a + ld de, wSimulatedJoypadStatesEnd + ld hl, wNPCMovementDirections2 + add l + ld l, a + jr nc, .loop + inc h +.loop + ld a, [hld] + call ConvertNPCMovementDirectionToJoypadMask + ld [de], a + inc de + ld a, [hNPCMovementDirections2Index] + dec a + ld [hNPCMovementDirections2Index], a + jr nz, .loop + ret + +ConvertNPCMovementDirectionToJoypadMask: + push hl + ld b, a + ld hl, NPCMovementDirectionsToJoypadMasksTable +.loop + ld a, [hli] + cp $ff + jr z, .done + cp b + jr z, .loadJoypadMask + inc hl + jr .loop +.loadJoypadMask + ld a, [hl] +.done + pop hl + ret + +NPCMovementDirectionsToJoypadMasksTable: + db NPC_MOVEMENT_UP, D_UP + db NPC_MOVEMENT_DOWN, D_DOWN + db NPC_MOVEMENT_LEFT, D_LEFT + db NPC_MOVEMENT_RIGHT, D_RIGHT + db $ff + +; unreferenced + ret diff --git a/engine/overworld/oaks_aide.asm b/engine/overworld/oaks_aide.asm index d73174c4..54ba6b7a 100755 --- a/engine/overworld/oaks_aide.asm +++ b/engine/overworld/oaks_aide.asm @@ -1,4 +1,4 @@ -OaksAideScript: ; 0x59035 +OaksAideScript: ld hl, OaksAideHiText call PrintText call YesNoChoice diff --git a/engine/overworld/oam.asm b/engine/overworld/oam.asm index 94082beb..5b9831b0 100644 --- a/engine/overworld/oam.asm +++ b/engine/overworld/oam.asm @@ -1,12 +1,14 @@ PrepareOAMData: ; Determine OAM data for currently visible ; sprites and write it to wOAMBuffer. +; Yellow code has been changed to use registers more efficiently +; as well as tweaking the code to show gbc palettes ld a, [wUpdateSpritesEnabled] dec a jr z, .updateEnabled - cp 0 - 1 + cp $ff ret nz ld [wUpdateSpritesEnabled], a jp HideSprites @@ -18,9 +20,9 @@ PrepareOAMData: .spriteLoop ld [hSpriteOffset2], a - ld d, wSpriteStateData1 / $100 - ld a, [hSpriteOffset2] ld e, a + ld d, wSpriteStateData1 / $100 + ld a, [de] ; c1x0 and a jp z, .nextSprite @@ -40,16 +42,22 @@ PrepareOAMData: jr c, .usefacing ; unchanging - and $f - add $10 ; skip to the second half of the table which doesn't account for facing direction + ld a, $0 jr .next .usefacing and $f .next +; read the entry from the table + ld c, a + ld b, 0 + ld hl, SpriteFacingAndAnimationTable + add hl, bc + add hl, bc + ld a, [hli] + ld h, [hl] ld l, a - ; get sprite priority push de inc d @@ -61,65 +69,46 @@ PrepareOAMData: ld [hSpritePriority], a ; temp store sprite priority pop de -; read the entry from the table - ld h, 0 - ld bc, SpriteFacingAndAnimationTable - add hl, hl - add hl, hl - add hl, bc - ld a, [hli] - ld c, a - ld a, [hli] - ld b, a - ld a, [hli] - ld h, [hl] - ld l, a call GetSpriteScreenXY ld a, [hOAMBufferOffset] + add [hl] + cp $a0 + jr z, .hidden + jr nc, .asm_4a41 +.hidden + call Func_4a7b + ld [wd5cd], a + ld a, [hOAMBufferOffset] + ld e, a ld d, wOAMBuffer / $100 .tileLoop + ld a, [hli] + ld c, a +.loop ld a, [hSpriteScreenY] ; temp for sprite Y position add $10 ; Y=16 is top of screen (Y=0 is invisible) add [hl] ; add Y offset from table ld [de], a ; write new sprite OAM Y position inc hl + inc e ld a, [hSpriteScreenX] ; temp for sprite X position add $8 ; X=8 is left of screen (X=0 is invisible) add [hl] ; add X offset from table + ld [de], a + inc hl inc e - ld [de], a ; write new sprite OAM X position - inc e - ld a, [bc] ; read pattern number offset (accommodates orientation (offset 0,4 or 8) and animation (offset 0 or $80)) - inc bc - push bc + ld a, [wd5cd] + add [hl] + cp $80 + jr c, .asm_4a1c ld b, a - - ld a, [wd5cd] ; temp copy of c1x2 - swap a ; high nybble determines sprite used (0 is always player sprite, next are some npcs) - and $f - - ; Sprites $a and $b have one face (and therefore 4 tiles instead of 12). - ; As a result, sprite $b's tile offset is less than normal. - cp $b - jr nz, .notFourTileSprite - ld a, $a * 12 + 4 - jr .next2 - -.notFourTileSprite - ; a *= 12 - sla a - sla a - ld c, a - sla a - add c - -.next2 - add b ; add the tile offset from the table (based on frame and facing direction) - pop bc + ld a, [$fffc] + add b +.asm_4a1c ld [de], a ; tile id inc hl inc e @@ -129,15 +118,19 @@ PrepareOAMData: ld a, [hSpritePriority] or [hl] .skipPriority - inc hl + and $f0 + bit 4, a ; OBP0 or OBP1 + jr z, .spriteusesOBP0 + or %100 ; palettes 4-7 are OBP1 +.spriteusesOBP0 ld [de], a + inc hl inc e - bit 0, a ; OAMFLAG_ENDOFDATA - jr z, .tileLoop + dec c + jr nz, .loop ld a, e ld [hOAMBufferOffset], a - .nextSprite ld a, [hSpriteOffset2] add $10 @@ -145,26 +138,31 @@ PrepareOAMData: jp nz, .spriteLoop ; Clear unused OAM. - ld a, [hOAMBufferOffset] - ld l, a - ld h, wOAMBuffer / $100 - ld de, $4 - ld b, $a0 +.asm_4a41 ld a, [wd736] bit 6, a ; jumping down ledge or fishing animation? - ld a, $a0 + ld c, $a0 jr z, .clear ; Don't clear the last 4 entries because they are used for the shadow in the ; jumping down ledge animation and the rod in the fishing animation. - ld a, $90 + ld c, $90 .clear - cp l - ret z + ld a, [hOAMBufferOffset] + cp c + ret nc + ld l, a + ld h, wOAMBuffer / $100 + ld a, c + ld de, $4 ; entry size + ld b, $a0 +.clearLoop ld [hl], b add hl, de - jr .clear + cp l + jr nz, .clearLoop + ret GetSpriteScreenXY: inc e @@ -187,3 +185,48 @@ GetSpriteScreenXY: and $f0 ld [de], a ; c1xb (x) ret + +Func_4a7b: + push bc + ld a, [wd5cd] ; temp copy of c1x2 + swap a ; high nybble determines sprite used (0 is always player sprite, next are some npcs) + and $f + + ; Sprites $a and $b have one face (and therefore 4 tiles instead of 12). + ; As a result, sprite $b's tile offset is less than normal. + cp $b + jr nz, .notFourTileSprite + ld a, $a * 12 + 4 ; $7c + jr .done + +.notFourTileSprite + ; a *= 12 + add a + add a + ld c, a + add a + add c +.done + pop bc + ret + +INCLUDE "engine/oam_dma.asm" + +_IsTilePassable:: + ld hl,wTilesetCollisionPtr ; pointer to list of passable tiles + ld a,[hli] + ld h,[hl] + ld l,a ; hl now points to passable tiles +.loop + ld a,[hli] + cp a,$ff + jr z,.tileNotPassable + cp c + jr nz,.loop + xor a + ret +.tileNotPassable + scf + ret + +INCLUDE "data/collision.asm" ; probably diff --git a/engine/overworld/player_animations.asm b/engine/overworld/player_animations.asm index f7b63aaa..eea1b375 100755 --- a/engine/overworld/player_animations.asm +++ b/engine/overworld/player_animations.asm @@ -9,15 +9,15 @@ EnterMapAnim: bit 7, [hl] ; used fly out of battle? res 7, [hl] jr nz, .flyAnimation - ld a, SFX_TELEPORT_ENTER_1 + ld a, $a0 ; (SFX_02_4c - SFX_Headers_02) / 3 call PlaySound ld hl, wd732 bit 4, [hl] ; used dungeon warp? - res 4, [hl] pop hl + ;res 4, [hl] jr nz, .dungeonWarpAnimation call PlayerSpinWhileMovingDown - ld a, SFX_TELEPORT_ENTER_2 + ld a, $a3 ; (SFX_02_4f - SFX_Headers_02) / 3 call PlaySound call IsPlayerStandingOnWarpPadOrHole ld a, b @@ -34,23 +34,24 @@ EnterMapAnim: ld [hl], $ff ; wPlayerSpinInPlaceAnimSoundID ld hl, wFacingDirectionList call PlayerSpinInPlace + ld a, $1 + ld [wPikachuSpawnState], a .restoreDefaultMusic call PlayDefaultMusic .done + call Func_151d jp RestoreFacingDirectionAndYScreenPos .dungeonWarpAnimation ld c, 50 call DelayFrames call PlayerSpinWhileMovingDown + ld a, $0 + ld [wPikachuSpawnState], a jr .done .flyAnimation pop hl - ld de, BirdSprite - ld hl, vNPCSprites - lb bc, BANK(BirdSprite), $0c - call CopyVideoData call LoadBirdSpriteGraphics - ld a, SFX_FLY + ld a, $a4 ; SFX_BIRD_FLY call PlaySound ld hl, wFlyAnimUsingCoordList xor a ; is using coord list @@ -61,6 +62,8 @@ EnterMapAnim: ld de, FlyAnimationEnterScreenCoords call DoFlyAnimation call LoadPlayerSpriteGraphics + ld a, $1 + ld [wPikachuSpawnState], a jr .restoreDefaultMusic FlyAnimationEnterScreenCoords: @@ -90,7 +93,9 @@ PlayerSpinWhileMovingDown: ld [hl], a ; wPlayerSpinWhileMovingUpOrDownAnimFrameDelay jp PlayerSpinWhileMovingUpOrDown + _LeaveMapAnim: + call Func_1510 call InitFacingDirectionList call IsPlayerStandingOnWarpPadOrHole ld a, b @@ -99,7 +104,7 @@ _LeaveMapAnim: dec a jp nz, LeaveMapThroughHoleAnim .spinWhileMovingUp - ld a, SFX_TELEPORT_EXIT_1 + ld a, $9f ; (SFX_02_4b - SFX_Headers_02) / 3 call PlaySound ld hl, wPlayerSpinWhileMovingUpOrDownAnimDeltaY ld a, -$10 @@ -133,7 +138,7 @@ _LeaveMapAnim: ld [hli], a ; wPlayerSpinInPlaceAnimFrameDelayDelta xor a ld [hli], a ; wPlayerSpinInPlaceAnimFrameDelayEndValue - ld [hl], SFX_TELEPORT_EXIT_2 ; wPlayerSpinInPlaceAnimSoundID + ld [hl], $a1 ; SFX_TELEPORT_EXIT_2 ld hl, wFacingDirectionList call PlayerSpinInPlace jr .spinWhileMovingUp @@ -146,7 +151,7 @@ _LeaveMapAnim: ld [hli], a ; wFlyAnimCounter ld [hl], $c ; wFlyAnimBirdSpriteImageIndex call DoFlyAnimation - ld a, SFX_FLY + ld a, $a4 ; SFX_FLY call PlaySound ld hl, wFlyAnimUsingCoordList xor a ; is using coord list @@ -248,13 +253,15 @@ DoFlyAnimation: ret LoadBirdSpriteGraphics: - ld de, BirdSprite + ld de, BirdSprite ; $4d80 + ld b, BANK(BirdSprite) + ld c, $c ld hl, vNPCSprites - lb bc, BANK(BirdSprite), $0c call CopyVideoData - ld de, BirdSprite + $c0 ; moving animation sprite + ld de, BirdSprite + $c0 ; $4e40 ; moving amination sprite + ld b, BANK(BirdSprite) + ld c, $0c ld hl, vNPCSprites2 - lb bc, BANK(BirdSprite), $0c jp CopyVideoData InitFacingDirectionList: @@ -385,10 +392,11 @@ FishingAnim: ld c, 10 call DelayFrames ld hl, wd736 - set 6, [hl] ; reserve the last 4 OAM entries - ld de, RedSprite + set 6, [hl] ld hl, vNPCSprites - lb bc, BANK(RedSprite), $0c + ld de, RedSprite ; $4180 + ld b, BANK(RedSprite) + ld c, $c call CopyVideoData ld a, $4 ld hl, RedFishingTiles @@ -414,6 +422,7 @@ FishingAnim: ; there was a bite ; shake the player's sprite vertically + ld b, 10 .loop ld hl, wSpriteStateData1 + 4 ; player's sprite Y screen position @@ -426,19 +435,17 @@ FishingAnim: ; If the player is facing up, hide the fishing rod so it doesn't overlap with ; the exclamation bubble that will be shown next. - ld a, [wSpriteStateData1 + 2] ; player's sprite facing direction + ld a, [wSpriteStateData1 + 2] cp SPRITE_FACING_UP jr nz, .skipHidingFishingRod ld a, $a0 ld [wOAMBuffer + $9c], a - .skipHidingFishingRod ld hl, wEmotionBubbleSpriteIndex xor a ld [hli], a ; player's sprite ld [hl], a ; EXCLAMATION_BUBBLE predef EmotionBubble - ; If the player is facing up, unhide the fishing rod. ld a, [wSpriteStateData1 + 2] ; player's sprite facing direction cp SPRITE_FACING_UP @@ -456,7 +463,7 @@ FishingAnim: call LoadFontTilePatterns ret -.ShakePlayerSprite +.ShakePlayerSprite ; 708a3 (1c:48a3) ld a, [hl] xor $1 ld [hl], a diff --git a/engine/overworld/pokecenter.asm b/engine/overworld/pokecenter.asm index 3a302d70..1801d9e9 100755 --- a/engine/overworld/pokecenter.asm +++ b/engine/overworld/pokecenter.asm @@ -1,4 +1,13 @@ DisplayPokemonCenterDialogue_: + ld a, [wCurMap] + cp PEWTER_POKECENTER + jr nz, .regularCenter + call CheckPikachuFollowingPlayer + jr z, .regularCenter + ld hl, LooksContentText ; if pikachu is sleeping, don't heal + call PrintText + ret +.regularCenter call SaveScreenTilesToBuffer1 ; save screen ld hl, PokemonCenterWelcomeText call PrintText @@ -11,18 +20,36 @@ DisplayPokemonCenterDialogue_: call PrintText .skipShallWeHealYourPokemon call YesNoChoicePokeCenter ; yes/no menu + call UpdateSprites ld a, [wCurrentMenuItem] and a - jr nz, .declinedHealing ; if the player chose No + jp nz, .declinedHealing ; if the player chose No call SetLastBlackoutMap - call LoadScreenTilesFromBuffer1 ; restore screen + callab IsStarterPikachuInOurParty + jr nc, .notHealingPlayerPikachu + call CheckPikachuFollowingPlayer + jr nz, .notHealingPlayerPikachu + call LoadCurrentMapView + call Delay3 + call UpdateSprites + callab PikachuWalksToNurseJoy ; todo +.notHealingPlayerPikachu ld hl, NeedYourPokemonText call PrintText - ld a, $18 - ld [wSpriteStateData1 + $12], a ; make the nurse turn to face the machine - call Delay3 - predef HealParty + ld c, 64 + call DelayFrames + call CheckPikachuFollowingPlayer + jr nz, .playerPikachuNotOnScreen + call DisablePikachuOverworldSpriteDrawing + callab IsStarterPikachuInOurParty + call c, Func_6eaa +.playerPikachuNotOnScreen + lb bc, 1, 8 + call Func_6ebb + ld c, 30 + call DelayFrames callba AnimateHealingMachine ; do the healing machine animation + predef HealParty xor a ld [wAudioFadeOutControl], a ld a, [wAudioSavedROMBank] @@ -31,19 +58,69 @@ DisplayPokemonCenterDialogue_: ld [wLastMusicSoundID], a ld [wNewSoundID], a call PlaySound + call CheckPikachuFollowingPlayer + jr nz, .doNotReturnPikachu + callab IsStarterPikachuInOurParty + call c, Func_6eaa + ld a, $5 + ld [wPikachuSpawnState], a + call EnablePikachuOverworldSpriteDrawing +.doNotReturnPikachu + lb bc, 1, 0 + call Func_6ebb ld hl, PokemonFightingFitText call PrintText - ld a, $14 - ld [wSpriteStateData1 + $12], a ; make the nurse bow - ld c, a + callab IsStarterPikachuInOurParty + jr nc, .notInParty + lb bc, 15, 0 + call Func_6ebb +.notInParty + call LoadCurrentMapView + call Delay3 + call UpdateSprites + callab ReloadWalkingTilePatterns + ld a, $1 + ld [H_SPRITEINDEX], a + ld a, $1 + ld [hSpriteImageIndex], a + call SpriteFunc_34a1 + ld c, 40 call DelayFrames + call UpdateSprites + call LoadFontTilePatterns jr .done .declinedHealing call LoadScreenTilesFromBuffer1 ; restore screen .done ld hl, PokemonCenterFarewellText call PrintText - jp UpdateSprites + call UpdateSprites + ret + +Func_6eaa: + ld a, $1 + ld [H_SPRITEINDEX], a + ld a, $4 + ld [hSpriteImageIndex], a + call SpriteFunc_34a1 + ld c, 64 + call DelayFrames + ret + +Func_6ebb: + ld a, b + ld [H_SPRITEINDEX], a + ld a, c + ld [hSpriteImageIndex], a + push bc + call SetSpriteFacingDirectionAndDelay + pop bc + ld a, b + ld [H_SPRITEINDEX], a + ld a, c + ld [hSpriteImageIndex], a + call SpriteFunc_34a1 + ret PokemonCenterWelcomeText: TX_FAR _PokemonCenterWelcomeText @@ -66,3 +143,7 @@ PokemonCenterFarewellText: db $a TX_FAR _PokemonCenterFarewellText db "@" + +LooksContentText: + TX_FAR _LooksContentText + db "@" diff --git a/engine/overworld/pokemart.asm b/engine/overworld/pokemart.asm index e50c508e..823939b1 100755 --- a/engine/overworld/pokemart.asm +++ b/engine/overworld/pokemart.asm @@ -85,7 +85,7 @@ DisplayPokemartDialogue_: lb bc, 14, 1 ; location that PrintText always prints to, this is useless call PrintText coord hl, 14, 7 - lb bc, 08, 15 + lb bc, 8, 15 ld a,TWO_OPTION_MENU ld [wTextBoxID],a call DisplayTextBoxID ; yes/no menu diff --git a/engine/overworld/print_safari_steps.asm b/engine/overworld/print_safari_steps.asm new file mode 100644 index 00000000..01dd34e0 --- /dev/null +++ b/engine/overworld/print_safari_steps.asm @@ -0,0 +1,36 @@ +PrintSafariZoneSteps: + ld a, [wCurMap] + cp SAFARI_ZONE_EAST + ret c + cp UNKNOWN_DUNGEON_2 + ret nc + coord hl, 0, 0 + lb bc, 3, 7 + call TextBoxBorder + coord hl, 1, 1 + ld de, wSafariSteps + lb bc, 2, 3 + call PrintNumber + coord hl, 4, 1 + ld de, SafariSteps + call PlaceString + coord hl, 1, 3 + ld de, SafariBallText + call PlaceString + ld a, [wNumSafariBalls] + cp 10 + jr nc, .numSafariBallsTwoDigits + coord hl, 5, 3 + ld a, " " + ld [hl], a +.numSafariBallsTwoDigits + coord hl, 6, 3 + ld de, wNumSafariBalls + lb bc, 1, 2 + jp PrintNumber + +SafariSteps: + db "/500@" + +SafariBallText: + db "BALL×× @" diff --git a/engine/overworld/replace_tile_block.asm b/engine/overworld/replace_tile_block.asm new file mode 100644 index 00000000..8577b9e7 --- /dev/null +++ b/engine/overworld/replace_tile_block.asm @@ -0,0 +1,126 @@ +; replaces a tile block with the one specified in [wNewTileBlockID] +; and redraws the map view if necessary +; b = Y +; c = X +ReplaceTileBlock: + call GetPredefRegisters + ld hl, wOverworldMap + ld a, [wCurMapWidth] + add $6 + ld e, a + ld d, $0 + add hl, de + add hl, de + add hl, de + ld e, $3 + add hl, de + ld e, a + ld a, b + and a + jr z, .addX +; add width * Y +.addWidthYTimesLoop + add hl, de + dec b + jr nz, .addWidthYTimesLoop +.addX + add hl, bc ; add X + ld a, [wNewTileBlockID] + ld [hl], a + ld a, [wCurrentTileBlockMapViewPointer] + ld c, a + ld a, [wCurrentTileBlockMapViewPointer + 1] + ld b, a + call CompareHLWithBC + ret c ; return if the replaced tile block is below the map view in memory + push hl + ld l, e + ld h, $0 + ld e, $6 + ld d, h + add hl, hl + add hl, hl + add hl, de + add hl, bc + pop bc + call CompareHLWithBC + ret c ; return if the replaced tile block is above the map view in memory + +RedrawMapView: + ld a, [wIsInBattle] + inc a + ret z + ld a, [H_AUTOBGTRANSFERENABLED] + push af + ld a, [hTilesetType] + push af + xor a + ld [H_AUTOBGTRANSFERENABLED], a + ld [hTilesetType], a ; no flower/water BG tile animations + call LoadCurrentMapView + call RunDefaultPaletteCommand + ld hl, wMapViewVRAMPointer + ld a, [hli] + ld h, [hl] + ld l, a + ld de, -2 * 32 + add hl, de + ld a, h + and $3 + or $98 + ld a, l + ld [wBuffer], a + ld a, h + ld [wBuffer + 1], a ; this copy of the address is not used + ld a, 2 + ld [$ffbe], a + ld c, 9 ; number of rows of 2x2 tiles (this covers the whole screen) +.redrawRowLoop + push bc + push hl + push hl + ld hl, wTileMap - 2 * SCREEN_WIDTH + ld de, SCREEN_WIDTH + ld a, [$ffbe] +.calcWRAMAddrLoop + add hl, de + dec a + jr nz, .calcWRAMAddrLoop + call CopyToRedrawRowOrColumnSrcTiles + pop hl + ld de, $20 + ld a, [$ffbe] + ld c, a +.calcVRAMAddrLoop + add hl, de + ld a, h + and $3 + or $98 + dec c + jr nz, .calcVRAMAddrLoop + ld [hRedrawRowOrColumnDest + 1], a + ld a, l + ld [hRedrawRowOrColumnDest], a + ld a, REDRAW_ROW + ld [hRedrawRowOrColumnMode], a + call DelayFrame + ld hl, $ffbe + inc [hl] + inc [hl] + pop hl + pop bc + dec c + jr nz, .redrawRowLoop + pop af + ld [hTilesetType], a + pop af + ld [H_AUTOBGTRANSFERENABLED], a + ret + +CompareHLWithBC: + ld a, h + sub b + ret nz + ld a, l + sub c + ret diff --git a/engine/overworld/set_blackout_map.asm b/engine/overworld/set_blackout_map.asm new file mode 100644 index 00000000..9bfe82bd --- /dev/null +++ b/engine/overworld/set_blackout_map.asm @@ -0,0 +1,29 @@ +SetLastBlackoutMap: +; Set the map to return to when +; blacking out or using Teleport or Dig. +; Safari rest houses don't count. + + push hl + ld hl, SafariZoneRestHouses + ld a, [wCurMap] + ld b, a +.loop + ld a, [hli] + cp -1 + jr z, .notresthouse + cp b + jr nz, .loop + jr .done + +.notresthouse + ld a, [wLastMap] + ld [wLastBlackoutMap], a +.done + pop hl + ret + +SafariZoneRestHouses: + db SAFARI_ZONE_REST_HOUSE_2 + db SAFARI_ZONE_REST_HOUSE_3 + db SAFARI_ZONE_REST_HOUSE_4 + db -1 diff --git a/engine/overworld/special_warps.asm b/engine/overworld/special_warps.asm new file mode 100644 index 00000000..4814e668 --- /dev/null +++ b/engine/overworld/special_warps.asm @@ -0,0 +1,147 @@ +SpecialWarpIn: + call LoadSpecialWarpData + predef LoadTilesetHeader + ld hl,wd732 + bit 2,[hl] ; dungeon warp or fly warp? + res 2,[hl] + jr z,.next +; if dungeon warp or fly warp + ld a,[wDestinationMap] + jr .next2 +.next + bit 1,[hl] + jr z,.next3 + call EmptyFunc +.next3 + ld a,0 +.next2 + ld b,a + ld a,[wd72d] + and a + jr nz,.next4 + ld a,b +.next4 + ld hl,wd732 + bit 4,[hl] ; dungeon warp? + ret nz +; if not dungeon warp + ld [wLastMap],a + ret + +; gets the map ID, tile block map view pointer, tileset, and coordinates +LoadSpecialWarpData: + ld a, [wd72d] + cp TRADE_CENTER + jr nz, .notTradeCenter + ld hl, TradeCenterSpec1 + ld a, [hSerialConnectionStatus] + cp USING_INTERNAL_CLOCK ; which gameboy is clocking determines who is on the left and who is on the right + jr z, .copyWarpData + ld hl, TradeCenterSpec2 + jr .copyWarpData +.notTradeCenter + cp COLOSSEUM + jr nz, .notColosseum + ld hl, ColosseumSpec1 + ld a, [hSerialConnectionStatus] + cp USING_INTERNAL_CLOCK + jr z, .copyWarpData + ld hl, ColosseumSpec2 + jr .copyWarpData +.notColosseum + ld a, [wd732] + bit 1, a + jr nz, .notFirstMap + bit 2, a + jr nz, .notFirstMap + ld hl, FirstMapSpec +.copyWarpData + ld de, wCurMap + ld c, $7 +.copyWarpDataLoop + ld a, [hli] + ld [de], a + inc de + dec c + jr nz, .copyWarpDataLoop + ld a, [hli] + ld [wCurMapTileset], a + xor a + jr .done +.notFirstMap + ld a, [wLastMap] ; this value is overwritten before it's ever read + ld hl, wd732 + bit 4, [hl] ; used dungeon warp (jumped down hole/waterfall)? + jr nz, .usedDunegonWarp + bit 6, [hl] ; return to last pokemon center (or player's house)? + res 6, [hl] + jr z, .otherDestination +; return to last pokemon center or player's house + ld a, [wLastBlackoutMap] + jr .usedFlyWarp +.usedDunegonWarp + ld hl, wd72d + res 4, [hl] + ld a, [wDungeonWarpDestinationMap] + ld b, a + ld [wCurMap], a + ld a, [wWhichDungeonWarp] + ld c, a + ld hl, DungeonWarpList + ld de, 0 + ld a, 6 + ld [wDungeonWarpDataEntrySize], a +.dungeonWarpListLoop + ld a, [hli] + cp b + jr z, .matchedDungeonWarpDestinationMap + inc hl + jr .nextDungeonWarp +.matchedDungeonWarpDestinationMap + ld a, [hli] + cp c + jr z, .matchedDungeonWarpID +.nextDungeonWarp + ld a, [wDungeonWarpDataEntrySize] + add e + ld e, a + jr .dungeonWarpListLoop +.matchedDungeonWarpID + ld hl, DungeonWarpData + add hl, de + jr .copyWarpData2 +.otherDestination + ld a, [wDestinationMap] +.usedFlyWarp + ld b, a + ld [wCurMap], a + ld hl, FlyWarpDataPtr +.flyWarpDataPtrLoop + ld a, [hli] + inc hl + cp b + jr z, .foundFlyWarpMatch + inc hl + inc hl + jr .flyWarpDataPtrLoop +.foundFlyWarpMatch + ld a, [hli] + ld h, [hl] + ld l, a +.copyWarpData2 + ld de, wCurrentTileBlockMapViewPointer + ld c, $6 +.copyWarpDataLoop2 + ld a, [hli] + ld [de], a + inc de + dec c + jr nz, .copyWarpDataLoop2 + xor a ; OVERWORLD + ld [wCurMapTileset], a +.done + ld [wYOffsetSinceLastSpecialWarp], a + ld [wXOffsetSinceLastSpecialWarp], a + ld a, $ff ; the player's coordinates have already been updated using a special warp, so don't use any of the normal warps + ld [wDestinationWarpID], a + ret diff --git a/engine/overworld/ssanne.asm b/engine/overworld/ssanne.asm index 712c53ed..347dc459 100755 --- a/engine/overworld/ssanne.asm +++ b/engine/overworld/ssanne.asm @@ -7,6 +7,7 @@ AnimateBoulderDust: ld [wUpdateSpritesEnabled], a ld a, %11100100 ld [rOBP1], a + call UpdateGBCPal_OBP1 call LoadSmokeTileFourTimes callba WriteCutOrBoulderDustAnimationOAMBlock ld c, 8 ; number of steps in animation @@ -21,6 +22,7 @@ AnimateBoulderDust: ld a, [rOBP1] xor %01100100 ld [rOBP1], a + call UpdateGBCPal_OBP1 call Delay3 pop bc dec c @@ -30,7 +32,7 @@ AnimateBoulderDust: jp LoadPlayerSpriteGraphics GetMoveBoulderDustFunctionPointer: - ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction + ld a, [wPlayerFacingDirection] ; player's sprite facing direction ld hl, MoveBoulderDustFunctionPointerTable ld c, a ld b, $0 diff --git a/engine/overworld/step_functions.asm b/engine/overworld/step_functions.asm new file mode 100644 index 00000000..84b09291 --- /dev/null +++ b/engine/overworld/step_functions.asm @@ -0,0 +1,151 @@ +ApplyOutOfBattlePoisonDamage: + ld a, [wd730] + add a + jp c, .noBlackOut ; no black out if joypad states are being simulated + ld a, [wd492] + bit 7, a + jp nz, .noBlackOut + ld a, [wd72e] + bit 6, a + jp nz, .noBlackOut + ld a, [wPartyCount] + and a + jp z, .noBlackOut + call IncrementDayCareMonExp + call Func_c4c7 + ld a, [wStepCounter] + and $3 ; is the counter a multiple of 4? + jp nz, .skipPoisonEffectAndSound ; only apply poison damage every fourth step + ld [wWhichPokemon], a + ld hl, wPartyMon1Status + ld de, wPartySpecies +.applyDamageLoop + ld a, [hl] + and (1 << PSN) + jr z, .nextMon2 ; not poisoned + dec hl + dec hl + ld a, [hld] + ld b, a + ld a, [hli] + or b + jr z, .nextMon ; already fainted +; subtract 1 from HP + ld a, [hl] + dec a + ld [hld], a + inc a + jr nz, .noBorrow +; borrow 1 from upper byte of HP + dec [hl] + inc hl + jr .nextMon +.noBorrow + ld a, [hli] + or [hl] + jr nz, .nextMon ; didn't faint from damage +; the mon fainted from the damage + push hl + inc hl + inc hl + ld [hl], a + ld a, [de] + ld [wd11e], a + push de + ld a, [wWhichPokemon] + ld hl, wPartyMonNicks + call GetPartyMonName + xor a + ld [wJoyIgnore], a + call EnableAutoTextBoxDrawing + ld a, $d0 + ld [hSpriteIndexOrTextID], a + call DisplayTextID + callab IsThisPartymonStarterPikachu_Party + jr nc, .curMonNotPlayerPikachu + ld e, $3 + callab PlayPikachuSoundClip + calladb_ModifyPikachuHappiness PIKAHAPPY_PSNFNT +.curMonNotPlayerPikachu + pop de + pop hl +.nextMon + inc hl + inc hl +.nextMon2 + inc de + ld a, [de] + inc a + jr z, .applyDamageLoopDone + ld bc, wPartyMon2 - wPartyMon1 + add hl, bc + push hl + ld hl, wWhichPokemon + inc [hl] + pop hl + jr .applyDamageLoop +.applyDamageLoopDone + ld hl, wPartyMon1Status + ld a, [wPartyCount] + ld d, a + ld e, 0 +.countPoisonedLoop + ld a, [hl] + and (1 << PSN) + or e + ld e, a + ld bc, wPartyMon2 - wPartyMon1 + add hl, bc + dec d + jr nz, .countPoisonedLoop + ld a, e + and a ; are any party members poisoned? + jr z, .skipPoisonEffectAndSound + ld b, $2 + predef InvertBGPal_4Frames ; change BG white to dark grey for 4 frames + ld a, SFX_POISONED + call PlaySound +.skipPoisonEffectAndSound + predef AnyPartyAlive + ld a, d + and a + jr nz, .noBlackOut + call EnableAutoTextBoxDrawing + ld a, $d1 + ld [hSpriteIndexOrTextID], a + call DisplayTextID + ld hl, wd72e + set 5, [hl] + ld a, $ff + jr .done +.noBlackOut + xor a +.done + ld [wOutOfBattleBlackout], a + ret + +Func_c4c7: + ld a, [wStepCounter] + and a + jr nz, .asm_c4de + call Random + and $1 + jr z, .asm_c4de + calladb_ModifyPikachuHappiness $6 +.asm_c4de + ld hl, wPikachuMood + ld a, [hl] + cp $80 + jr z, .asm_c4ef + jr c, .asm_c4ea + dec a + dec a +.asm_c4ea + inc a + ld [hl], a + cp $80 + ret nz +.asm_c4ef + xor a + ld [wd49c], a + ret diff --git a/engine/overworld/trainers.asm b/engine/overworld/trainers.asm index 1d0340c9..3ee19e8a 100755 --- a/engine/overworld/trainers.asm +++ b/engine/overworld/trainers.asm @@ -4,16 +4,16 @@ _GetSpritePosition1: ld a, [wSpriteIndex] ld [H_SPRITEINDEX], a call GetSpriteDataPointer - ld a, [hli] + ld a, [hli] ; c1x4 (screen Y pos) ld [$ffeb], a inc hl - ld a, [hl] + ld a, [hl] ; c1x6 (screen X pos) ld [$ffec], a - ld de, $fe + ld de, (wSpriteStateData2 + $4) - (wSpriteStateData1 + $6) add hl, de - ld a, [hli] + ld a, [hli] ; c2x4 (map Y pos) ld [$ffed], a - ld a, [hl] + ld a, [hl] ; c2x5 (map X pos) ld [$ffee], a ret @@ -28,7 +28,7 @@ _GetSpritePosition2: inc hl ld a, [hl] ; c1x6 (screen X pos) ld [wSavedSpriteScreenX], a - ld de, $104 - $6 + ld de, (wSpriteStateData2 + $4) - (wSpriteStateData1 + $6) add hl, de ld a, [hli] ; c2x4 (map Y pos) ld [wSavedSpriteMapY], a @@ -47,7 +47,7 @@ _SetSpritePosition1: inc hl ld a, [$ffec] ; c1x6 (screen X pos) ld [hl], a - ld de, $104 - $6 + ld de, (wSpriteStateData2 + $4) - (wSpriteStateData1 + $6) add hl, de ld a, [$ffed] ; c2x4 (map Y pos) ld [hli], a @@ -57,21 +57,21 @@ _SetSpritePosition1: _SetSpritePosition2: ld hl, wSpriteStateData1 - ld de, $0004 + ld de, $4 ld a, [wSpriteIndex] ld [H_SPRITEINDEX], a call GetSpriteDataPointer ld a, [wSavedSpriteScreenY] - ld [hli], a + ld [hli], a ; c1x4 (screen Y pos) inc hl ld a, [wSavedSpriteScreenX] - ld [hl], a - ld de, $00fe + ld [hl], a ; c1x6 (screen X pos) + ld de, (wSpriteStateData2 + $4) - (wSpriteStateData1 + $6) add hl, de ld a, [wSavedSpriteMapY] - ld [hli], a + ld [hli], a ; c2x4 (map Y pos) ld a, [wSavedSpriteMapX] - ld [hl], a + ld [hl], a ; c2x5 (map X pos) ret TrainerWalkUpToPlayer: @@ -80,11 +80,11 @@ TrainerWalkUpToPlayer: ld [wTrainerSpriteOffset], a call ReadTrainerScreenPosition ld a, [wTrainerFacingDirection] - and a + and a ; SPRITE_FACING_DOWN jr z, .facingDown - cp $4 + cp SPRITE_FACING_UP jr z, .facingUp - cp $8 + cp SPRITE_FACING_LEFT jr z, .facingLeft jr .facingRight .facingDown @@ -148,7 +148,7 @@ TrainerWalkUpToPlayer: jp MoveSprite_ ; input: de = offset within sprite entry -; output: de = pointer to sprite data +; output: hl = pointer to sprite data GetSpriteDataPointer: push de add hl, de @@ -225,7 +225,7 @@ TrainerEngage: set 0, [hl] call EngageMapTrainer ld a, $ff -.noEngage: +.noEngage ld [wTrainerSpriteOffset], a pop de pop hl @@ -239,7 +239,7 @@ ReadTrainerScreenPosition: ld e, a ld hl, wSpriteStateData1 add hl, de - ld a, [hl] + ld a, [hl] ; c1x4 (sprite Y pos) ld [wTrainerScreenY], a ld a, [wTrainerSpriteOffset] add $6 @@ -247,7 +247,7 @@ ReadTrainerScreenPosition: ld e, a ld hl, wSpriteStateData1 add hl, de - ld a, [hl] + ld a, [hl] ; c1x6 (sprite X pos) ld [wTrainerScreenX], a ret @@ -262,13 +262,13 @@ CheckSpriteCanSeePlayer: jr .notInLine ; player too far away .checkIfLinedUp ld a, [wTrainerFacingDirection] ; sprite facing direction - cp $0 ; down + cp SPRITE_FACING_DOWN ; down jr z, .checkXCoord - cp $4 ; up + cp SPRITE_FACING_UP ; up jr z, .checkXCoord - cp $8 ; left + cp SPRITE_FACING_LEFT ; left jr z, .checkYCoord - cp $c ; right + cp SPRITE_FACING_RIGHT ; right jr z, .checkYCoord jr .notInLine .checkXCoord @@ -315,21 +315,21 @@ CheckPlayerIsInFrontOfSprite: ld a, [hl] ; c1x6 (sprite screen X pos) ld [wTrainerScreenX], a ld a, [wTrainerFacingDirection] ; facing direction - cp $0 + cp SPRITE_FACING_DOWN jr nz, .notFacingDown ld a, [wTrainerScreenY] ; sprite screen Y pos cp $3c jr c, .engage ; sprite above player jr .noEngage ; sprite below player .notFacingDown - cp $4 + cp SPRITE_FACING_UP jr nz, .notFacingUp ld a, [wTrainerScreenY] ; sprite screen Y pos cp $3c jr nc, .engage ; sprite below player jr .noEngage ; sprite above player .notFacingUp - cp $8 + cp SPRITE_FACING_LEFT jr nz, .notFacingLeft ld a, [wTrainerScreenX] ; sprite screen X pos cp $40 diff --git a/engine/overworld/try_pushing_boulder.asm b/engine/overworld/try_pushing_boulder.asm new file mode 100644 index 00000000..a00790b9 --- /dev/null +++ b/engine/overworld/try_pushing_boulder.asm @@ -0,0 +1,107 @@ +TryPushingBoulder: + ld a, [wd728] + bit 0, a ; using Strength? + ret z +Func_f0a7: +; where LoadMissableObjects predef points to now + ld a, [wFlags_0xcd60] + bit 1, a ; has boulder dust animation from previous push played yet? + ret nz + xor a + ld [hSpriteIndexOrTextID], a + call IsSpriteInFrontOfPlayer + ld a, [hSpriteIndexOrTextID] + ld [wBoulderSpriteIndex], a + and a + jp z, ResetBoulderPushFlags + ld hl, wSpriteStateData1 + 1 + ld d, $0 + ld a, [hSpriteIndexOrTextID] + swap a + ld e, a + add hl, de + res 7, [hl] + call GetSpriteMovementByte2Pointer + ld a, [hl] + cp BOULDER_MOVEMENT_BYTE_2 + jp nz, ResetBoulderPushFlags + ld hl, wFlags_0xcd60 + bit 6, [hl] + set 6, [hl] ; indicate that the player has tried pushing + ret z ; the player must try pushing twice before the boulder will move + ld a, [hJoyHeld] + and D_RIGHT | D_LEFT | D_UP | D_DOWN + ret z + predef CheckForCollisionWhenPushingBoulder + ld a, [wTileInFrontOfBoulderAndBoulderCollisionResult] + and a ; was there a collision? + jp nz, ResetBoulderPushFlags + ld a, [hJoyHeld] + ld b, a + ld a, [wPlayerFacingDirection] ; player's sprite facing direction + cp SPRITE_FACING_UP + jr z, .pushBoulderUp + cp SPRITE_FACING_LEFT + jr z, .pushBoulderLeft + cp SPRITE_FACING_RIGHT + jr z, .pushBoulderRight +.pushBoulderDown + bit 7, b + ret z + ld de, PushBoulderDownMovementData + jr .done +.pushBoulderUp + bit 6, b + ret z + ld de, PushBoulderUpMovementData + jr .done +.pushBoulderLeft + bit 5, b + ret z + ld de, PushBoulderLeftMovementData + jr .done +.pushBoulderRight + bit 4, b + ret z + ld de, PushBoulderRightMovementData +.done + call MoveSprite + ld a, SFX_PUSH_BOULDER + call PlaySound + ld hl, wFlags_0xcd60 + set 1, [hl] + ret + +PushBoulderUpMovementData: + db NPC_MOVEMENT_UP,$FF + +PushBoulderDownMovementData: + db NPC_MOVEMENT_DOWN,$FF + +PushBoulderLeftMovementData: + db NPC_MOVEMENT_LEFT,$FF + +PushBoulderRightMovementData: + db NPC_MOVEMENT_RIGHT,$FF + +DoBoulderDustAnimation: + ld a, [wd730] + bit 0, a + ret nz + callab AnimateBoulderDust + call DiscardButtonPresses + ld [wJoyIgnore], a + call ResetBoulderPushFlags + set 7, [hl] + ld a, [wBoulderSpriteIndex] + ld [H_SPRITEINDEX], a + call GetSpriteMovementByte2Pointer + ld [hl], $10 + ld a, SFX_CUT + jp PlaySound + +ResetBoulderPushFlags: + ld hl, wFlags_0xcd60 + res 1, [hl] + res 6, [hl] + ret |