INCLUDE "hardware.inc" INCLUDE "oam.inc" INCLUDE "player.inc" INCLUDE "util.inc" Section "Player Data", WRAM0 DEF PLAYER_INV_SIZE EQU 32 PLAYER_X:: db PLAYER_Y:: dw PLAYER_DIR:: db PLAYER_STATE:: db PLAYER_VY:: dw PLAYER_INV:: ds (2 * PLAYER_INV_SIZE) Section "Player Code", ROM0 spriteData: INCBIN "png/sprite/player.2bpp" DEF SPRITE_OAM_IDX EQU 0 DEF SPRITE_IDX EQU 32 ; tile index, rename later DEF SPRITE_WIDTH EQU 2 DEF SPRITE_HEIGHT EQU 2 DEF PLAYER_SPEED EQU 2 DEF TILE_WIDTH EQU 8 ; Width of tile in bytes DEF TILE_HEIGHT EQU 8 ; Height of tile in bytes DEF TILE_SIZE EQU 16 ; Total length of tile in bytes DEF GRAVITY EQU (0 << 8) | $2e ; 0.18 DEF INIT_VY EQU (3 << 8) | $99 ; 3.60 DEF INIT_FALL_VY EQU ($ff << 8) | $1a ; -1.1015625 DEF MAX_VY EQU ($f9 << 8) | $00 ; -7.0 Player_Init:: ; Clear player data (X/Y initialized by map engine) ld hl, PLAYER_DIR xor a REPT (7 + 2 * PLAYER_INV_SIZE) ld [hl+], a ENDR ; Copy sprite to VRAM ld bc, _VRAM8000 + SPRITE_IDX * TILE_SIZE ld hl, spriteData ld d, TILE_SIZE * (SPRITE_WIDTH * SPRITE_HEIGHT) call memcpy ; Initialize OAM entries with player state call Player_UpdateOAM ret Player_Update:: ; check for jump ld a, [keys] and PADF_UP jr z, .jump_update_check ; initialize jump state if not already jumping ld a, [PLAYER_STATE] and PLAYER_STATEF_JUMP jr nz, .jump_update ld a, PLAYER_STATEF_JUMP ld [PLAYER_STATE], a ld hl, PLAYER_VY STORE16 INIT_VY ; todo: deduplicate .jump_update_check: ld a, [PLAYER_STATE] and PLAYER_STATEF_JUMP jr z, .right .jump_update: ; load y velocity into bc ld hl, PLAYER_VY ld b, [hl] inc hl ld c, [hl] ; adjust velocity by gravity ld a, c sub LOW(GRAVITY) ld c, a ld a, b sbc HIGH(GRAVITY) ld b, a ; ; we only need to clamp when moving down ; bit 7, b ; jr z, .no_clamp ; ; vy = MIN(MAX_VY, vy) ; ld a, b ; cp HIGH(MAX_VY) ; jr c, .no_clamp ; ld b, HIGH(MAX_VY) ; ld c, LOW(MAX_VY) .no_clamp: ld hl, PLAYER_VY STORE16 bc ; y -= floor(vy) ld hl, PLAYER_Y + 1 ld a, [hl] sub c ld [hl-], a ld a, [hl] sbc b ld [hl], a ; roll back jump if there was a collision call player_bg_collides jr nz, .jump_Player_UpdateOAM ; TODO: Enable once scroll zones are implemented ; ld a, [PLAYER_VY] ; check if VY < 0 and call Map_ScrollUp / Map_ScrollDown .jump_rollback: ld hl, PLAYER_VY bit 7, [hl] ld hl, PLAYER_Y jr z, .jump_rollback_down dec [hl] jr .jump_rollback_check .jump_rollback_down: inc [hl] .jump_rollback_check: call player_bg_collides jr z, .jump_rollback ; set PLAYER_STATE = PLAYER_STATEF_WALK if VY < 0 (fell into ground) ld hl, PLAYER_VY bit 7, [hl] ; either way, set VY = 0. ; NOTE: MUST USE LD HERE TO KEEP FLAGS FROM BIT TEST ld [hl], 0 ld hl, PLAYER_VY + 1 ld [hl], 0 jr z, .jump_Player_UpdateOAM ld a, PLAYER_STATEF_WALK ld [PLAYER_STATE], a .jump_Player_UpdateOAM: call Player_UpdateOAM ; check for move right .right: ld a, [keys] and PADF_RIGHT jr z, .left ; check for right boundary ld a, [PLAYER_X] add 16 cp SCRN_X jr nc, .left .move_right: ld a, [PLAYER_X] add PLAYER_SPEED ld [PLAYER_X], a call player_bg_collides jr z, .right_rollback ; TODO: Enable once scroll zones are implemented ; ld d, PLAYER_SPEED ; call Map_ScrollRight xor a ld [PLAYER_DIR], a call Player_UpdateOAM ret .right_rollback: ld a, [PLAYER_X] sub PLAYER_SPEED ld [PLAYER_X], a ret .left: ; check for left button ld hl, keys ld a, [hl] and PADF_LEFT jr z, .fall ; check for left boundary ld a, [PLAYER_X] or a jr z, .fall sub PLAYER_SPEED ld [PLAYER_X], a call player_bg_collides jr z, .left_rollback ; TODO: Enable once scroll zones are implemented ; ld d, PLAYER_SPEED ; call Map_ScrollLeft ld a, $ff ld [PLAYER_DIR], a call Player_UpdateOAM jr .fall .left_rollback: ld hl, PLAYER_X inc [hl] inc [hl] .fall: call player_in_air ret z ; only set jump state if not already jumping ld hl, PLAYER_STATE ld a, [hl] and PLAYER_STATEF_JUMP ret nz ld [hl], PLAYER_STATEF_JUMP ld hl, PLAYER_VY STORE16 INIT_FALL_VY ret ; Update player metasprite with positional data Player_UpdateOAM:: ld hl, PLAYER_DIR ld a, [hl-] ; dir (0 = right , $ff = left) and OAMF_XFLIP ld c, a dec hl ; skip fractional part ld a, [hl-] ; y add 16 ld d, a ld a, [hl] ; x add 8 ld b, a ld a, d ld_OAM_y hl, SPRITE_OAM_IDX ; top left sprite ld [hli], a ld [hl], b inc hl ld [hl], SPRITE_IDX inc hl ld [hl], c inc hl ; top right ld d, a ld a, b add 8 ld b, a ld a, d ld [hli], a ld [hl], b inc hl ld [hl], SPRITE_IDX + 1 inc hl ld [hl], c inc hl ; bottom left add 8 ld d, a ld a, b sub 8 ld b, a ld a, d ld [hli], a ld [hl], b inc hl ld [hl], SPRITE_IDX + 2 inc hl ld [hl], c inc hl ; bottom right ld d, a ld a, b add 8 ld b, a ld a, d ld [hli], a ld [hl], b inc hl ld [hl], SPRITE_IDX + 3 inc hl ld [hl], c ; flip tile indices if necessary ld a, c or c jr z, .done ; TODO: Can be sped up by using LD A, [NN] ld_OAM_tile hl, SPRITE_OAM_IDX ld [hl], SPRITE_IDX + 1 ld_OAM_tile hl, SPRITE_OAM_IDX + 1 ld [hl], SPRITE_IDX ld_OAM_tile hl, SPRITE_OAM_IDX + 2 ld [hl], SPRITE_IDX + 3 ld_OAM_tile hl, SPRITE_OAM_IDX + 3 ld [hl], SPRITE_IDX + 2 .done: ret ; Add item(s) to the player's inventory ; @param b Item ID ; @param c Item quantity Player_AddInvItem:: push de ld hl, PLAYER_INV ld e, PLAYER_INV_SIZE .find_inv_slot: ld a, [hl] cp b jr z, .update_item inc hl dec e jr nz, .find_inv_slot .update_item: ; set item id ld a, b ld [hl+], a ; update item qty ld a, [hl] add c ld [hl], a pop de ret