INCLUDE "hardware.inc" INCLUDE "input.inc" INCLUDE "oam.inc" INCLUDE "util.inc" Section "Player Data", WRAM0 playerWorldX: dw playerWorldY: dw ; player data PLAYER_X:: db PLAYER_Y:: dw PLAYER_DIR:: db PLAYER_JUMPING:: db PLAYER_VY:: dw 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 GRAVITY EQU (0 << 8) | $2e ; 0.18 DEF INIT_VY EQU (3 << 8) | $99 ; 3.60 DEF INIT_FALL_VY EQU ($ff << 8) | $1a ; a.b Player_Init:: ; Clear player data ld hl, PLAYER_X ld [hl], 0 inc hl ld [hl], 144-32 inc hl xor a ld [hl+], a ld [hl+], a ld [hl+], a ld [hl+], a ld [hl+], a ; Copy sprite to VRAM ld bc, _VRAM8000 + SPRITE_IDX * 16 ld hl, spriteData ld d, 16 * (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 BTN_UP jr z, .jump_update_check ; initialize jump state if not already jumping ld a, [PLAYER_JUMPING] or a jr nz, .jump_update ld a, 1 ld [PLAYER_JUMPING], a ld hl, PLAYER_VY STORE16 INIT_VY ; todo: deduplicate .jump_update_check: ld a, [PLAYER_JUMPING] or a 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 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 z, .jump_Player_UpdateOAM .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 nz, .jump_rollback ; set PLAYER_JUMPING = 0 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 xor a ld [PLAYER_JUMPING], a .jump_Player_UpdateOAM: call Player_UpdateOAM ; check for move right .right: ld a, [keys] and BTN_RIGHT jr z, .left ; check for right boundary ld a, [PLAYER_X] add 16 cp SCRN_X jr nc, .left .move_right: ld hl, PLAYER_X inc [hl] inc [hl] call player_bg_collides jr nz, .right_rollback xor a ld [PLAYER_DIR], a call Player_UpdateOAM ret .right_rollback: ld hl, PLAYER_X dec [hl] dec [hl] ret .left: ; check for left button ld hl, keys ld a, [hl] and BTN_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 nz, .left_rollback 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 nz ; only set jump state if not already jumping ld hl, PLAYER_JUMPING ld a, [hl] or a ret nz ld [hl], 1 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 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