diff --git a/png/map/intro_coll.png b/png/map/intro_coll.png index d4e0b03..2701022 100644 Binary files a/png/map/intro_coll.png and b/png/map/intro_coll.png differ diff --git a/scripts/generate_map.py b/scripts/generate_map.py index ccf6eef..bdfe5a1 100644 --- a/scripts/generate_map.py +++ b/scripts/generate_map.py @@ -10,6 +10,7 @@ from PIL import Image GREEN = (0, 255, 0) RED = (255, 0, 0) BLUE = (0, 0, 255) +YELLOW = (255, 255, 0) def abort(msg: str) -> NoReturn: @@ -17,12 +18,12 @@ def abort(msg: str) -> NoReturn: sys.exit(1) -SpawnPoint = Tuple[int, int] +Point = Tuple[int, int] def generate_coll_map( in_path: pathlib.Path, width: int, height: int, compress: bool = False -) -> Tuple[str, SpawnPoint]: +) -> Tuple[str, Point, Point]: png = Image.open(in_path).convert("RGB") if png.width % 8 != 0 or png.height % 8 != 0: abort(f"file '{in_path}' has invalid dimensions (should be multiple of 8)") @@ -33,6 +34,7 @@ def generate_coll_map( out_bytes = [] bits = [] spawn = None + camera = (0, 0) for y in range(png.height // 8): for x in range(png.width // 8): @@ -46,6 +48,9 @@ def generate_coll_map( elif pixel == BLUE: bit = 1 spawn = (x, y) + elif pixel == YELLOW: + bit = 1 + camera = (x, y) else: abort(f"unsupported pixel in collision map: {pixel}") @@ -63,7 +68,7 @@ def generate_coll_map( if spawn is None: abort(f"no spawn point located") - return format_bytes(out_bytes, width=width), spawn + return format_bytes(out_bytes, width=width), spawn, camera def format_bytes(data: bytes, width: int = 16) -> str: @@ -112,7 +117,9 @@ def generate_map(pngfile: str, compress: bool = False) -> None: section = pngpath.name.replace(".png", "") collpath = pngpath.parent / pngpath.name.replace(".png", "_coll.png") - coll_map, spawn = generate_coll_map(collpath, width, height, compress=compress) + coll_map, spawn, camera = generate_coll_map( + collpath, width, height, compress=compress + ) with open(incpath, "w") as outf: outf.write( @@ -134,10 +141,13 @@ ASSERT {section}_MAP_SIZE == {section}_WIDTH * {section}_HEIGHT {section}_TILE_PTR: DW {section}_TILES {section}_TILE_SIZE: DB ({section}_TILES_end - {section}_TILES) {section}_MAP_PTR: DW {section}_MAP +{section}_MAP_COLLISION: DW {section}_COLLISION {section}_MAP_WIDTH: DB {width} {section}_MAP_HEIGHT: DB {height} {section}_SPAWN_X: DB {spawn[0]} {section}_SPAWN_Y: DB {spawn[1]} +{section}_CAMERA_X: DB {camera[0]} +{section}_CAMERA_Y: DB {camera[1]} {section}_MAP:: {map_data} diff --git a/src/bg.s b/src/bg.s deleted file mode 100644 index 3069a78..0000000 --- a/src/bg.s +++ /dev/null @@ -1,69 +0,0 @@ -INCLUDE "hardware.inc" -INCLUDE "util.inc" - -INCLUDE "png/map/intro.inc" - -SECTION "BG Data", WRAM0 - -BG_COLLISION_DATA:: dw -BG_MAP_WIDTH:: db -BG_MAP_HEIGHT:: db - -BG_SCROLLING:: db - -SECTION "BG Code", ROM0 - -MACRO update_map_info - ld hl, BG_COLLISION_DATA - ld a, HIGH(\1) - ld [hl+], a - ld a, LOW(\1) - ld [hl+], a - - ld a, \2 - ld [hl+], a - - ld a, \3 - ld [hl+], a -ENDM - -BG_Init:: - SET8 BG_SCROLLING, 0 - - ; copy map - ld e, intro_HEIGHT - ld bc, _SCRN0 - ld hl, intro_MAP -.copy_map_row: - ld d, intro_WIDTH - call memcpy - dec e - jr z, .done - ; nothing to skip if map is max width - ld a, 32 - intro_WIDTH - or a - jr z, .copy_map_row - ld d, a -.skip: - ; skip over trailing part in vram - inc c - jr nz, .nocarry - inc b -.nocarry: - dec d - jr nz, .skip - jr .copy_map_row -.done: - - ; copy tiles - ld hl, intro_TILES - ld bc, _VRAM - ld d, intro_NUM_TILES - call memcpy - - update_map_info intro_COLLISION, intro_WIDTH, intro_HEIGHT - - ret - -BG_Update:: - ret diff --git a/src/collision.s b/src/collision.s index 2280bbb..ab9a511 100644 --- a/src/collision.s +++ b/src/collision.s @@ -103,16 +103,16 @@ can_move_to: push bc push hl - ld a, [BG_COLLISION_DATA] + ld a, [CURRENT_MAP_COLLISION] ld h, a - ld a, [BG_COLLISION_DATA + 1] + ld a, [CURRENT_MAP_COLLISION + 1] ld l, a - ld a, [BG_MAP_WIDTH] + ld a, [CURRENT_MAP_WIDTH] ld d, 0 ld e, a - ; compute map index (HL + B * (BG_MAP_WIDTH + 1) + C) + ; compute map index (HL + B * (CURRENT_MAP_WIDTH + 1) + C) .mul_y_width: add hl, de dec b diff --git a/src/main.s b/src/main.s index 4e35120..ced46a9 100644 --- a/src/main.s +++ b/src/main.s @@ -29,10 +29,12 @@ start: ld hl, rIE ld [hl], IEF_VBLANK - call BG_Init call OAM_Init - call Keys_Init - call Player_Init + ; call Keys_Init + ; call Player_Init + + ld hl, intro_Data + call Map_Load ; set palette ld a, %11100100 @@ -52,12 +54,12 @@ start: inc [hl] call Keys_Update - call Player_Update - call BG_Update + ; call Player_Update ; wait for vblank halt + ; ~160 cycles ld a, HIGH(_OAM) call DMA_Start diff --git a/src/map.s b/src/map.s new file mode 100644 index 0000000..341a0f8 --- /dev/null +++ b/src/map.s @@ -0,0 +1,262 @@ +INCLUDE "hardware.inc" +INCLUDE "util.inc" + +SECTION "Map Data", WRAM0 + +PAGEX:: DB +PAGEY:: DB + +CURRENT_DATA_START:: +CURRENT_TILE_PTR:: DW +CURRENT_TILE_SIZE:: DB +CURRENT_MAP_PTR:: DW +CURRENT_MAP_COLLISION:: DW +CURRENT_MAP_WIDTH:: DB +CURRENT_MAP_HEIGHT:: DB +CURRENT_SPAWN_X:: DB +CURRENT_SPAWN_Y:: DB +CURRENT_CAMERA_X:: DB +CURRENT_CAMERA_Y:: DB +CURRENT_DATA_END:: + +SECTION "Map Code", ROM0 + +DEF INIT_SCX EQUS "((SCRN_VX - SCRN_X) / 2)" +DEF INIT_SCY EQUS "((SCRN_VY - SCRN_Y) / 2)" + +DEF PAGE_SIZE EQU 2 + +DEF STACK_OFFSET_ROW EQU 3 +DEF STACK_OFFSET_MAP EQU 1 +DEF STACK_OFFSET_LEFT EQU 0 + +; TODO: Don't hardcode +DEF CAMERAX EQU 0 +DEF CAMERAY EQU 14 + +; Increments a 16-bit value located on the stack +; \1 Stack offset +MACRO INC_STACK16 + ld hl, sp + \1 + inc [hl] + jr nz, .no_carry\@ + + inc hl + inc [hl] + +.no_carry\@: +ENDM + +MACRO ZERO_ROW_TILE + ld hl, sp + STACK_OFFSET_ROW + ld a, [hl+] + ld e, a + ld d, [hl] + xor a + ld [de], a +ENDM + +; Loads a map +; @param hl Pointer to map metadata +Map_Load:: + ; Initialize scroll state + ld a, INIT_SCX + ld [rSCX], a + + ld a, INIT_SCY + ld [rSCY], a + + xor a + ld [PAGEX], a + ld [PAGEY], a + + ; Store metadata + ld bc, CURRENT_DATA_START + ld d, CURRENT_DATA_END - CURRENT_DATA_START + call memcpy + + ; Write tiles to VRAM + ld hl, CURRENT_TILE_PTR + ld a, [hl+] + ld c, a + ld a, [hl+] + ld b, a + + ld a, [CURRENT_TILE_SIZE] + ld d, a + + ld hl, _VRAM + MEMCPY hl, bc, d + + ; Write initial map data + ld hl, _SCRN0 + + ld a, [CURRENT_CAMERA_X] + sub INIT_SCX / 8 + ld b, a + + ld a, [CURRENT_CAMERA_Y] + sub INIT_SCY / 8 + ld c, a + + ld d, SCRN_VY_B +.write_rows: + call write_map_row + inc c + dec d + jr nz, .write_rows + + ret + +; Write a row of map data into map RAM +; @param b Map X coordinate (signed) +; @param c Map Y coordinate (signed) +; @param hl Start of the row to write +write_map_row: + push bc ; sp + 7 + push de ; sp + 5 + push hl ; sp + 3 + + ; If Y < 0, write a row of 0s + bit 7, c + jr nz, .zero_row + + ; If Y >= MAP_HEIGHT, write a row of 0s + ld a, [CURRENT_MAP_HEIGHT] + dec a + cp c + jr nc, .begin_writing + +.zero_row: + ld d, SCRN_VX_B + xor a +.zero_row_loop: + ld a, [hl+] + dec d + jr nz, .zero_row_loop +.zero_row_done: + pop hl + pop de + pop bc + ret + +.begin_writing: + ; Allocate 2 bytes for map pointer + ; Allocate 1 byte for number of tiles left + add sp, -3 + + ; left = number of tiles left to write + ld hl, sp + STACK_OFFSET_LEFT + ld [hl], SCRN_VX_B + + ; HL = CURRENT_MAP_PTR + Y * CURRENT_MAP_HEIGHT + ld a, [CURRENT_MAP_PTR] + ld l, a + ld a, [CURRENT_MAP_PTR + 1] + ld h, a + +.compute_map_row: + ld a, c + or a + jr z, .compute_map_row_done + + ld a, [CURRENT_MAP_HEIGHT] + ADD16 HL + dec c + jr .compute_map_row + + ; map = HL +.compute_map_row_done: + LD16 de, hl + ld hl, sp + STACK_OFFSET_MAP + ld a, e + ld [hl+], a + ld a, d + ld [hl], a + + ld hl, sp + STACK_OFFSET_LEFT + + ; While X < 0 and left > 0, write 0 to the row +.pad_left: + bit 7, b + jr z, .copy_center + + ld a, [hl] + or a + jr z, .done + + ; *row = 0 + ZERO_ROW_TILE + + ; row++ + INC_STACK16 STACK_OFFSET_ROW + + ; X++, left-- + inc b + ld hl, sp + STACK_OFFSET_LEFT + dec [hl] + jr .pad_left + + ; While X < MAP_WIDTH and [SP] > 0, copy from map +.copy_center: + ld a, [CURRENT_MAP_WIDTH] + dec a + cp b + jr z, .pad_right + + ld a, [hl] + or a + jr z, .done + + ; A = *map_ptr + ld hl, sp + STACK_OFFSET_MAP + ld a, [hl+] + ld e, a + ld d, [hl] + ld a, [de] + + ; map_ptr++ + INC_STACK16 STACK_OFFSET_MAP + + ; *row = A + ld hl, sp + STACK_OFFSET_ROW + ld e, [hl] + inc hl + ld d, [hl] + ld [de], a + + ; row++ + INC_STACK16 3 + + ; X++, [SP]-- +.copy_center_inc: + inc b + ld hl, sp + STACK_OFFSET_LEFT + dec [hl] + jr .copy_center + + ; While [SP] > 0, write 0 to the row +.pad_right: + ld a, [hl] + or a + jr z, .done + + ; *row = 0 + ZERO_ROW_TILE + + ; row++ + INC_STACK16 STACK_OFFSET_ROW + + ; [SP]-- +.pad_right_inc: + ld hl, sp + STACK_OFFSET_ROW + dec [hl] + jr .pad_right + +.done: + add sp, 3 + pop hl + pop de + pop bc + + ret diff --git a/src/player.s b/src/player.s index c66b742..885117c 100644 --- a/src/player.s +++ b/src/player.s @@ -38,15 +38,12 @@ Player_Init:: inc hl ld [hl], 144-32 inc hl - ld [hl], 0 - inc hl - ld [hl], 0 - inc hl - ld [hl], 0 - inc hl - ld [hl], 0 - inc hl - ld [hl], 0 + 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 @@ -55,39 +52,30 @@ Player_Init:: call memcpy ; Initialize OAM entries with player state - call update_oam + call Player_UpdateOAM ret Player_Update:: - ; suspend player state while map is scrolling - ld hl, BG_SCROLLING - ld a, [hl] - or a - ret nz - ; check for jump - ld hl, keys - ld b, [hl] - ld a, b + ld a, [keys] and BTN_UP jr z, .jump_update_check ; initialize jump state if not already jumping - ld hl, PLAYER_JUMPING - ld a, [hl] + ld a, [PLAYER_JUMPING] or a jr nz, .jump_update - ld [hl], 1 + ld a, 1 + ld [PLAYER_JUMPING], a ld hl, PLAYER_VY STORE16 INIT_VY ; todo: deduplicate .jump_update_check: - ld hl, PLAYER_JUMPING - ld a, [hl] + ld a, [PLAYER_JUMPING] or a jr z, .right @@ -120,7 +108,7 @@ Player_Update:: ; roll back jump if there was a collision call player_bg_collides - jr z, .jump_update_oam + jr z, .jump_Player_UpdateOAM .jump_rollback: ld hl, PLAYER_VY @@ -142,43 +130,44 @@ Player_Update:: ld hl, PLAYER_VY bit 7, [hl] - ; either way, set VY = 0 + ; 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_update_oam + jr z, .jump_Player_UpdateOAM - ld hl, PLAYER_JUMPING - ld [hl], 0 + xor a + ld [PLAYER_JUMPING], a -.jump_update_oam: - call update_oam +.jump_Player_UpdateOAM: + call Player_UpdateOAM ; check for move right .right: - ld hl, keys - ld a, [hl] + ld a, [keys] and BTN_RIGHT jr z, .left ; check for right boundary - ld hl, PLAYER_X - ld a, [hl] + 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 - ld hl, PLAYER_DIR - ld [hl], 0 + xor a + ld [PLAYER_DIR], a - call update_oam + call Player_UpdateOAM ret .right_rollback: @@ -195,21 +184,20 @@ Player_Update:: jr z, .fall ; check for left boundary - ld hl, PLAYER_X - ld a, [hl] + ld a, [PLAYER_X] or a jr z, .fall sub PLAYER_SPEED - ld [hl], a + ld [PLAYER_X], a call player_bg_collides jr nz, .left_rollback - ld hl, PLAYER_DIR - ld [hl], $ff + ld a, $ff + ld [PLAYER_DIR], a - call update_oam + call Player_UpdateOAM jr .fall .left_rollback: @@ -235,7 +223,7 @@ Player_Update:: ret ; Update player metasprite with positional data -update_oam: +Player_UpdateOAM:: ld hl, PLAYER_DIR ld a, [hl-] ; dir (0 = right , $ff = left) and OAMF_XFLIP