Browse Source

Add support for loading 127x127 maps

master
Forest Belton 3 years ago
parent
commit
bcb1609897
7 changed files with 320 additions and 127 deletions
  1. BIN
      png/map/intro_coll.png
  2. +14
    -4
      scripts/generate_map.py
  3. +0
    -69
      src/bg.s
  4. +4
    -4
      src/collision.s
  5. +7
    -5
      src/main.s
  6. +262
    -0
      src/map.s
  7. +33
    -45
      src/player.s

BIN
png/map/intro_coll.png View File

Before After
Width: 256  |  Height: 256  |  Size: 721 B Width: 256  |  Height: 256  |  Size: 723 B

+ 14
- 4
scripts/generate_map.py View File

@ -10,6 +10,7 @@ from PIL import Image
GREEN = (0, 255, 0) GREEN = (0, 255, 0)
RED = (255, 0, 0) RED = (255, 0, 0)
BLUE = (0, 0, 255) BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
def abort(msg: str) -> NoReturn: def abort(msg: str) -> NoReturn:
@ -17,12 +18,12 @@ def abort(msg: str) -> NoReturn:
sys.exit(1) sys.exit(1)
SpawnPoint = Tuple[int, int]
Point = Tuple[int, int]
def generate_coll_map( def generate_coll_map(
in_path: pathlib.Path, width: int, height: int, compress: bool = False 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") png = Image.open(in_path).convert("RGB")
if png.width % 8 != 0 or png.height % 8 != 0: if png.width % 8 != 0 or png.height % 8 != 0:
abort(f"file '{in_path}' has invalid dimensions (should be multiple of 8)") abort(f"file '{in_path}' has invalid dimensions (should be multiple of 8)")
@ -33,6 +34,7 @@ def generate_coll_map(
out_bytes = [] out_bytes = []
bits = [] bits = []
spawn = None spawn = None
camera = (0, 0)
for y in range(png.height // 8): for y in range(png.height // 8):
for x in range(png.width // 8): for x in range(png.width // 8):
@ -46,6 +48,9 @@ def generate_coll_map(
elif pixel == BLUE: elif pixel == BLUE:
bit = 1 bit = 1
spawn = (x, y) spawn = (x, y)
elif pixel == YELLOW:
bit = 1
camera = (x, y)
else: else:
abort(f"unsupported pixel in collision map: {pixel}") abort(f"unsupported pixel in collision map: {pixel}")
@ -63,7 +68,7 @@ def generate_coll_map(
if spawn is None: if spawn is None:
abort(f"no spawn point located") 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: 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", "") section = pngpath.name.replace(".png", "")
collpath = pngpath.parent / pngpath.name.replace(".png", "_coll.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: with open(incpath, "w") as outf:
outf.write( outf.write(
@ -134,10 +141,13 @@ ASSERT {section}_MAP_SIZE == {section}_WIDTH * {section}_HEIGHT
{section}_TILE_PTR: DW {section}_TILES {section}_TILE_PTR: DW {section}_TILES
{section}_TILE_SIZE: DB ({section}_TILES_end - {section}_TILES) {section}_TILE_SIZE: DB ({section}_TILES_end - {section}_TILES)
{section}_MAP_PTR: DW {section}_MAP {section}_MAP_PTR: DW {section}_MAP
{section}_MAP_COLLISION: DW {section}_COLLISION
{section}_MAP_WIDTH: DB {width} {section}_MAP_WIDTH: DB {width}
{section}_MAP_HEIGHT: DB {height} {section}_MAP_HEIGHT: DB {height}
{section}_SPAWN_X: DB {spawn[0]} {section}_SPAWN_X: DB {spawn[0]}
{section}_SPAWN_Y: DB {spawn[1]} {section}_SPAWN_Y: DB {spawn[1]}
{section}_CAMERA_X: DB {camera[0]}
{section}_CAMERA_Y: DB {camera[1]}
{section}_MAP:: {section}_MAP::
{map_data} {map_data}

+ 0
- 69
src/bg.s View File

@ -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

+ 4
- 4
src/collision.s View File

@ -103,16 +103,16 @@ can_move_to:
push bc push bc
push hl push hl
ld a, [BG_COLLISION_DATA]
ld a, [CURRENT_MAP_COLLISION]
ld h, a ld h, a
ld a, [BG_COLLISION_DATA + 1]
ld a, [CURRENT_MAP_COLLISION + 1]
ld l, a ld l, a
ld a, [BG_MAP_WIDTH]
ld a, [CURRENT_MAP_WIDTH]
ld d, 0 ld d, 0
ld e, a 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: .mul_y_width:
add hl, de add hl, de
dec b dec b

+ 7
- 5
src/main.s View File

@ -29,10 +29,12 @@ start:
ld hl, rIE ld hl, rIE
ld [hl], IEF_VBLANK ld [hl], IEF_VBLANK
call BG_Init
call OAM_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 ; set palette
ld a, %11100100 ld a, %11100100
@ -52,12 +54,12 @@ start:
inc [hl] inc [hl]
call Keys_Update call Keys_Update
call Player_Update
call BG_Update
; call Player_Update
; wait for vblank ; wait for vblank
halt halt
; ~160 cycles
ld a, HIGH(_OAM) ld a, HIGH(_OAM)
call DMA_Start call DMA_Start

+ 262
- 0
src/map.s View File

@ -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

+ 33
- 45
src/player.s View File

@ -38,15 +38,12 @@ Player_Init::
inc hl inc hl
ld [hl], 144-32 ld [hl], 144-32
inc hl 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 ; Copy sprite to VRAM
ld bc, _VRAM8000 + SPRITE_IDX * 16 ld bc, _VRAM8000 + SPRITE_IDX * 16
@ -55,39 +52,30 @@ Player_Init::
call memcpy call memcpy
; Initialize OAM entries with player state ; Initialize OAM entries with player state
call update_oam
call Player_UpdateOAM
ret ret
Player_Update:: Player_Update::
; suspend player state while map is scrolling
ld hl, BG_SCROLLING
ld a, [hl]
or a
ret nz
; check for jump ; check for jump
ld hl, keys
ld b, [hl]
ld a, b
ld a, [keys]
and BTN_UP and BTN_UP
jr z, .jump_update_check jr z, .jump_update_check
; initialize jump state if not already jumping ; initialize jump state if not already jumping
ld hl, PLAYER_JUMPING
ld a, [hl]
ld a, [PLAYER_JUMPING]
or a or a
jr nz, .jump_update jr nz, .jump_update
ld [hl], 1
ld a, 1
ld [PLAYER_JUMPING], a
ld hl, PLAYER_VY ld hl, PLAYER_VY
STORE16 INIT_VY STORE16 INIT_VY
; todo: deduplicate ; todo: deduplicate
.jump_update_check: .jump_update_check:
ld hl, PLAYER_JUMPING
ld a, [hl]
ld a, [PLAYER_JUMPING]
or a or a
jr z, .right jr z, .right
@ -120,7 +108,7 @@ Player_Update::
; roll back jump if there was a collision ; roll back jump if there was a collision
call player_bg_collides call player_bg_collides
jr z, .jump_update_oam
jr z, .jump_Player_UpdateOAM
.jump_rollback: .jump_rollback:
ld hl, PLAYER_VY ld hl, PLAYER_VY
@ -142,43 +130,44 @@ Player_Update::
ld hl, PLAYER_VY ld hl, PLAYER_VY
bit 7, [hl] 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], 0
ld hl, PLAYER_VY + 1 ld hl, PLAYER_VY + 1
ld [hl], 0 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 ; check for move right
.right: .right:
ld hl, keys
ld a, [hl]
ld a, [keys]
and BTN_RIGHT and BTN_RIGHT
jr z, .left jr z, .left
; check for right boundary ; check for right boundary
ld hl, PLAYER_X
ld a, [hl]
ld a, [PLAYER_X]
add 16 add 16
cp SCRN_X cp SCRN_X
jr nc, .left jr nc, .left
.move_right:
ld hl, PLAYER_X
inc [hl] inc [hl]
inc [hl] inc [hl]
call player_bg_collides call player_bg_collides
jr nz, .right_rollback jr nz, .right_rollback
ld hl, PLAYER_DIR
ld [hl], 0
xor a
ld [PLAYER_DIR], a
call update_oam
call Player_UpdateOAM
ret ret
.right_rollback: .right_rollback:
@ -195,21 +184,20 @@ Player_Update::
jr z, .fall jr z, .fall
; check for left boundary ; check for left boundary
ld hl, PLAYER_X
ld a, [hl]
ld a, [PLAYER_X]
or a or a
jr z, .fall jr z, .fall
sub PLAYER_SPEED sub PLAYER_SPEED
ld [hl], a
ld [PLAYER_X], a
call player_bg_collides call player_bg_collides
jr nz, .left_rollback 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 jr .fall
.left_rollback: .left_rollback:
@ -235,7 +223,7 @@ Player_Update::
ret ret
; Update player metasprite with positional data ; Update player metasprite with positional data
update_oam:
Player_UpdateOAM::
ld hl, PLAYER_DIR ld hl, PLAYER_DIR
ld a, [hl-] ; dir (0 = right , $ff = left) ld a, [hl-] ; dir (0 = right , $ff = left)
and OAMF_XFLIP and OAMF_XFLIP

Loading…
Cancel
Save