diff options
Diffstat (limited to 'engine/gfx')
-rwxr-xr-x | engine/gfx/hp_bar.asm | 268 | ||||
-rwxr-xr-x | engine/gfx/load_pokedex_tiles.asm | 11 | ||||
-rwxr-xr-x | engine/gfx/mon_icons.asm | 307 | ||||
-rw-r--r-- | engine/gfx/oam_dma.asm | 28 | ||||
-rwxr-xr-x | engine/gfx/palettes.asm | 1117 | ||||
-rw-r--r-- | engine/gfx/screen_effects.asm | 73 | ||||
-rw-r--r-- | engine/gfx/sprite_oam.asm | 232 |
7 files changed, 2036 insertions, 0 deletions
diff --git a/engine/gfx/hp_bar.asm b/engine/gfx/hp_bar.asm new file mode 100755 index 00000000..b47b1fbd --- /dev/null +++ b/engine/gfx/hp_bar.asm @@ -0,0 +1,268 @@ +HPBarLength: + call GetPredefRegisters + +; calculates bc * 48 / de, the number of pixels the HP bar has +; the result is always at least 1 +GetHPBarLength: + push hl + xor a + ld hl, hMultiplicand + ld [hli], a + ld a, b + ld [hli], a + ld a, c + ld [hli], a + ld [hl], $30 + call Multiply ; 48 * bc (hp bar is 48 pixels long) + ld a, d + and a + jr z, .maxHPSmaller256 + srl d ; make HP in de fit into 1 byte by dividing by 4 + rr e + srl d + rr e + ldh a, [hMultiplicand+1] + ld b, a + ldh a, [hMultiplicand+2] + srl b ; divide multiplication result as well + rr a + srl b + rr a + ldh [hMultiplicand+2], a + ld a, b + ldh [hMultiplicand+1], a +.maxHPSmaller256 + ld a, e + ldh [hDivisor], a + ld b, $4 + call Divide + ldh a, [hMultiplicand+2] + ld e, a ; e = bc * 48 / de (num of pixels of HP bar) + pop hl + and a + ret nz + ld e, $1 ; make result at least 1 + ret + +; predef $48 +UpdateHPBar: +UpdateHPBar2: + push hl + ld hl, wHPBarOldHP + ld a, [hli] + ld c, a ; old HP into bc + ld a, [hli] + ld b, a + ld a, [hli] + ld e, a ; new HP into de + ld d, [hl] + pop hl + push de + push bc + call UpdateHPBar_CalcHPDifference + ld a, e + ld [wHPBarHPDifference+1], a + ld a, d + ld [wHPBarHPDifference], a + pop bc + pop de + call UpdateHPBar_CompareNewHPToOldHP + ret z + ld a, $ff + jr c, .HPdecrease + ld a, $1 +.HPdecrease + ld [wHPBarDelta], a + call GetPredefRegisters + ld a, [wHPBarNewHP] + ld e, a + ld a, [wHPBarNewHP+1] + ld d, a +.animateHPBarLoop + push de + ld a, [wHPBarOldHP] + ld c, a + ld a, [wHPBarOldHP+1] + ld b, a + call UpdateHPBar_CompareNewHPToOldHP + jr z, .animateHPBarDone + jr nc, .HPIncrease +; HP decrease + dec bc ; subtract 1 HP + ld a, c + ld [wHPBarNewHP], a + ld a, b + ld [wHPBarNewHP+1], a + call UpdateHPBar_CalcOldNewHPBarPixels + ld a, e + sub d ; calc pixel difference + jr .ok +.HPIncrease + inc bc ; add 1 HP + ld a, c + ld [wHPBarNewHP], a + ld a, b + ld [wHPBarNewHP+1], a + call UpdateHPBar_CalcOldNewHPBarPixels + ld a, d + sub e ; calc pixel difference +.ok + call UpdateHPBar_PrintHPNumber + and a + jr z, .noPixelDifference + call UpdateHPBar_AnimateHPBar +.noPixelDifference + ld a, [wHPBarNewHP] + ld [wHPBarOldHP], a + ld a, [wHPBarNewHP+1] + ld [wHPBarOldHP+1], a + pop de + jr .animateHPBarLoop +.animateHPBarDone + pop de + ld a, e + ld [wHPBarOldHP], a + ld a, d + ld [wHPBarOldHP+1], a + or e + jr z, .monFainted + call UpdateHPBar_CalcOldNewHPBarPixels + ld d, e +.monFainted + call UpdateHPBar_PrintHPNumber + ld a, $1 + call UpdateHPBar_AnimateHPBar + jp Delay3 + +; animates the HP bar going up or down for (a) ticks (two waiting frames each) +; stops prematurely if bar is filled up +; e: current health (in pixels) to start with +UpdateHPBar_AnimateHPBar: + push hl +.barAnimationLoop + push af + push de + ld d, $6 + call DrawHPBar + ld c, 2 + call DelayFrames + pop de + ld a, [wHPBarDelta] ; +1 or -1 + add e + cp $31 + jr nc, .barFilledUp + ld e, a + pop af + dec a + jr nz, .barAnimationLoop + pop hl + ret +.barFilledUp + pop af + pop hl + ret + +; compares old HP and new HP and sets c and z flags accordingly +UpdateHPBar_CompareNewHPToOldHP: + ld a, d + sub b + ret nz + ld a, e + sub c + ret + +; calcs HP difference between bc and de (into de) +UpdateHPBar_CalcHPDifference: + ld a, d + sub b + jr c, .oldHPGreater + jr z, .testLowerByte +.newHPGreater + ld a, e + sub c + ld e, a + ld a, d + sbc b + ld d, a + ret +.oldHPGreater + ld a, c + sub e + ld e, a + ld a, b + sbc d + ld d, a + ret +.testLowerByte + ld a, e + sub c + jr c, .oldHPGreater + jr nz, .newHPGreater + ld de, $0 + ret + +UpdateHPBar_PrintHPNumber: + push af + push de + ld a, [wHPBarType] + and a + jr z, .done ; don't print number in enemy HUD +; convert from little-endian to big-endian for PrintNumber + ld a, [wHPBarOldHP] + ld [wHPBarTempHP + 1], a + ld a, [wHPBarOldHP + 1] + ld [wHPBarTempHP], a + push hl + ld de, $15 + ldh a, [hFlagsFFFA] + bit 0, a + jr z, .next + ld de, $9 +.next + add hl, de + push hl + ld a, " " + ld [hli], a + ld [hli], a + ld [hli], a + pop hl + ld de, wHPBarTempHP + lb bc, 2, 3 + call PrintNumber + call DelayFrame + pop hl +.done + pop de + pop af + ret + +; calcs number of HP bar pixels for old and new HP value +; d: new pixels +; e: old pixels +UpdateHPBar_CalcOldNewHPBarPixels: + push hl + ld hl, wHPBarMaxHP + ld a, [hli] ; max HP into de + ld e, a + ld a, [hli] + ld d, a + ld a, [hli] ; old HP into bc + ld c, a + ld a, [hli] + ld b, a + ld a, [hli] ; new HP into hl + ld h, [hl] + ld l, a + push hl + push de + call GetHPBarLength ; calc num pixels for old HP + ld a, e + pop de + pop bc + push af + call GetHPBarLength ; calc num pixels for new HP + pop af + ld d, e + ld e, a + pop hl + ret diff --git a/engine/gfx/load_pokedex_tiles.asm b/engine/gfx/load_pokedex_tiles.asm new file mode 100755 index 00000000..a3f69171 --- /dev/null +++ b/engine/gfx/load_pokedex_tiles.asm @@ -0,0 +1,11 @@ +; Loads tile patterns for tiles used in the pokedex. +LoadPokedexTilePatterns: + call LoadHpBarAndStatusTilePatterns + ld de, PokedexTileGraphics + ld hl, vChars2 tile $60 + lb bc, BANK(PokedexTileGraphics), (PokedexTileGraphicsEnd - PokedexTileGraphics) / $10 + call CopyVideoData + ld de, PokeballTileGraphics + ld hl, vChars2 tile $72 + lb bc, BANK(PokeballTileGraphics), 1 + jp CopyVideoData ; load pokeball tile for marking caught mons diff --git a/engine/gfx/mon_icons.asm b/engine/gfx/mon_icons.asm new file mode 100755 index 00000000..6e2cf0ba --- /dev/null +++ b/engine/gfx/mon_icons.asm @@ -0,0 +1,307 @@ +AnimatePartyMon_ForceSpeed1: + xor a + ld [wCurrentMenuItem], a + ld b, a + inc a + jr GetAnimationSpeed + +; wPartyMenuHPBarColors contains the party mon's health bar colors +; 0: green +; 1: yellow +; 2: red +AnimatePartyMon:: + ld hl, wPartyMenuHPBarColors + ld a, [wCurrentMenuItem] + ld c, a + ld b, 0 + add hl, bc + ld a, [hl] + +GetAnimationSpeed: + ld c, a + ld hl, PartyMonSpeeds + add hl, bc + ld a, [wOnSGB] + xor $1 + add [hl] + ld c, a + add a + ld b, a + ld a, [wAnimCounter] + and a + jr z, .resetSprites + cp c + jr z, .animateSprite +.incTimer + inc a + cp b + jr nz, .skipResetTimer + xor a ; reset timer +.skipResetTimer + ld [wAnimCounter], a + jp DelayFrame +.resetSprites + push bc + ld hl, wMonPartySpritesSavedOAM + ld de, wOAMBuffer + ld bc, $60 + call CopyData + pop bc + xor a + jr .incTimer +.animateSprite + push bc + ld hl, wOAMBuffer + $02 ; OAM tile id + ld bc, $10 + ld a, [wCurrentMenuItem] + call AddNTimes + ld c, ICONOFFSET + ld a, [hl] + cp ICON_BALL << 2 + jr z, .editCoords + cp ICON_HELIX << 2 + jr nz, .editTileIDS +; ICON_BALL and ICON_HELIX only shake up and down +.editCoords + dec hl + dec hl ; dec hl to the OAM y coord + ld c, $1 ; amount to increase the y coord by +; otherwise, load a second sprite frame +.editTileIDS + ld b, $4 + ld de, $4 +.loop + ld a, [hl] + add c + ld [hl], a + add hl, de + dec b + jr nz, .loop + pop bc + ld a, c + jr .incTimer + +; Party mon animations cycle between 2 frames. +; The members of the PartyMonSpeeds array specify the number of V-blanks +; that each frame lasts for green HP, yellow HP, and red HP in order. +; On the naming screen, the yellow HP speed is always used. +PartyMonSpeeds: + db 5, 16, 32 + +LoadMonPartySpriteGfx: +; Load mon party sprite tile patterns into VRAM during V-blank. + ld hl, MonPartySpritePointers + ld a, $1e + +LoadAnimSpriteGfx: +; Load animated sprite tile patterns into VRAM during V-blank. hl is the address +; of an array of structures that contain arguments for CopyVideoData and a is +; the number of structures in the array. + ld bc, $0 +.loop + push af + push bc + push hl + add hl, bc + ld a, [hli] + ld e, a + ld a, [hli] + ld d, a + ld a, [hli] + ld c, a + ld a, [hli] + ld b, a + ld a, [hli] + ld h, [hl] + ld l, a + call CopyVideoData + pop hl + pop bc + ld a, $6 + add c + ld c, a + pop af + dec a + jr nz, .loop + ret + +LoadMonPartySpriteGfxWithLCDDisabled: +; Load mon party sprite tile patterns into VRAM immediately by disabling the +; LCD. + call DisableLCD + ld hl, MonPartySpritePointers + ld a, $1e + ld bc, $0 +.loop + push af + push bc + push hl + add hl, bc + ld a, [hli] + ld e, a + ld a, [hli] + ld d, a + push de + ld a, [hli] + ld c, a + swap c + ld b, $0 + ld a, [hli] + ld e, [hl] + inc hl + ld d, [hl] + pop hl + call FarCopyData + pop hl + pop bc + ld a, $6 + add c + ld c, a + pop af + dec a + jr nz, .loop + jp EnableLCD + +INCLUDE "data/icon_pointers.asm" + +WriteMonPartySpriteOAMByPartyIndex: +; Write OAM blocks for the party mon in [hPartyMonIndex]. + push hl + push de + push bc + ldh a, [hPartyMonIndex] + cp $ff + jr z, .asm_7191f + ld hl, wPartySpecies + ld e, a + ld d, 0 + add hl, de + ld a, [hl] + call GetPartyMonSpriteID + ld [wOAMBaseTile], a + call WriteMonPartySpriteOAM + pop bc + pop de + pop hl + ret + +.asm_7191f + ld hl, wOAMBuffer + ld de, wMonPartySpritesSavedOAM + ld bc, $60 + call CopyData + pop bc + pop de + pop hl + ret + +WriteMonPartySpriteOAMBySpecies: +; Write OAM blocks for the party sprite of the species in +; [wMonPartySpriteSpecies]. + xor a + ldh [hPartyMonIndex], a + ld a, [wMonPartySpriteSpecies] + call GetPartyMonSpriteID + ld [wOAMBaseTile], a + jr WriteMonPartySpriteOAM + +UnusedPartyMonSpriteFunction: +; This function is unused and doesn't appear to do anything useful. It looks +; like it may have been intended to load the tile patterns and OAM data for +; the mon party sprite associated with the species in [wcf91]. +; However, its calculations are off and it loads garbage data. + ld a, [wcf91] + call GetPartyMonSpriteID + push af + ld hl, vSprites tile $00 + call .LoadTilePatterns + pop af + add $5A + ld hl, vSprites tile $04 + call .LoadTilePatterns + xor a + ld [wMonPartySpriteSpecies], a + jr WriteMonPartySpriteOAMBySpecies + +.LoadTilePatterns + push hl + add a + ld c, a + ld b, 0 + ld hl, MonPartySpritePointers + add hl, bc + add hl, bc + add hl, bc + ld a, [hli] + ld e, a + ld a, [hli] + ld d, a + ld a, [hli] + ld c, a + ld a, [hli] + ld b, a + pop hl + jp CopyVideoData + +WriteMonPartySpriteOAM: +; Write the OAM blocks for the first animation frame into the OAM buffer and +; make a copy at wMonPartySpritesSavedOAM. + push af + ld c, $10 + ld h, HIGH(wOAMBuffer) + ldh a, [hPartyMonIndex] + swap a + ld l, a + add $10 + ld b, a + pop af + cp ICON_HELIX << 2 + jr z, .helix + call WriteSymmetricMonPartySpriteOAM + jr .makeCopy +.helix + call WriteAsymmetricMonPartySpriteOAM +; Make a copy of the OAM buffer with the first animation frame written so that +; we can flip back to it from the second frame by copying it back. +.makeCopy + ld hl, wOAMBuffer + ld de, wMonPartySpritesSavedOAM + ld bc, $60 + jp CopyData + +GetPartyMonSpriteID: + ld [wd11e], a + predef IndexToPokedex + ld a, [wd11e] + ld c, a + dec a + srl a + ld hl, MonPartyData + ld e, a + ld d, 0 + add hl, de + ld a, [hl] + bit 0, c + jr nz, .skipSwap + swap a ; use lower nybble if pokedex num is even +.skipSwap + and $f0 + srl a ; value == ICON constant << 2 + srl a + ret + +INCLUDE "data/pokemon/menu_icons.asm" + +INC_FRAME_1 EQUS "0, $20" +INC_FRAME_2 EQUS "$20, $20" + +BugIconFrame1: INCBIN "gfx/icons/bug.2bpp", INC_FRAME_1 +PlantIconFrame1: INCBIN "gfx/icons/plant.2bpp", INC_FRAME_1 +BugIconFrame2: INCBIN "gfx/icons/bug.2bpp", INC_FRAME_2 +PlantIconFrame2: INCBIN "gfx/icons/plant.2bpp", INC_FRAME_2 +SnakeIconFrame1: INCBIN "gfx/icons/snake.2bpp", INC_FRAME_1 +QuadrupedIconFrame1: INCBIN "gfx/icons/quadruped.2bpp", INC_FRAME_1 +SnakeIconFrame2: INCBIN "gfx/icons/snake.2bpp", INC_FRAME_2 +QuadrupedIconFrame2: INCBIN "gfx/icons/quadruped.2bpp", INC_FRAME_2 + +TradeBubbleIconGFX: INCBIN "gfx/trade/bubble.2bpp" diff --git a/engine/gfx/oam_dma.asm b/engine/gfx/oam_dma.asm new file mode 100644 index 00000000..fe93e90d --- /dev/null +++ b/engine/gfx/oam_dma.asm @@ -0,0 +1,28 @@ +WriteDMACodeToHRAM:: +; Since no other memory is available during OAM DMA, +; DMARoutine is copied to HRAM and executed there. + ld c, LOW(hDMARoutine) + ld b, DMARoutineEnd - DMARoutine + ld hl, DMARoutine +.copy + ld a, [hli] + ldh [c], a + inc c + dec b + jr nz, .copy + ret + +DMARoutine: +LOAD "OAM DMA", HRAM +hDMARoutine:: + ; initiate DMA + ld a, HIGH(wOAMBuffer) + ldh [rDMA], a + ; wait for DMA to finish + ld a, $28 +.wait + dec a + jr nz, .wait + ret +ENDL +DMARoutineEnd: diff --git a/engine/gfx/palettes.asm b/engine/gfx/palettes.asm new file mode 100755 index 00000000..dd723afa --- /dev/null +++ b/engine/gfx/palettes.asm @@ -0,0 +1,1117 @@ +_RunPaletteCommand: + call GetPredefRegisters + ld a, b + cp SET_PAL_DEFAULT + jr nz, .not_default + ld a, [wDefaultPaletteCommand] +.not_default + cp SET_PAL_PARTY_MENU_HP_BARS + jp z, UpdatePartyMenuBlkPacket + ld l, a + ld h, 0 + add hl, hl + ld de, SetPalFunctions + add hl, de + ld a, [hli] + ld h, [hl] + ld l, a + ld de, SendSGBPackets + push de + jp hl + +SetPal_Black: + ld hl, PalPacket_Black + ld de, BlkPacket_Battle + ret + +; uses PalPacket_Empty to build a packet based on mon IDs and health color +SetPal_Battle: + ld hl, PalPacket_Empty + ld de, wPalPacket + ld bc, $10 + call CopyData + ;ld a, [wPlayerBattleStatus3] + ld hl, wBattleMonSpecies + ld a, [hl] + and a + jr z, .asm_71ef9 + ld hl, wPartyMon1 + ld a, [wPlayerMonNumber] + ld bc, wPartyMon2 - wPartyMon1 + call AddNTimes +.asm_71ef9 + call DeterminePaletteID + ld b, a + ;ld a, [wEnemyBattleStatus3] + ld hl, wEnemyMonSpecies2 + call DeterminePaletteID + ld c, a + ld hl, wPalPacket + 1 + ld a, [wPlayerHPBarColor] + add PAL_GREENBAR + ld [hli], a + inc hl + ld a, [wEnemyHPBarColor] + add PAL_GREENBAR + ld [hli], a + inc hl + ld a, b + ld [hli], a + inc hl + ld a, c + ld [hl], a + ld hl, wPalPacket + ld de, BlkPacket_Battle + ld a, SET_PAL_BATTLE + ld [wDefaultPaletteCommand], a + ret + +SetPal_TownMap: + ld hl, PalPacket_TownMap + ld de, BlkPacket_WholeScreen + ret + +; uses PalPacket_Empty to build a packet based the mon ID +SetPal_StatusScreen: + ld hl, PalPacket_Empty + ld de, wPalPacket + ld bc, $10 + call CopyData + ld a, [wcf91] + cp NUM_POKEMON_INDEXES + 1 + jr c, .pokemon + ld a, $1 ; not pokemon +.pokemon + call DeterminePaletteIDOutOfBattle + push af + ld hl, wPalPacket + 1 + ld a, [wStatusScreenHPBarColor] + add PAL_GREENBAR + ld [hli], a + inc hl + pop af + ld [hl], a + ld hl, wPalPacket + ld de, BlkPacket_StatusScreen + ret + +SetPal_PartyMenu: + ld hl, PalPacket_PartyMenu + ld de, wPartyMenuBlkPacket + ret + +SetPal_Pokedex: + ld hl, PalPacket_Pokedex + ld de, wPalPacket + ld bc, $10 + call CopyData + ld a, [wcf91] + call DeterminePaletteIDOutOfBattle + ld hl, wPalPacket + 3 + ld [hl], a + ld hl, wPalPacket + ld de, BlkPacket_Pokedex + ret + +SetPal_Slots: + ld hl, PalPacket_Slots + ld de, BlkPacket_Slots + ret + +SetPal_Titlescreen: + ld hl, PalPacket_Titlescreen + ld de, BlkPacket_Titlescreen + ret + +; used mostly for menus and the Oak intro +SetPal_Generic: + ld hl, PalPacket_Generic + ld de, BlkPacket_WholeScreen + ret + +SetPal_NidorinoIntro: + ld hl, PalPacket_NidorinoIntro + ld de, BlkPacket_NidorinoIntro + ret + +SetPal_GameFreakIntro: + ld hl, PalPacket_GameFreakIntro + ld de, BlkPacket_GameFreakIntro + ld a, SET_PAL_GENERIC + ld [wDefaultPaletteCommand], a + ret + +; uses PalPacket_Empty to build a packet based on the current map +SetPal_Overworld: + ld hl, PalPacket_Empty + ld de, wPalPacket + ld bc, $10 + call CopyData + ld a, [wCurMapTileset] + cp CEMETERY + jr z, .PokemonTowerOrAgatha + cp CAVERN + jr z, .caveOrBruno + ld a, [wCurMap] + cp FIRST_INDOOR_MAP + jr c, .townOrRoute + cp CERULEAN_CAVE_2F + jr c, .normalDungeonOrBuilding + cp CERULEAN_CAVE_1F + 1 + jr c, .caveOrBruno + cp LORELEIS_ROOM + jr z, .Lorelei + cp BRUNOS_ROOM + jr z, .caveOrBruno + cp TRADE_CENTER + jr z, .trade_center_colosseum + cp COLOSSEUM + jr z, .trade_center_colosseum +.normalDungeonOrBuilding + ld a, [wLastMap] ; town or route that current dungeon or building is located +.townOrRoute + cp NUM_CITY_MAPS + jr c, .town + ld a, PAL_ROUTE - 1 +.town + inc a ; a town's palette ID is its map ID + 1 + ld hl, wPalPacket + 1 + ld [hld], a + ld de, BlkPacket_WholeScreen + ld a, SET_PAL_OVERWORLD + ld [wDefaultPaletteCommand], a + ret +.PokemonTowerOrAgatha + ld a, PAL_GREYMON - 1 + jr .town +.caveOrBruno + ld a, PAL_CAVE - 1 + jr .town +.Lorelei + xor a + jr .town +.trade_center_colosseum + ld a, PAL_GREYMON - 1 + jr .town + +; used when a Pokemon is the only thing on the screen +; such as evolution, trading and the Hall of Fame +SetPal_PokemonWholeScreen: + push bc + ld hl, PalPacket_Empty + ld de, wPalPacket + ld bc, $10 + call CopyData + pop bc + ld a, c + and a + ld a, PAL_BLACK + jr nz, .next + ld a, [wWholeScreenPaletteMonSpecies] + call DeterminePaletteIDOutOfBattle +.next + ld [wPalPacket + 1], a + ld hl, wPalPacket + ld de, BlkPacket_WholeScreen + ret + +SetPal_TrainerCard: + ld hl, BlkPacket_TrainerCard + ld de, wTrainerCardBlkPacket + ld bc, $40 + call CopyData + ld de, BadgeBlkDataLengths + ld hl, wTrainerCardBlkPacket + 2 + ld a, [wObtainedBadges] + ld c, NUM_BADGES +.badgeLoop + srl a + push af + jr c, .haveBadge +; The player doens't have the badge, so zero the badge's blk data. + push bc + ld a, [de] + ld c, a + xor a +.zeroBadgeDataLoop + ld [hli], a + dec c + jr nz, .zeroBadgeDataLoop + pop bc + jr .nextBadge +.haveBadge +; The player does have the badge, so skip past the badge's blk data. + ld a, [de] +.skipBadgeDataLoop + inc hl + dec a + jr nz, .skipBadgeDataLoop +.nextBadge + pop af + inc de + dec c + jr nz, .badgeLoop + ld hl, PalPacket_TrainerCard + ld de, wTrainerCardBlkPacket + ret + +SendUnknownPalPacket_7205d:: + ld hl, UnknownPalPacket_72811 + ld de, BlkPacket_WholeScreen + ret + +SendUnknownPalPacket_72064:: + ld hl, UnknownPalPacket_72821 + ld de, UnknownPacket_72751 + ret + +SetPalFunctions: +; entries correspond to SET_PAL_* constants + dw SetPal_Black + dw SetPal_Battle + dw SetPal_TownMap + dw SetPal_StatusScreen + dw SetPal_Pokedex + dw SetPal_Slots + dw SetPal_Titlescreen + dw SetPal_NidorinoIntro + dw SetPal_Generic + dw SetPal_Overworld + dw SetPal_PartyMenu + dw SetPal_PokemonWholeScreen + dw SetPal_GameFreakIntro + dw SetPal_TrainerCard + dw SendUnknownPalPacket_7205d + dw SendUnknownPalPacket_72064 + +; The length of the blk data of each badge on the Trainer Card. +; The Rainbow Badge has 3 entries because of its many colors. +BadgeBlkDataLengths: + db 6 ; Boulder Badge + db 6 ; Cascade Badge + db 6 ; Thunder Badge + db 6 * 3 ; Rainbow Badge + db 6 ; Soul Badge + db 6 ; Marsh Badge + db 6 ; Volcano Badge + db 6 ; Earth Badge + +DeterminePaletteID: + ld a, [hl] +DeterminePaletteIDOutOfBattle: + ld [wd11e], a + and a ; is the mon index 0? + jr z, .skipDexNumConversion + push bc + predef IndexToPokedex + pop bc + ld a, [wd11e] +.skipDexNumConversion + ld e, a + ld d, 0 + ld hl, MonsterPalettes ; not just for Pokemon, Trainers use it too + add hl, de + ld a, [hl] + ret + +YellowIntroPaletteAction:: + ld a, e + and a + jr nz, .asm_720bd + ld hl, PalPacket_Generic + ldh a, [hGBC] + and a + jp z, SendSGBPacket + jp InitGBCPalettes + +.asm_720bd + ld hl, UnknownPalPacket_72811 + ldh a, [hGBC] + and a + jp z, SendSGBPacket + call InitGBCPalettes + ld hl, PalPacket_Generic + inc hl + ld a, [hli] + call GetGBCBasePalAddress + ld a, e + ld [wGBCBasePalPointers + 2], a + ld a, d + ld [wGBCBasePalPointers + 2 + 1], a + xor a ; CONVERT_BGP + call DMGPalToGBCPal + ld a, 1 + call TransferCurBGPData + ret + +LoadOverworldPikachuFrontpicPalettes:: + ld hl, PalPacket_Empty + ld de, wPalPacket + ld bc, $10 + call CopyData + call GetPal_Pikachu + ld hl, wPartyMenuBlkPacket + ld [hl], a + ld hl, wPartyMenuBlkPacket + 2 + ld a, $26 + ld [hl], a + ld hl, wPalPacket + ldh a, [hGBC] + and a + jr nz, .cgb_1 + call SendSGBPacket + jr .okay_1 + +.cgb_1 + call InitGBCPalettes +.okay_1 + ld hl, BlkPacket_WholeScreen + ld de, wPalPacket + ld bc, $10 + call CopyData + ld hl, wPartyMenuBlkPacket + 2 + ld a, $5 + ld [hli], a + ld a, $7 + ld [hli], a + ld a, $6 + ld [hli], a + ld a, $b + ld [hli], a + ld a, $a + ld [hl], a + ld hl, wPalPacket + ldh a, [hGBC] + and a + jr nz, .cgb_2 + call SendSGBPacket + jr .okay_2 + +.cgb_2 + call InitGBCPalettes +.okay_2 + ret + +GetPal_Pikachu:: +; similar to SetPal_Overworld + ld a, [wCurMapTileset] + cp CEMETERY + jr z, .PokemonTowerOrAgatha + cp CAVERN + jr z, .caveOrBruno + ld a, [wCurMap] + cp REDS_HOUSE_1F + jr c, .townOrRoute + cp CERULEAN_CAVE_2F + jr c, .normalDungeonOrBuilding + cp NAME_RATERS_HOUSE + jr c, .caveOrBruno + cp LORELEIS_ROOM + jr z, .Lorelei + cp BRUNOS_ROOM + jr z, .caveOrBruno + cp TRADE_CENTER + jr z, .battleOrTradeCenter + cp COLOSSEUM + jr z, .battleOrTradeCenter +.normalDungeonOrBuilding + ld a, [wLastMap] ; town or route that current dungeon or building is located +.townOrRoute + cp SAFFRON_CITY + 1 + jr c, .town + ld a, PAL_ROUTE - 1 +.town + inc a ; a town's pallete ID is its map ID + 1 + ret + +.PokemonTowerOrAgatha + ld a, PAL_GREYMON - 1 + jr .town + +.caveOrBruno + ld a, PAL_CAVE - 1 + jr .town + +.Lorelei + xor a ; PAL_PALLET - 1 + jr .town + +.battleOrTradeCenter + ld a, PAL_GREYMON - 1 + jr .town + +InitPartyMenuBlkPacket: + ld hl, BlkPacket_PartyMenu + ld de, wPartyMenuBlkPacket + ld bc, $30 + jp CopyData + +UpdatePartyMenuBlkPacket: +; Update the blk packet with the palette of the HP bar that is +; specified in [wWhichPartyMenuHPBar]. + ld hl, wPartyMenuHPBarColors + ld a, [wWhichPartyMenuHPBar] + ld e, a + ld d, 0 + add hl, de + ld e, l + ld d, h + ld a, [de] + and a + ld e, (1 << 2) | 1 ; green + jr z, .next + dec a + ld e, (2 << 2) | 2 ; yellow + jr z, .next + ld e, (3 << 2) | 3 ; red +.next + push de + ld hl, wPartyMenuBlkPacket + 8 + 1 + ld bc, 6 + ld a, [wWhichPartyMenuHPBar] + call AddNTimes + pop de + ld [hl], e + ret + +SendSGBPacket: + ld a, 1 + ldh [hDisableJoypadPolling], a ; don't poll joypad while sending packet + call _SendSGBPacket + xor a + ldh [hDisableJoypadPolling], a + ret + +_SendSGBPacket: +;check number of packets + ld a, [hl] + and $07 + ret z +; store number of packets in B + ld b, a +.loop2 +; save B for later use + push bc +; send RESET signal (P14=LOW, P15=LOW) + xor a + ldh [rJOYP], a +; set P14=HIGH, P15=HIGH + ld a, $30 + ldh [rJOYP], a +;load length of packets (16 bytes) + ld b, $10 +.nextByte +;set bit counter (8 bits per byte) + ld e, $08 +; get next byte in the packet + ld a, [hli] + ld d, a +.nextBit0 + bit 0, d +; if 0th bit is not zero set P14=HIGH, P15=LOW (send bit 1) + ld a, $10 + jr nz, .next0 +; else (if 0th bit is zero) set P14=LOW, P15=HIGH (send bit 0) + ld a, $20 +.next0 + ldh [rJOYP], a +; must set P14=HIGH,P15=HIGH between each "pulse" + ld a, $30 + ldh [rJOYP], a +; rotation will put next bit in 0th position (so we can always use command +; "bit 0, d" to fetch the bit that has to be sent) + rr d +; decrease bit counter so we know when we have sent all 8 bits of current byte + dec e + jr nz, .nextBit0 + dec b + jr nz, .nextByte +; send bit 1 as a "stop bit" (end of parameter data) + ld a, $20 + ldh [rJOYP], a +; set P14=HIGH,P15=HIGH + ld a, $30 + ldh [rJOYP], a +; wait for about 70000 cycles + call Wait7000 +; restore (previously pushed) number of packets + pop bc + dec b +; return if there are no more packets + ret z +; else send 16 more bytes + jr .loop2 + +LoadSGB: + xor a + ld [wOnSGB], a + call CheckSGB + jr c, .onSGB + ldh a, [hGBC] + and a + jr z, .onDMG + ld a, $1 + ld [wOnSGB], a +.onDMG + ret +.onSGB + ld a, $1 + ld [wOnSGB], a + di + call PrepareSuperNintendoVRAMTransfer + ei + ld a, 1 + ld [wCopyingSGBTileData], a + ld de, ChrTrnPacket + ld hl, SGBBorderGraphics + call CopyGfxToSuperNintendoVRAM + xor a + ld [wCopyingSGBTileData], a + ld de, PctTrnPacket + ld hl, BorderPalettes + call CopyGfxToSuperNintendoVRAM + xor a + ld [wCopyingSGBTileData], a + ld de, PalTrnPacket + ld hl, SuperPalettes + call CopyGfxToSuperNintendoVRAM + call ClearVram + ld hl, MaskEnCancelPacket + jp SendSGBPacket + +PrepareSuperNintendoVRAMTransfer: + ld hl, .packetPointers + ld c, 9 +.loop + push bc + ld a, [hli] + push hl + ld h, [hl] + ld l, a + call SendSGBPacket + pop hl + inc hl + pop bc + dec c + jr nz, .loop + ret + +.packetPointers +; Only the first packet is needed. + dw MaskEnFreezePacket + dw DataSnd_728a1 + dw DataSnd_728b1 + dw DataSnd_728c1 + dw DataSnd_728d1 + dw DataSnd_728e1 + dw DataSnd_728f1 + dw DataSnd_72901 + dw DataSnd_72911 + +CheckSGB: +; Returns whether the game is running on an SGB in carry. + ld hl, MltReq2Packet + call SendSGBPacket + call Wait7000 + ldh a, [rJOYP] + and $3 + cp $3 + jr nz, .isSGB + ld a, $20 + ldh [rJOYP], a + ldh a, [rJOYP] + ldh a, [rJOYP] + call Wait7000 + call Wait7000 + ld a, $30 + ldh [rJOYP], a + call Wait7000 + call Wait7000 + ld a, $10 + ldh [rJOYP], a + ldh a, [rJOYP] + ldh a, [rJOYP] + ldh a, [rJOYP] + ldh a, [rJOYP] + ldh a, [rJOYP] + ldh a, [rJOYP] + call Wait7000 + call Wait7000 + ld a, $30 + ldh [rJOYP], a + ldh a, [rJOYP] + ldh a, [rJOYP] + ldh a, [rJOYP] + call Wait7000 + call Wait7000 + ldh a, [rJOYP] + and $3 + cp $3 + jr nz, .isSGB + call SendMltReq1Packet + and a + ret +.isSGB + call SendMltReq1Packet + scf + ret + +SendMltReq1Packet: + ld hl, MltReq1Packet + call SendSGBPacket + jp Wait7000 + +CopyGfxToSuperNintendoVRAM: + di + push de + call DisableLCD + ld a, $e4 + ldh [rBGP], a + call _UpdateGBCPal_BGP_CheckDMG + ld de, vChars1 + ld a, [wCopyingSGBTileData] + and a + jr z, .notCopyingTileData + call CopySGBBorderTiles + jr .next +.notCopyingTileData + ld bc, $1000 + call CopyData +.next + ld hl, vBGMap0 + ld de, $c + ld a, $80 + ld c, $d +.loop + ld b, $14 +.innerLoop + ld [hli], a + inc a + dec b + jr nz, .innerLoop + add hl, de + dec c + jr nz, .loop + ld a, $e3 + ldh [rLCDC], a + pop hl + call SendSGBPacket + xor a + ldh [rBGP], a + call _UpdateGBCPal_BGP_CheckDMG + ei + ret + +Wait7000: +; Each loop takes 9 cycles so this routine actually waits 63000 cycles. + ld de, 7000 +.loop + nop + nop + nop + dec de + ld a, d + or e + jr nz, .loop + ret + +SendSGBPackets: + ldh a, [hGBC] + and a + jr z, .notGBC + push de + call InitGBCPalettes + pop hl + call InitGBCPalettes + ldh a, [rLCDC] + and rLCDC_ENABLE_MASK + ret z + call Delay3 + ret +.notGBC + push de + call SendSGBPacket + pop hl + jp SendSGBPacket + +InitGBCPalettes: + ld a, [hl] + and $f8 + cp $20 + jp z, TranslatePalPacketToBGMapAttributes + + inc hl + +index = 0 + + REPT NUM_ACTIVE_PALS + IF index > 0 + pop hl + ENDC + + ld a, [hli] + inc hl + + IF index < (NUM_ACTIVE_PALS + -1) + push hl + ENDC + + call GetGBCBasePalAddress + ld a, e + ld [wGBCBasePalPointers + index * 2], a + ld a, d + ld [wGBCBasePalPointers + index * 2 + 1], a + + xor a ; CONVERT_BGP + call DMGPalToGBCPal + ld a, index + call TransferCurBGPData + + ld a, CONVERT_OBP0 + call DMGPalToGBCPal + ld a, index + call TransferCurOBPData + + ld a, CONVERT_OBP1 + call DMGPalToGBCPal + ld a, index + 4 + call TransferCurOBPData + +index = index + 1 + ENDR + + ret + +GetGBCBasePalAddress:: +; Input: a = palette ID +; Output: de = palette address + push hl + ld l, a + xor a + ld h, a + add hl, hl + add hl, hl + add hl, hl + ld de, GBCBasePalettes + add hl, de + ld a, l + ld e, a + ld a, h + ld d, a + pop hl + ret + +DMGPalToGBCPal:: +; Populate wGBCPal with colors from a base palette, selected using one of the +; DMG palette registers. +; Input: +; a = which DMG palette register +; de = address of GBC base palette + and a + jr nz, .notBGP + ldh a, [rBGP] + ld [wLastBGP], a + jr .convert +.notBGP + dec a + jr nz, .notOBP0 + ldh a, [rOBP0] + ld [wLastOBP0], a + jr .convert +.notOBP0 + ldh a, [rOBP1] + ld [wLastOBP1], a +.convert +color_index = 0 + REPT NUM_COLORS + ld b, a + and %11 + call .GetColorAddress + ld a, [hli] + ld [wGBCPal + color_index * 2], a + ld a, [hl] + ld [wGBCPal + color_index * 2 + 1], a + + IF color_index < (NUM_COLORS + -1) + ld a, b + rrca + rrca + ENDC + +color_index = color_index + 1 + ENDR + ret + +.GetColorAddress: + add a + ld l, a + xor a + ld h, a + add hl, de + ret + +TransferCurBGPData:: + push de + add a + add a + add a + or $80 ; auto-increment + ldh [rBGPI], a + ld de, rBGPD + ld hl, wGBCPal + ld b, %10 ; mask for non-V-blank/non-H-blank STAT mode + ldh a, [rLCDC] + and rLCDC_ENABLE_MASK + jr nz, .lcdEnabled + rept NUM_COLORS + call TransferPalColorLCDDisabled + endr + jr .done +.lcdEnabled + rept NUM_COLORS + call TransferPalColorLCDEnabled + endr +.done + pop de + ret + +BufferBGPPal:: +; Copy wGBCPal to palette a in wBGPPalsBuffer. + push de + add a + add a + add a + ld l, a + xor a + ld h, a + ld de, wBGPPalsBuffer + add hl, de + ld de, wGBCPal + ld c, PAL_SIZE +.loop + ld a, [de] + ld [hli], a + inc de + dec c + jr nz, .loop + pop de + ret + +TransferBGPPals:: +; Transfer the buffered BG palettes. + ldh a, [rLCDC] + and rLCDC_ENABLE_MASK + jr z, .lcdDisabled + di +.waitLoop + ldh a, [rLY] + cp 144 + jr c, .waitLoop +.lcdDisabled + call .DoTransfer + ei + ret + +.DoTransfer: + xor a + or $80 ; auto-increment + ldh [rBGPI], a + ld de, rBGPD + ld hl, wBGPPalsBuffer + ld c, 4 * PAL_SIZE +.loop + ld a, [hli] + ld [de], a + dec c + jr nz, .loop + ret + +TransferCurOBPData: + push de + add a + add a + add a + or $80 ; auto-increment + ldh [rOBPI], a + ld de, rOBPD + ld hl, wGBCPal + ld b, %10 ; mask for non-V-blank/non-H-blank STAT mode + ldh a, [rLCDC] + and rLCDC_ENABLE_MASK + jr nz, .lcdEnabled + rept NUM_COLORS + call TransferPalColorLCDDisabled + endr + jr .done +.lcdEnabled + rept NUM_COLORS + call TransferPalColorLCDEnabled + endr +.done + pop de + ret + +TransferPalColorLCDEnabled: +; Transfer a palette color while the LCD is enabled. + +; In case we're already in H-blank or V-blank, wait for it to end. This is a +; precaution so that the transfer doesn't extend past the blanking period. + ldh a, [rSTAT] + and b + jr z, TransferPalColorLCDEnabled + +; Wait for H-blank or V-blank to begin. +.notInBlankingPeriod + ldh a, [rSTAT] + and b + jr nz, .notInBlankingPeriod +; fall through + +TransferPalColorLCDDisabled: +; Transfer a palette color while the LCD is disabled. + ld a, [hli] + ld [de], a + ld a, [hli] + ld [de], a + ret + +_UpdateGBCPal_BGP_CheckDMG:: + ldh a, [hGBC] + and a + ret z +; fall through + +_UpdateGBCPal_BGP:: +index = 0 + + REPT NUM_ACTIVE_PALS + ld a, [wGBCBasePalPointers + index * 2] + ld e, a + ld a, [wGBCBasePalPointers + index * 2 + 1] + ld d, a + xor a ; CONVERT_BGP + call DMGPalToGBCPal + ld a, index + call BufferBGPPal + +index = index + 1 + ENDR + + call TransferBGPPals + ret + +_UpdateGBCPal_OBP:: +index = 0 + + REPT NUM_ACTIVE_PALS + ld a, [wGBCBasePalPointers + index * 2] + ld e, a + ld a, [wGBCBasePalPointers + index * 2 + 1] + ld d, a + ld a, c + call DMGPalToGBCPal + ld a, c + dec a + rlca + rlca + + IF index > 0 + IF index == 1 + inc a + ELSE + add index + ENDC + ENDC + + call TransferCurOBPData + +index = index + 1 + ENDR + + ret + +TranslatePalPacketToBGMapAttributes:: +; translate the SGB pal packets into something usable for the GBC + push hl + pop de + ld hl, PalPacketPointers + ld a, [hli] + ld c, a +.loop + ld a, e +.innerLoop + cp [hl] + jr z, .checkHighByte + inc hl + inc hl + dec c + jr nz, .innerLoop + ret +.checkHighByte +; the low byte of pointer matched, so check the high byte + inc hl + ld a, d + cp [hl] + jr z, .foundMatchingPointer + inc hl + dec c + jr nz, .loop + ret +.foundMatchingPointer + farcall LoadBGMapAttributes + ret + +PalPacketPointers:: + db (palPacketPointersEnd - palPacketPointers) / 2 +palPacketPointers: + dw BlkPacket_WholeScreen + dw BlkPacket_Battle + dw BlkPacket_StatusScreen + dw BlkPacket_Pokedex + dw BlkPacket_Slots + dw BlkPacket_Titlescreen + dw BlkPacket_NidorinoIntro + dw wPartyMenuBlkPacket + dw wTrainerCardBlkPacket + dw BlkPacket_GameFreakIntro + dw wPalPacket + dw UnknownPacket_72751 +palPacketPointersEnd: + +CopySGBBorderTiles: +; SGB tile data is stored in a 4BPP planar format. +; Each tile is 32 bytes. The first 16 bytes contain bit planes 1 and 2, while +; the second 16 bytes contain bit planes 3 and 4. +; This function converts 2BPP planar data into this format by mapping +; 2BPP colors 0-3 to 4BPP colors 0-3. 4BPP colors 4-15 are not used. + ld b, 128 +.tileLoop +; Copy bit planes 1 and 2 of the tile data. + ld c, 16 +.copyLoop + ld a, [hli] + ld [de], a + inc de + dec c + jr nz, .copyLoop + +; Zero bit planes 3 and 4. + ld c, 16 + xor a +.zeroLoop + ld [de], a + inc de + dec c + jr nz, .zeroLoop + + dec b + jr nz, .tileLoop + ret + +INCLUDE "data/sgb/sgb_packets.asm" + +INCLUDE "data/pokemon/palettes.asm" + +INCLUDE "data/sgb/sgb_palettes.asm" + +INCLUDE "data/sgb/sgb_border.asm" diff --git a/engine/gfx/screen_effects.asm b/engine/gfx/screen_effects.asm new file mode 100644 index 00000000..973a951a --- /dev/null +++ b/engine/gfx/screen_effects.asm @@ -0,0 +1,73 @@ +; inverts the BGP for 4 (6 on CGB due to lag) frames +ChangeBGPalColor0_4Frames: + call GetPredefRegisters ; leftover of red/blue, has no use here + ldh a, [rBGP] + xor $ff + ldh [rBGP], a + call UpdateGBCPal_BGP + ld c, 4 + call DelayFrames + ldh a, [rBGP] + xor $ff + ldh [rBGP], a + call UpdateGBCPal_BGP + ret + +PredefShakeScreenVertically: +; Moves the window down and then back in a sequence of progressively smaller +; numbers of pixels, starting at b. + call GetPredefRegisters + ld a, 1 + ld [wDisableVBlankWYUpdate], a + xor a +.loop + ldh [hMutateWY], a + call .MutateWY + call .MutateWY + dec b + ld a, b + jr nz, .loop + xor a + ld [wDisableVBlankWYUpdate], a + ret + +.MutateWY + ldh a, [hMutateWY] + xor b + ldh [hMutateWY], a + ldh [rWY], a + ld c, 3 + jp DelayFrames + +PredefShakeScreenHorizontally: +; Moves the window right and then back in a sequence of progressively smaller +; numbers of pixels, starting at b. + call GetPredefRegisters + xor a +.loop + ldh [hMutateWX], a + call .MutateWX + ld c, 1 + call DelayFrames + call .MutateWX + dec b + ld a, b + jr nz, .loop + +; restore normal WX + ld a, 7 + ldh [rWX], a + ret + +.MutateWX + ldh a, [hMutateWX] + xor b + ldh [hMutateWX], a + bit 7, a + jr z, .skipZeroing + xor a ; zero a if it's negative +.skipZeroing + add 7 + ldh [rWX], a + ld c, 4 + jp DelayFrames diff --git a/engine/gfx/sprite_oam.asm b/engine/gfx/sprite_oam.asm new file mode 100644 index 00000000..01b2c412 --- /dev/null +++ b/engine/gfx/sprite_oam.asm @@ -0,0 +1,232 @@ +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 -1 + ret nz + ld [wUpdateSpritesEnabled], a + jp HideSprites + +.updateEnabled + xor a + ldh [hOAMBufferOffset], a + +.spriteLoop + ldh [hSpriteOffset2], a + + ld e, a + ld d, HIGH(wSpriteStateData1) + + ld a, [de] ; [x#SPRITESTATEDATA1_PICTUREID] + and a + jp z, .nextSprite + + inc e + inc e + ld a, [de] ; [x#SPRITESTATEDATA1_IMAGEINDEX] + ld [wd5cd], a + cp $ff ; off-screen (don't draw) + jr nz, .visible + + call GetSpriteScreenXY + jr .nextSprite + +.visible + cp $a0 ; is the sprite unchanging like an item ball or boulder? + jr c, .usefacing + +; unchanging + 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 + ld a, e + add $5 + ld e, a + ld a, [de] ; [x#SPRITESTATEDATA2_GRASSPRIORITY] + and $80 + ldh [hSpritePriority], a ; temp store sprite priority + pop de + + + call GetSpriteScreenXY + + ldh a, [hOAMBufferOffset] + add [hl] + cp $a0 + jr z, .hidden + jr nc, .asm_4a41 +.hidden + call Func_4a7b + ld [wd5cd], a + ldh a, [hOAMBufferOffset] + + ld e, a + ld d, HIGH(wOAMBuffer) + +.tileLoop + ld a, [hli] + ld c, a +.loop + ldh 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 + ldh 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 a, [wd5cd] + add [hl] + cp $80 + jr c, .asm_4a1c + ld b, a + ldh a, [hFFFC] + add b +.asm_4a1c + ld [de], a ; tile id + inc hl + inc e + ld a, [hl] + bit 1, a ; is the tile allowed to set the sprite priority bit? + jr z, .skipPriority + ldh a, [hSpritePriority] + or [hl] +.skipPriority + 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 + dec c + jr nz, .loop + + ld a, e + ldh [hOAMBufferOffset], a +.nextSprite + ldh a, [hSpriteOffset2] + add $10 + cp LOW($100) + jp nz, .spriteLoop + + ; Clear unused OAM. +.asm_4a41 + ld a, [wd736] + bit 6, a ; jumping down ledge or fishing animation? + 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 c, $90 + +.clear + ldh 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 + cp l + jr nz, .clearLoop + ret + +GetSpriteScreenXY: + inc e + inc e + ld a, [de] ; [x#SPRITESTATEDATA1_YPIXELS] + ldh [hSpriteScreenY], a + inc e + inc e + ld a, [de] ; [x#SPRITESTATEDATA1_XPIXELS] + ldh [hSpriteScreenX], a + ld a, 4 + add e + ld e, a + ldh a, [hSpriteScreenY] + add 4 + and $f0 + ld [de], a ; [x#SPRITESTATEDATA1_YADJUSTED] + inc e + ldh a, [hSpriteScreenX] + and $f0 + ld [de], a ; [x#SPRITESTATEDATA1_XADJUSTED] + ret + +Func_4a7b: + push bc + ld a, [wd5cd] ; temp copy of [x#SPRITESTATEDATA1_IMAGEINDEX] + 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/gfx/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/tilesets/collision_tile_ids.asm" |