; CARD_HELPER_VARS_START defines the beginning of the $100 bytes set aside for system usage PUSHS "Card Helper Vars", WRAM0[CARD_HELPER_VARS_START] cvCardBank: db cvCardAddress: dw cvNeedsCleanup: db POPS FindCardTileMap: ; puts the tilemap into hl ld hl, cvCardAddress ld c, [hl] inc hl ld b, [hl] ld l, c ld h, b ld bc, Card_Offset_tilemap add hl, bc ; address of the length and address of the tilemap inc hl inc hl ; step past the two-byte length of the tilemap ld c, [hl] inc hl ld b, [hl] ld h, b ld l, c ret FindCardKeyTiles: ld hl, cvCardAddress ld c, [hl] inc hl ld b, [hl] ld l, c ld h, b ld bc, Card_Offset_keytiles add hl, bc ; address of the length and address of the key tilels inc hl inc hl ; step past the two-byte length of the key tiles ld c, [hl] inc hl ld b, [hl] ld h, b ld l, c ret FindCardSpriteTiles: ld hl, cvCardAddress ld c, [hl] inc hl ld b, [hl] ld l, c ld h, b ld bc, Card_Offset_spritetiles add hl, bc ; address of the length and address of the sprite tile data inc hl inc hl ; step past the two-byte length of the sprite tile data ld c, [hl] inc hl ld b, [hl] ld h, b ld l, c ret LoadCardData: LoadCardDataAsync: ; first and foremost, clear the card init, update, and draw handles, so there's ; no chance of trying to jump into code from a previous card. di ld bc, Instructions ld hl, CARD_INIT ld [hl], c inc hl ld [hl], b ld hl, CARD_UPDATE ld [hl], c inc hl ld [hl], b ld hl, CARD_DRAW ld [hl], c inc hl ld [hl], b ld hl, CARD_PRINT_PREP ld [hl], c inc hl ld [hl], b ei ld a, [vSelectedCardIndex] ld [vPreviousCardIndex], a ld b, 0 ld c, a ; load bc from a, the number of the card in the cards list ld hl, Cards + 1 ; skip the length prefix add hl, bc add hl, bc add hl, bc ; triple add bc entries are bank, addr, addr ld a, [hl+] ld [cvCardBank], a ld [rROMB0], a ; select the specified bank ; follow the pointer we're looking at, and write it to cvCardAddress ld a, [hl+] ld [cvCardAddress], a ld c, a ld a, [hl+] ld [cvCardAddress + 1], a ld b, a ld a, [vSelectedCardIndex] ld b, 0 ld c, a ; bc from a, the number of the card in the cards list ld hl, DECK_FLIPS + 1 add hl, bc ld a, [hl] ld [vCurrentCardReversed], a cp a, 0 jp z, LoadUpright jp nz, LoadReversed ret ; shouldn't ever get here LoadUpright: ; card struct starts with a sequence of length-prefixed strings in memory ; so when we're done writing one, hl will be correctly placed to read the next ; length-prefixed print doesn't require passing a length ; so all we have to set is the destination for each ld a, [cvCardAddress] ld l, a ld a, [cvCardAddress + 1] ld h, a ; hl now contains the address of the card data. ld bc, Card_Offset_title ; jump straight to sprite tiles length and location! add hl, bc ld de, _SCRN0 + 32*11 + 10 call PrintString ld de, _SCRN0 + 32*12 + 10 call PrintString ; write the "(reversed)" string ld hl, EmptyString ld de, _SCRN0 + 32*13 + 10 call PrintString ld a, [cvCardAddress] ld l, a ld a, [cvCardAddress + 1] ld h, a ; hl now contains the address of the card data. ld bc, Card_Offset_description ; jump straight to sprite tiles length and location! add hl, bc ld de, _SCRN0 + 32*14 + 10 call PrintString ld de, _SCRN0 + 32*15 + 10 call PrintString ld de, _SCRN0 + 32*16 + 10 call PrintString ld a, [cvCardAddress] ld l, a ld a, [cvCardAddress + 1] ld h, a ; hl now contains the address of the card data. ld bc, Card_Offset_tilemap ; jump straight to sprite tiles length and location! add hl, bc ; [hl+] and [hl+] read the length first, into bc ld a, [hl+] ld c, a ld a, [hl+] ld b, a ; bc has length ld a, [hl+] ld e, a ld a, [hl+] ld d, a ; de has source of tile range copy ld h, d ld l, e ; source ld de, _SCRN0 + 32 + 1 ; destination ld b, 16 ; height ld c, 8 ; width call CopyTilesToMap ld a, [cvCardAddress] ld l, a ld a, [cvCardAddress + 1] ld h, a ; hl now contains the address of the card data. ld bc, Card_Offset_functions ; jump straight to function handles add hl, bc ; hl now points to the card functions di ; make sure we never call any callbacks without them being fully set nop ld a, [hl+] ld [CARD_INIT], a ld a, [hl+] ld [CARD_INIT+1], a ld a, [hl+] ld [CARD_UPDATE], a ld a, [hl+] ld [CARD_UPDATE+1], a ld a, [hl+] ld [CARD_DRAW], a ld a, [hl+] ld [CARD_DRAW+1], a ld a, [hl+] ld [CARD_PRINT_PREP], a ld a, [hl+] ld [CARD_PRINT_PREP+1], a ei call CardInit ld a, [cvCardAddress] ld l, a ld a, [cvCardAddress + 1] ld h, a ; hl now contains the address of the card data. ld bc, Card_Offset_keytiles ; jump straight to keytiles length and location! add hl, bc ld a, [hl+] ld c, a ld a, [hl+] ld b, a ; bc has length of tile data ld a, [hl+] ld e, a ld a, [hl+] ld d, a ; de has source of tile range copy ld h, d ld l, e ; hl takes the source ld de, _VRAM + $1000 + VARIABLE_TILES_START*$10 ; always load tile data into the same spot in vram call CopyRangeBy8s ld a, [cvCardAddress] ld l, a ld a, [cvCardAddress + 1] ld h, a ; hl now contains the address of the card data. ld bc, Card_Offset_spritetiles ; jump straight to sprite tiles length and location! add hl, bc ld a, [hl+] ld c, a ld a, [hl+] ld b, a ; bc has length of tile data ld a, [hl+] ld e, a ld a, [hl+] ld d, a ; de has source of tile range copy ld h, d ld l, e ; hl takes the source ld de, _VRAM ; sprite tiles get loaded into the bottom of vram call CopyRangeBy8s ret LoadReversed: ; same as LoadRightSideUp but loads in the reversed data ; card struct starts with a sequence of length-prefixed strings in memory ; so when we're done writing one, hl will be correctly placed to read the next ; length-prefixed print doesn't require passing a length ; so all we have to set is the destination for each ; draw the title ld a, [cvCardAddress] ld l, a ld a, [cvCardAddress + 1] ld h, a ; hl now contains the address of the card data. ld bc, Card_Offset_title ; jump straight to sprite tiles length and location! add hl, bc ld de, _SCRN0 + 32*11 + 10 call PrintString ld de, _SCRN0 + 32*12 + 10 call PrintString ; write the "(reversed)" string ld hl, ReverseString ld de, _SCRN0 + 32*13 + 10 call PrintString ; draw the description ld a, [cvCardAddress] ld l, a ld a, [cvCardAddress + 1] ld h, a ; hl now contains the address of the card data. ld bc, Card_Offset_description_rev ; jump straight to sprite tiles length and location! add hl, bc ld de, _SCRN0 + 32*14 + 10 call PrintString ld de, _SCRN0 + 32*15 + 10 call PrintString ld de, _SCRN0 + 32*16 + 10 call PrintString ; move the tilemap into memory ld a, [cvCardAddress] ld l, a ld a, [cvCardAddress + 1] ld h, a ; hl now contains the address of the card data. ld bc, Card_Offset_tilemap ; jump straight to sprite tiles length and location! add hl, bc ; [hl+] and [hl+] read the length first, into bc ld a, [hl+] ld c, a ld a, [hl+] ld b, a ; bc has length ld a, [hl+] ld e, a ld a, [hl+] ld d, a ; de has source of tile range copy ld h, d ld l, e ; source ld de, _SCRN0 + 32 + 1 ; destination ld b, 16 ; height ld c, 8 ; width call CopyTilesToMapButRotated ; move key tile data into vram ld a, [cvCardAddress] ld l, a ld a, [cvCardAddress + 1] ld h, a ; hl now contains the address of the card data. ld bc, Card_Offset_functions ; jump straight to function handles add hl, bc ; hl now points to the card functions di ; make sure we never call any callbacks without them being fully set nop ld a, [hl+] ld [CARD_INIT], a ld a, [hl+] ld [CARD_INIT+1], a ld a, [hl+] ld [CARD_UPDATE], a ld a, [hl+] ld [CARD_UPDATE+1], a ld a, [hl+] ld [CARD_DRAW], a ld a, [hl+] ld [CARD_DRAW+1], a ld a, [hl+] ld [CARD_PRINT_PREP], a ld a, [hl+] ld [CARD_PRINT_PREP+1], a ei call CardInit ld a, [cvCardAddress] ld l, a ld a, [cvCardAddress + 1] ld h, a ; hl now contains the address of the card data. ld bc, Card_Offset_keytiles ; jump straight to keytiles length and location! add hl, bc ld a, [hl+] ld c, a ld a, [hl+] ld b, a ; bc has length of tile data ld a, [hl+] ld e, a ld a, [hl+] ld d, a ; de has source of tile range copy ld h, d ld l, e ; hl takes the source ld de, _VRAM + $1000 + VARIABLE_TILES_START*$10 ; always load tile data into the same spot in vram call CopyRangeOfTilesButRotated ld a, [cvCardAddress] ld l, a ld a, [cvCardAddress + 1] ld h, a ; hl now contains the address of the card data. ld bc, Card_Offset_spritetiles ; jump straight to sprite tiles length and location! add hl, bc ld a, [hl+] ld c, a ld a, [hl+] ld b, a ; bc has length of tile data ld a, [hl+] ld e, a ld a, [hl+] ld d, a ; de has source of tile range copy ld h, d ld l, e ; hl takes the source ld de, _VRAM ; sprite tiles get loaded into the bottom of vram call CopyRangeOfTilesButRotated ret ReverseString: db 10, "(reversed)" EmptyString: db 10, " " CardInit: ld a, [rLCDC] and a, %1111_1011 ld [rLCDC], a ld hl, ZEROES ld de, MY_OAM ld bc, $100 call CopyRange ld a, %11100100 ld [rOBP0], a ld [rOBP1], a call CARD_INIT - 1 ld a, [vCurrentCardReversed] cp a, 0 call nz, RotateSprites180 ret CardUpdate: ld a, 0 ld [cvNeedsCleanup], a ld a, [vCurrentCardReversed] cp a, 0 call nz, RotateSprites180 call CARD_UPDATE - 1 ld a, [vCurrentCardReversed] cp a, 0 call nz, RotateSprites180 ld a, [cvNeedsCleanup] cp a, 0 call nz, CleanUpOutsideSprites ret CardDraw: call CARD_DRAW - 1 ret CardPrintPrep: call CARD_PRINT_PREP - 1 ret RotateSprites180: ld hl, MY_OAM .loop ld a, 0 cp a, [hl] ; if y value is nonzero, jr nz, :+ ; if y value is nonzero, skip this block where we jump to the next oam entry inc l inc l inc l inc l ld a, l cp a, $A0 ; this indicates we've reached the end of the mirror OAM jr nz, .loop ret : ld a, 168 sub a, [hl] ld [hl+], a ld b, [hl] ld a, 0 cp a, [hl] jr nz, :+ ; if x value is nonzero, skip this block where we jump to the next oam entry inc l inc l inc l ld a, l cp a, $A0 ; this indicates we've reached the end of the mirror OAM jr nz, .loop ret : ld a, 88 sub a, [hl] ld [hl+], a inc l inc l ld a, l cp a, $A0 ; this indicates we've reached the end of the mirror OAM jp nz, .loop ret IncrementTimer: ; hl is the location in memory of a timer ; squashes a and de ; uses the value from rDELTAT ld d, h ld e, l inc de ld a, [rDELTAT] add a, [hl] ld [hl], a ld h, d ld l, e ld a, [hl] adc a, 0 ld [hl], a dec hl ret SetUpEdgeMasks: ld hl, EdgeMasks ld de, _VRAM + 8*16*16 ; 8 rows of 16 tiles each, 16 bytes per tile ld bc, EdgeMasks.end - EdgeMasks call CopyRange ld a, %00_00_00_00 ld [rOBP1], a ret EdgeMasks: dw `00000000, `00000000, `00000000, `00000000, `00000000, `00000000, `00000000, `00000000, dw `00000003, `00000003, `00000003, `00000003, `00000003, `00000003, `00000003, `00000003, dw `00000033, `00000033, `00000033, `00000033, `00000033, `00000033, `00000033, `00000033, dw `00000333, `00000333, `00000333, `00000333, `00000333, `00000333, `00000333, `00000333, dw `00003333, `00003333, `00003333, `00003333, `00003333, `00003333, `00003333, `00003333, dw `00033333, `00033333, `00033333, `00033333, `00033333, `00033333, `00033333, `00033333, dw `00333333, `00333333, `00333333, `00333333, `00333333, `00333333, `00333333, `00333333, dw `03333333, `03333333, `03333333, `03333333, `03333333, `03333333, `03333333, `03333333, dw `33333333, `33333333, `33333333, `33333333, `33333333, `33333333, `33333333, `33333333, .end CleanUpOutsideSprites: ld hl, MY_OAM + 10*4 call HideObviousTiles ; now we hide the overlapping edges ld hl, MY_OAM + 10*4 ld de, MY_OAM .hideEdgesOfSprite ld a, 0 cp a, [hl] jp z, .skipFromY inc l cp a, [hl] jp z, .skipFromX ; now we know that the sprite does not have y=0 or x=0. ;dec hl ;call WriteTopEdgeMaskIfNecessary ;call WriteBottomEdgeMaskIfNecessary ;inc hl call WriteLeftEdgeMaskIfNecessary dec l inc l call WriteRightEdgeMaskIfNecessary dec l .skipFromY inc l .skipFromX inc l inc l inc l ld a, $A0 cp a, l jp nz, .hideEdgesOfSprite ret HideObviousTiles: .processSprite ;hl points at an OAM entry's y value ld a, 16 ; sprites are visible if y value >= 17 cp a, [hl] jr c, :+ ; skip if 17-y < 0 ld [hl], 0 : ld a, 152 ; sprites are visible if y value is < 152 cp a, [hl] jr nc, :+ ; skip if 152 - y < 0 ld [hl], 0 : inc hl ; look at x value ld a, 8 ; sprites are visible if x value >= 8 cp a, [hl] jr c, :+ ; skip if 8-y <= 0 ld [hl], 0 : ld a, 80 ; sprites are visible if x value < 80 cp a, [hl] jr nc, :+ ld [hl], 0 : inc l ; now pointing at tile ID inc l ; now pointing at attributes inc l ; now pointing at next OAM y value ld a, l cp a, $A0 jr nz, .processSprite ret WriteTopEdgeMaskIfNecessary: ;hl points at an OAM entry, de points at an empty OAM spot for masks ld a, 24 ; sprites are clear if y value > 24 cp a, [hl] ret c ; skip if 24-y < 0 ; writing an edge mask on the TOP ; else write an edge sprite ld a, 16 ; y value should be 16 (covers top row) ld [de], a ; y value in first slot inc e ;write head moves forward to x inc l ; read head moves forward to x ld a, [hl] ; x value matches the sprite we're mirroring ld [de], a inc e ; write head moves forward to tile ID ld a, 8*16 + 8 ld [de], a inc e ; write head moves forward to attribute ld a, OAMF_PRI | OAMF_PAL1 ld [de], a dec l inc e ret WriteBottomEdgeMaskIfNecessary: ld a, 145 ; sprites don't overlap edge if y value is <= 145 cp a, [hl] ret nc ; skip if 145 - y >= 0 ; writing an edge mask on the BOTTOM ld a, 144 ; y value should be 144 (covers bottom row) ld [de], a ; y value in first slot inc e ;write head moves forward to x inc l ; read head moves forward to x ld a, [hl] ; x value matches the sprite we're mirroring ld [de], a inc e ; write head moves forward to tile ID ld a, 8*16 + 8 ld [de], a ; tile ID 1 for the hidden ones inc e ; write head moves forward to attribute ld a, OAMF_PRI | OAMF_PAL1 ;OAM_PRIO | OAM_PAL1 ld [de], a dec l inc e ret WriteLeftEdgeMaskIfNecessary: ld a, 16 ; sprites dno't need coverage if x value > 16 cp a, [hl] ret c ; skip if 16-y < 0 ; else write an edge mask on the LEFT dec l ld a, [hl] ; copy y over from sprite ld [de], a inc l ; point at x now inc e ; point at x now ld a, 8 ; x value should be 8 (covers left edge) ld [de], a ; x value inc e ; write head moves forward to tile ID ld a, 1 ld [de], a ; tile ID 1 for the hidden ones inc e ; write head moves forward to attribute ld a, OAMF_PRI | OAMF_PAL1 ld [de], a inc e ret WriteRightEdgeMaskIfNecessary: ld a, 72 ; sprites dont needd coverage if x value < 72 cp a, [hl] ret nc ; else write an edge mask on the RIGHT dec l ld a, [hl] ; copy y over from sprite ld [de], a inc l ; point at x now inc e ; point at x now ld a, [hl] ; x value should match the sprite we're masking... ld [de], a ; x value inc e ; write head moves forward to tile ID ; a currently holds the x coordinate sub a, 72 ; subtract off 72 to get the amount that we're overlapping add a, 8*16 ; add the offset for the mask sprites... ld [de], a ; that makes the tile ID for the mask we want! inc e ; write head moves forward to attribute ld a, OAMF_PRI | OAMF_PAL1 ld [de], a inc e ret