-
- ; same as actor_try_add
- ; without inputs
-actor_try_add_player_projectile:
- ld hl, actors_player_projectiles
- ld b, ACTS_PLAYER_PROJECTILES
- jp actor_try_add
-
- ; same as actor_try_add
- ; without inputs
-actor_try_add_enemy:
- ld hl, actors_enemy
- ld b, ACTS_ENEMY
- jp actor_try_add
-
- ; attempts to spawna a new actor
- ; by searching for a free slot of type 0
- ; inputs:
- ; hl: actor table
- ; b: max
- ; returns:
- ; hl: free actor ptr
- ; hl: NULL if no slot is available
- ; note: memory is not cleared
- ; the caller will have to initialize the actor
-actor_try_add:
- ld de, act_size
-@loop:
- ld a, [hl] ; check type
- cp a, 0
- ret z ; if 0 type -> match! return now
- ; go to next ptr
- add hl, de
- dec b
- jr nz, @loop REL
-
- ; nothing found
- ld hl, NULL
- ret
-
-
- ; draws a single object
- ; for an actor accounting for scroll
- ; inputs:
- ; de: actor
- ; hl: oam ptr
- ; b: tile
- ; c: oam flags
- ; a: nnnn0000: y offset from actor pos;
- ; 0000nnnn: x offset from actor pos
- ; returns:
- ; hl: next oam ptr (if reserved!)
-actor_draw:
- push hl
- ld hl, act_pos_y
- add hl, de
- push hl
- pop de ; de = pos y
- pop hl
-
- ; hl = oam ptr again
-
- ; deal with y position
- push bc
- push af
-
- ; get y offset
- and a, 0xF0
- swap a ; a = y offset
- ld b, a
- ld a, [de]
- add a, OBJ_OFF_Y
- add a, b ; a = y postion
-
- inc de ; de = x pos
-
- ld [hl+], a
-
- pop af
-
- ; deal with x position
-
- and a, 0xF ; a = x offset
- ld b, a
- ld a, [de]
- add a, b ; b = x position
- add a, OBJ_OFF_X
-
- ld [hl+], a
-
- pop bc
-
- ; tile
- ld a, b
- ld [hl+], a
-
- ; oam flags
- ld a, c
- ld [hl+], a
-
-
- ret
-
- ; actor no-op call
-act_nop:
- ret
-
- ; tables for each actor type
-actor_update_draw_table:
- dw act_nop
- dw player_update_and_draw
- dw act_guard_update_and_draw
- dw act_nop
- dw act_nop
- dw act_projectile_arrow_update_and_draw
-
- ; called when an actor is spawned
- ; via a map object
-actor_init_table:
- dw act_nop
- dw act_nop
- dw act_guard_init
- dw act_nop
- dw act_nop
- dw act_nop
-
-actor_collision_res_table:
- dw act_nop
- dw player_col_res
- dw act_guard_col_res
- dw act_nop
- dw act_nop
- dw act_projectile_arrow_col_res
-
- ; inits an actors
- ; inputs:
- ; de: actor
-actor_init:
- ld a, [de]
- add a, a ; * 2 for offset
- ld hl, actor_init_table
- ld b, 0
- ld c, a
- add hl, bc
-
- ld a, [hl+]
- ld b, a
- ld a, [hl]
- ld h, a
- ld l, b
- call_hl
-
- ret
-
- ; calls update for all actors
- ; and draws them
-actor_update_all:
- ld de, actors
- ld b, ACTS_MAX
-
- ; updates a partial actor table
- ; inputs:
- ; de: actors
- ; b: size
-actor_update_table:
-@loop:
- ; look up rotuine from table
- ld a, [de] ; a = type
- add a, a ; * 2 for offset
-
- push_all
- ld b, 0
- ld c, a ; bc = offset
- ld hl, actor_update_draw_table
- add hl, bc
- ld a, [hl+]
- ld b, a
- ld a, [hl]
- ld h, a
- ld l, b
-
- call_hl
- pop_all
-
- ; move to next actor
- ld hl, act_size
- add hl, de
- push hl
- pop de ; de = next act
-
- dec b ; counter--
- jr nz, @loop REL
- ret
-
- ; despawns an actor
- ; inputs:
- ; de: actor ptr
-actor_despawn:
- xor a, a
- ld [de], a
- ret
-
- ; writes actor default collider
- ; bsed on an input position
- ; writes the collider to dst
- ; e.g. use with actor's real rectangle
- ; if real collision should be written
- ; use with tmp_rect for collision tests before moving
- ; inputs:
- ; a: collision mask
- ; b/c: y/x position of actor
- ; hl: destination rectangle
-actor_write_default_collider:
- ld [hl+], a ; mask
- ld a, b
- add a, 14
- ld [hl+], a ; y pos
-
- ld a, c
- add a, 2
- ld [hl+], a ; x pos
-
- ld a, 12 ; width/height
- ld [hl+], a ; height
-
- ld a, 10
- ld [hl+], a ; width
- ret
-
- ; performs a single test case
- ; inputs:
- ; tmp_rect: new target rect
- ; de: ptr to 2 coordinates (y/x) e.g. tmp_rect_points
- ; $1: rect_bl/tl/br/tr call
- ; hl: rectangle to compare to
- ; jumps to $1 on collision
- ; returns:
- ; de: de+=2
- ; preserves: hl
-#macro actor_test_movement_corner
- ld a, [de]
- inc de
- ld b, a
- ld a, [de]
- inc de
- ld c, a
- ; bc = point
-
- push hl
- push de
- call rect_point_test
- pop de
- pop hl
- cp a, 1
- jp z, $1
-#endmacro
-
- ; tests movement against all collision rectangles
- ; inputs:
- ; hl: actor table start
- ; c: actor table length
- ; a: collision mask
- ; b: direction
- ; de: current actor (this actor is skipped)
- ; tmp_rect: new collision rectangle
- ; returns:
- ; a == 0: no collision
- ; a == 1: collision
- ; hl: if collided with an actor points to the actor hit, otherwise NULL
-actor_test_movement:
- ; save actor table info
- push hl
- push bc
-
- ; save current actor
- push de
- ld [tmp_rect_mask], a
-
- ld a, b
- ld [tmp_act_direction], a
- ; a = direction
- call actor_test_movement_write_points
-
- ; check all static rectangles
- ld hl, rectangles
- ld b, RECT_MAX
-
-@rect_test_loop:
- push hl
- push bc
- call actor_test_rect
- pop bc
- pop hl
- cp a, 0
- jp nz, @rect_collision
-@skip_rect:
- ld de, r_size
- add hl, de ; next rect (de = r_size)
- dec b
- jp nz, @rect_test_loop
-
- pop de
-
- pop bc
- ld b, c ; b = actor table lenth
-
- pop hl ; hl = actor table
-
-@actor_test_loop:
- ld a, [hl]
- cp a, 0 ; check if type is 0
- jp z, @skip_actor
-
- ; check if current actor is in de
- ld a, d
- cp a, h
- jr nz, @test_actor REL
- ld a, e
- cp a, l
- jr z, @skip_actor REL
-
-@test_actor:
- push de
- push hl
- push bc
- ld de, act_rect
- add hl, de ; hl = rectangle for actor
-
- call actor_test_rect
- cp a, 0
- pop bc
- pop hl
- pop de
- jp nz, @actor_collision
-
-@skip_actor:
- push bc
- ld bc, act_size
- add hl, bc ; next actor
- pop bc
- dec b
- jp nz, @actor_test_loop
-
-@no_collision:
- ld hl, NULL
- xor a, a ; no collision
- ret
-@rect_collision:
- ; need to pop 3 values that were pushed
- ; before rect loop
- pop de
- pop hl
- pop bc
- ld hl, NULL
- ld a, 1
- ret
-@actor_collision:
- ; hl = actor already
- ld a, 1
- ret
-
- ; writes a point to a rectangle
- ; inputs:
- ; hl: rectangle
- ; bc: tmp_rect_points ptr
- ; $1 rect_tr,bl,br,tl
-#macro act_test_movement_write_point_at
- push hl
- push bc
- call $1
- pop bc
- pop hl
-
- ld a, d
- ld [bc], a
- inc bc
- ld a, e
- ld [bc], a
- inc bc
-#endmacro
-
- ; writes tmp rect points to
- ; tmp_rect_points. only writes 2 points
- ; based on the chosen direction
- ; inputs:
- ; a: direction
- ; tmp_rect: the rectangle
-actor_test_movement_write_points:
- ld hl, tmp_rect
- ld bc, tmp_rect_points
-
- cp a, DIRDOWN
- jp z, @down
- cp a, DIRLEFT
- jp z, @left
- cp a, DIRRIGHT
- jp z, @right
-
- ; default case: up
-@up:
- act_test_movement_write_point_at rect_tl
- act_test_movement_write_point_at rect_tr
- ret
-@down:
- act_test_movement_write_point_at rect_bl
- act_test_movement_write_point_at rect_br
- ret
-@right:
- act_test_movement_write_point_at rect_br
- act_test_movement_write_point_at rect_tr
- ret
-@left:
- act_test_movement_write_point_at rect_bl
- act_test_movement_write_point_at rect_tl
- ret
-
- ; tests a single rectangle against an actor
- ; based on the current direction only 2 points are tested
- ; inputs:
- ; tmp_act_direction: direction
- ; tmp_rect: new collision rectangle
- ; tmp_rect_mask: mask to test agains
- ; tmp_rect_points: 2 rectangle points base don tmp_rect and the
- ; chosen direction
- ; hl: rectangle to test
- ; returns:
- ; a: 1 collision
- ; a: 0 no collision
-actor_test_rect:
-
- ; pre-filter rectangle mask
- ld a, [tmp_rect_mask]
- call rect_test_mask
- jp z, @no_collision
-
- ; test the first 2 points in tmp_rect_points
- ld de, tmp_rect_points
- actor_test_movement_corner @rect_collision
- actor_test_movement_corner @rect_collision
-
-@no_collision:
- xor a, a
- ret
-@rect_collision:
- ld a, 1
- ret
-
- ; calls collision resolution
- ; inputs:
- ; de: event origin actor
- ; hl: actor collided with
-actor_col_res:
- ld a, h
- or a, l
- cp a, 0
- ret z ; do nothing if 0
-
- ld a, [hl]
- add a, a ; type * 2 as offset into table
-
- push hl
-
- ld hl, actor_collision_res_table
- ld b, 0
- ld c, a
- add hl, bc ; hl = routine ptr
-
- ld a, [hl+]
- ld b, a
- ld a, [hl]
- ld h, a
- ld l, b
-
- pop bc ; the rotuine wants collider in bc
- call_hl
- ret
+ ; common routines for both player and enemy
-#define DBG_MARKER_TILE 0x80
-
-debug_routines:
- dw dbg_nop
- dw dbg_rect_draw
-debug_routines_end:
-
-dbg_nop:
- ret
-
- ; inits the debug interface
-dbg_init:
- ; set first rectangle to display
- ld a, rectangles LO
- ld [dbg_rect], a
- ld a, rectangles HI
- ld [dbg_rect+1], a
-
- ret
-
- ; updates debug options
- ; keys:
- ; down + select: cycle next debug function
-dbg_update:
- ld a, [dbg_delay]
- cp a, 0
- jp nz, @debug_delay
-
- ; check if dbg swap to next function is needed
- ld b, BTNDOWN | BTNSELECT
- input_held
- cp a, BTNDOWN | BTNSELECT
- jr nz, @no_switch REL
- ; set a timer
- ld a, 16
- ld [dbg_delay], a
-
- ; next function
- ld a, [dbg_fn]
- inc a
-
- ; loop around if > max functions
- cp a, (debug_routines_end - debug_routines) / 2
- jr nz, @no_reset REL
- xor a, a
-@no_reset:
- ld [dbg_fn], a
-@no_switch:
-
- ld a, [dbg_fn]
- add a, a ; * 2 for table offset
-
- ld hl, debug_routines
- ld d, 0
- ld e, a
- add hl, de
-
- ld a, [hl+]
- ld d, a
- ld a, [hl]
- ld h, a
- ld l, d
-
- jp hl
-@debug_delay:
-
- dec a
- ld [dbg_delay], a
- ret
-
- ; gets rectangle point and loads it into de
- ; preserves all other registers
- ; inputs:
- ; $1: rect_br/bl/tr/tl
- ; de: rectangle ptr
-#macro dbg_rect_draw_get
- push hl
- push de
-
- push de
- pop hl ; hl = rectangle
- call $1
- push de
- pop bc ; bc = point
-
- pop de
- pop hl
-#endmacro
-
- ; draws a marker obj at
- ; the edge of a rectangle
- ; keys:
- ; up + select: cycle to next debug rectangle
-dbg_rect_draw:
- ld b, BTNUP | BTNSELECT
- input_held
- cp a, BTNUP | BTNSELECT
- jp z, @rect_draw_cycle
-
- ; draw each corner of the rectangle if it is not 0
- ld a, [dbg_rect]
- ld e, a
- ld a, [dbg_rect+1]
- ld d, a
-
- call dbg_rect_draw_ptr
-
- ; always draw player rectangle
- ld de, actors+act_rect
- call dbg_rect_draw_ptr
-
- ret
-
-@rect_draw_cycle:
- ld a, [dbg_rect]
- ld l, a
- ld a, [dbg_rect+1]
- ld h, a
- ld de, r_size
- add hl, de
-
- ; next rectangle
- ld a, l
- ld [dbg_rect], a
- ld a, h
- ld [dbg_rect+1], a
-
- ld a, 16
- ld [dbg_delay], a
- ret
-
- ; draws a rectangle from ptr
- ; inputs:
- ; de: rectangle
-dbg_rect_draw_ptr:
-
- ; de = rectangle ptr
-
- ; if flags are 0 we exit
- ld a, [de]
- cp a, 0
- jp z, @rect_not_alloced
- push de
- ld a, 4
- call oamalloc
- pop de
-
- ; hl = oam
- dbg_rect_draw_get rect_tl
- ; bc = top left
-
- ; write top left y
- ld a, b
- add a, OBJ_OFF_Y
- ld [hl+], a
-
- ; write top left x
- ld a, c
- add a, OBJ_OFF_X
- ld [hl+], a
-
- ; write tile
- ld a, DBG_MARKER_TILE
- ld [hl+], a
-
- ; write flags
- xor a, a
- ld [hl+], a
-
-
- ; bottom left
- dbg_rect_draw_get rect_bl
-
- ; write bottom left y
- ld a, b
- add a, OBJ_OFF_Y
- ld [hl+], a
-
- ; write bottom left x
- ld a, c
- add a, OBJ_OFF_X
- ld [hl+], a
-
- ; write tile
- ld a, DBG_MARKER_TILE
- ld [hl+], a
-
- ; write flags
- xor a, a
- ld [hl+], a
-
-
-
- ; bottom right
- dbg_rect_draw_get rect_br
-
- ; write bottom right y
- ld a, b
- add a, OBJ_OFF_Y
- ld [hl+], a
-
- ; write bottomt right x
- ld a, c
- add a, OBJ_OFF_X
- ld [hl+], a
-
- ; write tile
- ld a, DBG_MARKER_TILE
- ld [hl+], a
-
- ; write flags
- xor a, a
- ld [hl+], a
-
-
- ; top right
- dbg_rect_draw_get rect_tr
-
- ld a, b
- add a, OBJ_OFF_Y
- ld [hl+], a
-
- ; write top left x
- ld a, c
- add a, OBJ_OFF_X
- ld [hl+], a
-
- ; write tile
- ld a, DBG_MARKER_TILE
- ld [hl+], a
-
- ; write flags
- xor a, a
- ld [hl+], a
-
-
- ret
-@rect_not_alloced:
- ret
#define WRAM 0xC000
#define WRAMLEN 0xFFF
-#define GAME_SPEED_DEFAULT GAME_SPEED_NORMAL
-
#define NULL 0
-#define ACTS_PLAYER_PROJECTILES 6
-#define ACTS_ENEMY 4
-#define ACTS_ENEMY_PROJECTILES 6
-#define ACTS_MAX (ACTS_PLAYER_PROJECTILES + ACTS_ENEMY + ACTS_ENEMY_PROJECTILES)
-
-#define HP_MAX 20
-
-#define MAP_OBJ_MAX 32
-#define RECT_MAX 6
-
-#define MAP_ROW_H 16 ; pixels per row
-
#define STACK_BEGIN 0xDFFF
; seed for the rng
#define UI_TILE_WIDTH 32
#define UI_TILE_HEIGHT 4
-
- ; rectangle collision flags
-.se 1
- ; collides with player
-.de RF_PLAYER, 1
- ; is a generic wall
-.de RF_WALL, 2
- ; collides with enemy
-.de RF_ENEMY, 4
-
- ; rectangle struct
-.se 0
- ; if flags == 0 the rectangle is free
-.de r_flags, 1
-.de r_pos_y, 1
-.de r_pos_x, 1
-.de r_h, 1
-.de r_w, 1
-.de r_size, 0
-
+#define MAP_W 16
+#define MAP_H 16
; actor type enum
.se 0
.de ACT_T_NULL, 1
-.de ACT_T_PLAYER, 1
-.de ACT_T_GUARD, 1
-.de ACT_T_DOG, 1
-.de ACT_T_HAZMAT, 1
-.de ACT_T_ARROW_BULLET, 1
; actor struct
; act_def
.se 0
.de act_type, 1
.de act_flags, 1
-
.de act_pos_y, 1 ; y/x position
.de act_pos_x, 1
-
; custom parameter
.de act_p0, 1
-
; state parameter
.de act_state, 1
-
- ; stats
-.de act_hp, 1
-.de act_ac, 1
-
- ; every actor has a collision rectangle
-.de act_rect, r_size
.de act_size, 0
; map header struct
.se 0
.de map_flags, 1
-.de map_init_pat, 2
- ; ptr to map objects list
-.de map_objs_ptr, 2
+ ; ptr to map routine
+.de map_routine, 2
; pointers to tile banks to be loaded
; maps to map property tile_bank0, tile_bank1, tile_bank2, tile_bank3
; note that tile_bank1 and tile_bank2 are 128 bytes each
.de map_tile_bank3_ptr, 2
.de map_header_size, 0
-#define DISTANCE_BGTA 1
-#define DISTANCE_AGTB 0
+ ; the map header is followed by MAP_W * MAP_H bytes
; alias for directions
.def int DIRUP = BTNUP
.def int DIRLEFT = BTNLEFT
.def int DIRRIGHT = BTNRIGHT
-
- ; row pattern struct
-.se 0
-.de pat_tilemap, 10
-.de pat_size, 0
-
- ; map object types
-.se 0
-.de MOT_NOP, 1
-.de MOT_SET_PAT, 1
-.de MOT_DISABLE_SCROLL, 1
-.de MOT_ENABLE_SCROLL, 1
-.de MOT_RECT, 1
-.de MOT_ACTOR_SPAWNER, 1
-.de MOT_SET_MAP_ROUTINE, 1
-.de MOT_RECT_CLEAR, 1
-
- ; map object struct
-.se 0
-.de mo_type, 1
-.de mo_flags, 1
-.de mo_row, 1
-.de mo_dat, 2
-.de mo_size, 0
-
-
; gameplay control flags
.se 1
-.de GPF_NO_SCROLL, 1
- ; scroll a row up next vblank
-.de GPF_SCROLL, 2
- ; player max hp is set to 1
-.de GPF_HARD_MODE, 4
- ; weapon type for player
-.se 0
-.de WT_ARROW, 1
-.de WT_RIFLE, 1
-#define GUARD_SPRITE_IDLE1 0x88
-
-act_enemy_guard:
- actdef ACT_T_GUARD, 0, 0, 10, 0
-
-#define ACT_GUARD_WALK_RIGHT 0
-#define ACT_GUARD_SHOOT 1
-#define ACT_GUARD_WALK_LEFT 2
-
-#define ACT_GUARD_MOVE_FRAMES 40
-
-act_guard_state_table:
- dw act_guard_walk_right
- dw act_guard_shoot
- dw act_guard_walk_left
-
- ; updates the guard enemy
- ; inputs:
- ; de: actor ptr
-act_guard_update:
- ; load position
- ld hl, act_pos_y
- add hl, de
- ld a, [hl+]
- ld b, a
- ld a, [hl+]
- ld c, a
-
- ; check if dead
- ld hl, act_hp
- add hl, de
- ld a, [hl]
- cp a, 0
- jp z, @despawn
-
- ; write collision shape
- ld hl, act_rect
- add hl, de
- ld a, RF_ENEMY
- call actor_write_default_collider
-
- ; call states
- ld hl, act_state
- add hl, de
- ld a, [hl]
- ld hl, act_guard_state_table
- jp call_tbl
-
- ret
-@despawn:
- call actor_despawn
- ret
-
- ; guard shoot state
- ; inputs:
- ; de: actor
-act_guard_shoot:
- ; TODO
- ld hl, act_state
- add hl, de
- ld a, ACT_GUARD_WALK_LEFT
- ld [hl], a
- ret
-
- ; actor walk left state
- ; inputs:
- ; de: actor ptr
- ; uses:
- ; p0 as timer
-act_guard_walk_left:
- ; walk
- ld hl, act_pos_x
- add hl, de
- ld a, [hl]
- dec a
- ld [hl], a
-
- ; process timer
- ld hl, act_p0
- add hl, de
- ld a, [hl]
- inc a
- ld [hl], a
- cp a, ACT_GUARD_MOVE_FRAMES
- jr nz, @not_done REL
- ; clear p0 and transition
- xor a, a
- ld [hl], a
-
- ld hl, act_state
- add hl, de
- ld a, ACT_GUARD_WALK_RIGHT
- ld [hl], a
-@not_done:
- ret
-
- ; walk right state
- ; inputs:
- ; de: actor ptr
- ; uses:
- ; p0 as timer
-act_guard_walk_right:
- ; walk
- ld hl, act_pos_x
- add hl, de
- ld a, [hl]
- inc a
- ld [hl], a
-
- ; process timer
- ld hl, act_p0
- add hl, de
- ld a, [hl]
- inc a
- ld [hl], a
- cp a, ACT_GUARD_MOVE_FRAMES ; how many frames do we walk?
- jr nz, @not_done REL
- ; clear p0 and transition
- xor a, a
- ld [hl], a
-
- ld hl, act_state
- add hl, de
- ld a, ACT_GUARD_SHOOT
- ld [hl], a
-
-@not_done:
+ ; inits enemy
+enemy_init:
ret
- ; draws the guard enemy
- ; inputs:
- ; de: actor ptr
-act_guard_draw:
- push de
- ld a, 2
- call oamalloc
- pop de
-
- push de
- ld b, GUARD_SPRITE_IDLE1
- ld c, 0
- ld a, 0
- call actor_draw
- pop de
-
- ld b, GUARD_SPRITE_IDLE1+2
- ld c, 0
- ld a, 8
- call actor_draw
- ret
-
- ; combination of update and draw call
-act_guard_update_and_draw:
- push de
- call act_guard_draw
- pop de
- jp act_guard_update
-
- ; collision resolution between guard and another actor
- ; inputs:
- ; de: event origin actor
- ; bc: guard
-act_guard_col_res:
- ld a, [de]
- cp a, ACT_T_ARROW_BULLET
- jr z, @arrow_bullet REL
-
- ret
-@arrow_bullet:
- ld hl, act_hp
- add hl, bc
-
- ld a, [hl]
- dec a
- ld [hl], a
- ret
-
- ; inits a guard
- ; inputs:
- ; de: actor
-act_guard_init:
- ld hl, act_hp
- add hl, de
- ld a, 4
- ld [hl], a
-
- xor a, a
- ld hl, act_state
- add hl, de
- ld [hl], a
-
- ld hl, act_p0
- add hl, de
- ld [hl], a
-
+ ; updates enemy AI
+enemy_update:
ret
; followed by a 16x16 tilemap
l1:
+ mapdef 0, map_r_nop, bank8000, bank8800, bank8C00, bank9000
; defines a map header
; inputs:
; $1: flags
- ; $2: initial pattern
- ; $3: objs ptr
- ; $4: tile bank 0
- ; $5: tile bank 1
- ; $6: tile bank 2
- ; $7: tile bank 3
+ ; $2: map routine
+ ; $3: tile bank 0
+ ; $4: tile bank 1
+ ; $5: tile bank 2
+ ; $6: tile bank 3
#macro mapdef
.db $1
dw $2
dw $4
dw $5
dw $6
- dw $7
#endmacro
; defines a new actor
.db 0, 0, 0, 0, 0
#endmacro
- ; defines a map object
- ; inputs:
- ; $1: type
- ; $2: flags
- ; $3: row
- ; $4: dat1/dat2 (word)
-#macro modef
- .db $1, $2, $3
- dw $4
-#endmacro
-
- ; same as modef
- ; but
- ; $4: dat1
- ; $5: dat2
-#macro modef2
- .db $1, $2, $3, $4, $5
-#endmacro
-
-
; defines a rectangle
; inputs:
; $1: collision flags
#include "ui.s"
#include "audio.s"
#include "map.s"
-#include "mapobj.s"
-#include "rowpatterns.s"
#include "math.s"
#include "game.s"
#include "actor.s"
-#include "rectangle.s"
-#include "projectile.s"
#include "mainmenu.s"
#include "tiles.s"
#include "levels.s"
; fill bank
.fill 0xFF, 0x7FFF - $
-
-
- ; map routine that loads l1
-map_r_load_l1:
-
- ld a, [player+act_pos_y]
- cp a, 5
- ret nc
-
- ; if the player is on the right side of the map
- ; load hard mode
- ld a, [player+act_pos_x]
- cp a, 0x5F
- jr c, @no_hard REL
- ld a, [game_flags]
- or a, GPF_HARD_MODE
- ld [game_flags], a
-@no_hard:
-
- call player_init
-
- ld de, l1_map
- call map_load
- ret
-
-l_main_menu:
- mapdef 0, pat_empty, l_main_menu_objs, bank8000, bank8800, bank8C00, bank9000
-
-l_main_menu_objs:
- modef MOT_SET_MAP_ROUTINE, 0, 0, map_r_load_l1
- modef MOT_DISABLE_SCROLL, 0, 0, 0
-
- modef MOT_SET_PAT, 0, 5, pat_easy_hard_mode
- modef MOT_SET_PAT, 0, 6, pat_center_wall
- modef MOT_NOP, 0, 0xFF, 0
#define SCRN0_END 0x9BF3
-
- ; clears the map routine to nop
-map_clear_routine:
- ld a, map_r_nop LO
- ld [map_routine], a
- ld a, map_r_nop HI
- ld [map_routine+1], a
- ret
; loads a map
; including the required tileset
; inputs:
; de: map ptr
map_load:
- ; clear row
- xor a, a
- ld [map_curr_row], a
-
- ; enable scroll
- call mo_enable_scroll
-
- ; clear map routine
- call map_clear_routine
-
call disableinterrupts
call next_vblank_wait
call lcd_off
call map_tile_banks_load
pop de
- ; load initial pattern
- ld hl, map_init_pat
- add hl, de
- ld a, [hl+]
- ld [map_curr_pat], a
- ld a, [hl]
- ld [map_curr_pat+1], a
-
- ; reset bg ptr
- ld a, SCRN0_END LO
- ld [map_scrn_ptr], a
- ld a, SCRN0_END HI
- ld [map_scrn_ptr+1], a
-
- ; load mo ptr
- ld hl, map_objs_ptr
- add hl, de
- ld a, [hl+]
- ld [map_curr_obj], a
- ld a, [hl+]
- ld [map_curr_obj+1], a
-
-
call lcd_on
call vblank_wait
call enableinterrupts
- call map_page_full_draw
+ call map_full_draw
call player_init
call ui_init
ld [de], a
dec de
#endmacro
-
- ; draws the current row pattern of 16x16 tiles
- ; only call during blank
- ; also triggers objects
-map_advance_row:
- ld a, [map_scrn_ptr]
- ld e, a
- ld a, [map_scrn_ptr+1]
- ld d, a
-
- ld a, [map_curr_pat]
- ld l, a
- ld a, [map_curr_pat+1]
- ld h, a
-
- push hl
-
- ; first row of tiles
-
- map_advance_row_draw_bot
- map_advance_row_draw_bot
- map_advance_row_draw_bot
- map_advance_row_draw_bot
- map_advance_row_draw_bot
- map_advance_row_draw_bot
- map_advance_row_draw_bot
- map_advance_row_draw_bot
- map_advance_row_draw_bot
- map_advance_row_draw_bot
-
- ; move to next row
- ld hl, -12 & 0xFFFF
- add hl, de
- push hl
- pop de ; de = next row
-
- pop hl
- ; second row of tiles
- map_advance_row_draw_top
- map_advance_row_draw_top
- map_advance_row_draw_top
- map_advance_row_draw_top
- map_advance_row_draw_top
- map_advance_row_draw_top
- map_advance_row_draw_top
- map_advance_row_draw_top
- map_advance_row_draw_top
- map_advance_row_draw_top
-
- ; write new scrn ptr
- ld hl, -12 & 0xFFFF
- add hl, de
- ; hl = new scrn ptr
- ld a, l
- ld [map_scrn_ptr], a
- ld a, h
- ld [map_scrn_ptr+1], a
-
- ; check if we reached the last row of scrn0
- cp a, 0x97
- jr nz, @not_end_of_scrn0 REL
- ; end of scrn0 - wrap around back to first row
- ld a, SCRN0_END LO
- ld [map_scrn_ptr], a
- ld a, SCRN0_END HI
- ld [map_scrn_ptr+1], a
-@not_end_of_scrn0:
-
- ; move to next row
- ld a, [map_curr_row]
- inc a
- ld [map_curr_row], a
-
- ret
; draws a full page of the currently selected map
; waits for blank between each row
; inputs:
; [map]
-map_page_full_draw:
- call mo_exec
- call next_vblank_wait
- call map_advance_row
-
- call mo_exec
- call next_vblank_wait
- call map_advance_row
-
- call mo_exec
- call next_vblank_wait
- call map_advance_row
-
- call mo_exec
- call next_vblank_wait
- call map_advance_row
-
- call mo_exec
- call next_vblank_wait
- call map_advance_row
-
- call mo_exec
- call next_vblank_wait
- call map_advance_row
-
- call mo_exec
- call next_vblank_wait
- call map_advance_row
-
- ; last row will be out of visible scroll
- call mo_exec
- call next_vblank_wait
- call map_advance_row
+map_full_draw:
ret
; nop map rotuine
map_r_nop:
ret
- ; checks if all enemies are defeated
- ; and re-enables scroll
-map_r_enable_scroll_all_enemies_defeated:
- ; don't run check loop if the anmation is already going
- ld a, [map_anim_timer]
- cp a, 0
- jr nz, @no_check_loop REL
-
- ; check if all enemies are NULL actors
- ld hl, actors_enemy
- ld de, act_size
- ld b, ACTS_ENEMY
-
- xor a, a
-@check_loop:
- ; or type into a
- or a, [hl]
- ret nz ; one type is not 0
- add hl, de ; next actor
- dec b
- jr nz, @check_loop REL
-
-@no_check_loop:
-
- ld a, [map_anim_timer]
- cp a, 0
- jp z, @no_animation
- call map_arrow_indicator
-
- ld a, [map_anim_timer]
- cp a, 0
- ret nz ; if timer is not 0 do not set scroll
-
- ; re-enable scroll
- ld a, [game_flags]
- and a, ~GPF_NO_SCROLL & 0xFF
- ld [game_flags], a
- call map_clear_routine
- ret
-
-@no_animation:
- ; set animation
- ld a, 120
- ld [map_anim_timer], a
-
- xor a, a
- ld [map_anim_state], a
-
- ld a, 80 ; x position
- ld [map_anim_p0], a
- ret
-
-#define MAP_TILE_ARROW 0x84
-
- ; plays an arrow indicator animation
- ; flashng it every 32 frames
- ; uses timer for duration
- ; map anim state for state counter
- ; anim p0 for x position
-map_arrow_indicator:
- ld a, [map_anim_timer]
- dec a
- ld [map_anim_timer], a
- jp z, @end_anim
-
- ; check state
- ld a, [map_anim_state]
- inc a
- cp a, 32
- jr nz, @no_x_swap REL
- ld a, [map_anim_p0]
- xor a, 80
- ld [map_anim_p0], a
- xor a, a ; clear state
-@no_x_swap:
- ld [map_anim_state], a
-
- ld a, 1
- call oamalloc
-
- ld a, 16
- ; y
- ld [hl+], a
-
- ; x
- ld a, [map_anim_p0]
- ld [hl+], a
-
- ; tile
- ld a, MAP_TILE_ARROW
- ld [hl+], a
-
- ; flags
- xor a, a
- ld [hl+], a
-
- ret
-@end_anim:
- call map_clear_routine
- ret
-
-l1_map:
- mapdef 0, pat_empty, l1_objs, bank8000, bank8800, bank8C00, bank9000
-
-l1_objs:
- ; show GO arrow at the start
- modef MOT_SET_MAP_ROUTINE, 0, 0, map_r_enable_scroll_all_enemies_defeated
-
- modef MOT_SET_PAT, 0, 8, pat_center_empty_wall
- modef2 MOT_ACTOR_SPAWNER, 0, 8, ACT_T_GUARD, 0x40
- ; rectangle at y/x 0/0 with height 32 width 64
- modef MOT_RECT, 0, 8, 0x0703
-
-
- ; rectangle at y/x 0/0 with height 32 width 48
- modef MOT_RECT, 0, 8, 0xE503
-
- modef MOT_SET_PAT, 0, 10, pat_center_grass
- modef2 MOT_ACTOR_SPAWNER, 0, 11, ACT_T_GUARD, 0x80
- modef MOT_SET_PAT, 0, 18, pat_left_wall
-
- modef MOT_RECT_CLEAR, 0, 18, 0
- modef2 MOT_RECT, 0, 18, 0x0F, 0x07
- modef2 MOT_ACTOR_SPAWNER, 0, 18, ACT_T_GUARD, 0x40
-
- modef MOT_SET_PAT, 0, 0x1A, pat_empty
- modef2 MOT_ACTOR_SPAWNER, 0, 0x1F, ACT_T_GUARD, 0x40
- modef MOT_DISABLE_SCROLL, 0, 0x20, 0
- modef MOT_SET_MAP_ROUTINE, 0, 0x20, map_r_enable_scroll_all_enemies_defeated
-
- modef MOT_RECT_CLEAR, 0, 0x21, 0
- modef2 MOT_RECT, 0, 0x21, 0x0F, 0xE7
- modef MOT_SET_PAT, 0, 0x21, pat_right_wall
-
- modef2 MOT_RECT, 0, 0x29, 0x09, 0x07
- modef MOT_SET_PAT, 0, 0x29, pat_left_wall
-
- modef MOT_SET_PAT, 0, 0x2E, pat_empty
- modef MOT_DISABLE_SCROLL, 0, 0x30, 0
- modef MOT_NOP, 0, 0xFF, 0
+++ /dev/null
-; map objects are a list of objects
-; a map has a static list of loadable objects
-; when a row is being drawn the next object
-; is checked
-; if the row matches it is loaded into ram and executed
-; when an object is off-screen it is unloded
-
-mo_routines:
- dw mo_nop
- dw mo_set_pat
- dw mo_disable_scroll
- dw mo_enable_scroll
- dw mo_rect
- dw mo_actor_spawner
- dw mo_set_map_routine
- dw mo_rect_clear
-
-mo_nop:
- ret
-
- ; executes all map objects in the map object list
- ; until the mo's row != current row
- ; advances mo ptr
-mo_exec:
- ld a, [map_curr_obj]
- ld e, a
- ld a, [map_curr_obj+1]
- ld d, a
-
- ld a, [map_curr_row]
- ld b, a ; b = current row
-@loop:
- ; execute map object by type
- ld hl, mo_row
- add hl, de
- ld a, [hl]
- ; if rows do not match exit
- cp a, b
- jr nz, @done REL
-
- ; otherwise call object routine
- push_all
- ld a, [de] ; type
- add a, a ; * 2 for offset
- ld hl, mo_routines
- ld b, 0
- ld c, a
- add hl, bc
-
- ; hl = rotuine ptr
- ld a, [hl+]
- ld b, a
- ld a, [hl+]
- ld h, a
- ld l, b
-
- ; call routine
- call_hl
-
-
- pop_all
- ; go to next object
- ld hl, mo_size
- add hl, de
- push hl
- pop de ; de = next obj
- jr @loop REL
-
-@done:
-
- ; write back next ptr
- ld a, e
- ld [map_curr_obj], a
- ld a, d
- ld [map_curr_obj+1], a
-
- ret
-
- ; sets the next draw pattern
- ; inputs:
- ; de: map object ptr
-mo_set_pat:
- ; new pattern is in dat1 and dat2
- ld hl, mo_dat
- add hl, de
-
- ld a, [hl+]
- ld [map_curr_pat], a
- ld a, [hl]
- ld [map_curr_pat+1], a
- ret
-
- ; disables map scrolling
- ; inputs:
- ; de: map object ptr
-mo_disable_scroll:
- ld a, [game_flags]
- or a, GPF_NO_SCROLL
- ld [game_flags], a
- ret
-
- ; enables map scrolling
- ; inputs:
- ; de: map object ptr
-mo_enable_scroll:
- ld a, [game_flags]
- and a, ~GPF_NO_SCROLL & 0xFF
- ld [game_flags], a
- ret
-
- ; spawns a collision rectangle
- ; the origin of a rectangle is the bottom left
- ; dat1: nnnn0000: y offset from row in 8 pixel increments
- ; 0000nnnn: height in 8 pixel increments (+1 height is implicitly added)
- ; dat2: nnnn0000: x offset from row in 8 pixel increments
- ; 0000nnnn: width in 8 pixel increments (+1 widt is implicitly added)
- ; inputs:
- ; de: map object ptr
-mo_rect:
- ld hl, mo_dat
- add hl, de
- ; hl = dat1
-
- ; current row always has y-position 0
- ; (actually -16 so the next adjust will not move the rectanglet to the wrong spot)
- ; for the bottom left corner
-
- ; calculate y position
- ld a, [hl]
- and a, 0xF0
- swap a
- mul8 a
- ; a = y offset
- ld b, -16 & 0xFF
- add a, b
- ld b, a ; b = y position
-
- ; calculate height
- ld a, [hl+] ; hl = dat2
- and a, 0x0F
- inc a
- mul8 a
- ; d = height
- ld d, a
-
- ; calculate x position
- ld a, [hl]
- and a, 0xF0
- swap a
- mul8 a
- ; c = x offset
- ld c, a
-
- ; calculate width
- ld a, [hl]
- and a, 0x0F
- inc a
- mul8 a
- ; e = width
- ld e, a
-
- ; flag hard-coded to WALL
- ld a, RF_WALL
-
- call rect_try_add
-
- ret
-
- ; spawns an actor spawner
- ; dat1: actor type
- ; dat2 nnnn0000: x offset to spawn at / 8
- ; inputs:
- ; de: map object ptr
-mo_actor_spawner:
- ld hl, mo_dat
- add hl, de
-
- push hl
- call actor_try_add_enemy
- pop de
- ld a, h
- or a, l
- ret z ; bail if hl is NULL
-
- ; we have a ptr!
- ld a, [de]
- inc de ; go to dat2
-
- push hl
- ; set type
- ld [hl+], a
-
- ; clear flags
- xor a, a
- ld [hl+], a
-
- ; y position is always the same
- ld a, -16 & 0xFF
- ld [hl+], a
-
- ; TODO:
- ; x position is based on dat2
- ld a, [de]
- and a, 0xF0
- swap a
- add a, a ; * 2
- add a, a ; * 4
- add a, a ; * 8
- ld [hl+], a
-
- ; clear p0 and state
- xor a, a
- ld [hl+], a
- ld [hl+], a
-
- ; TODO: run some init code based on type
- pop de
- ; de = actor base
- call actor_init
-
- ret
-
- ; sets the current map routine
- ; dat1/2: map routine ptw
- ; inputs:
- ; de: map object ptr
-mo_set_map_routine:
- ld hl, mo_dat
- add hl, de
- ld a, [hl+]
- ld [map_routine], a
- ld a, [hl+]
- ld [map_routine+1], a
- ret
-
- ; clears all map rectangles
- ; use this to despawn current collision
- ; shapes
-mo_rect_clear:
- ld hl, rectangles
- ld de, r_size
- ld b, RECT_MAX
- xor a, a
-@loop:
- ld [hl], a
- add hl, de
- dec b
- jr nz, @loop REL
-
-
- ret
-
- ; calculates the distance between 2
- ; numbers:
- ; inputs:
- ; a: n1
- ; b: n2
- ; returns:
- ; a: absolute distance between a and b
- ; c: 1 if b > a
- ; c: 0 otherwise
-distance:
- cp a, b
- ; if b > a jp
- jr c, @b_gt_a REL
-
- ; otherwise a simple sub will do
- sub a, b
- ld c, DISTANCE_AGTB
- ret
-@b_gt_a:
- ; exchange a and b and sub
- ld c, a
- ld a, b
- ld b, c
- sub a, b
- ld c, DISTANCE_BGTA
- ret
ld hl, new_game
call game_set_state
- call dbg_init
-
ret
; copies memory from one location to another
-#define PLAYER_SPEED 0xE0
-#define ARROW_TILE 0x86
-#define PLAYER_WALKING_TILE 0x8E
-
- ; inits hard mode
-player_init_hard_mode:
- ld a, 1
- ld [player_hp_max], a
- ret
; sets up the player actor
player_init:
- ; initial position
- ld a, 0x60
- ld [player+act_pos_y], a
- ld a, 0x40
- ld [player+act_pos_x], a
-
- ld a, 0x30 ; initial next scroll
- ld [player_next_scroll_y], a
-
- ld a, [player_hp_max]
- ld [player+act_hp], a
-
-
- ld a, [game_flags]
- and a, GPF_HARD_MODE
- call nz, player_init_hard_mode
ret
; updates the special player actor
player_update:
-
- ld b, BTNUP
- input_held
- jr z, @not_up REL
- ld b, PLAYER_SPEED
- ld c, 0
- call player_stage_move_n
-
- ld a, DIRUP
- call player_try_move
-
- ; only scroll if player actually moved
- cp a, 0
- jr nz, @not_up REL
- call player_try_scroll_up
-@not_up:
-
- ld b, BTNDOWN
- input_held
- jr z, @not_down REL
- ld b, PLAYER_SPEED
- ld c, 0
- call player_stage_move_p
-
- ld a, DIRDOWN
- call player_try_move
-
-@not_down:
-
-@left_right:
-
- ld b, BTNLEFT
- input_held
- jr z, @not_left REL
- ld b, 0
- ld c, PLAYER_SPEED
- call player_stage_move_n
-
- ld a, DIRLEFT
- call player_try_move
-@not_left:
-
- ld b, BTNRIGHT
- input_held
- jr z, @not_right REL
-
- ld b, 0
- ld c, PLAYER_SPEED
- call player_stage_move_p
-
- ld a, DIRRIGHT
- call player_try_move
-@not_right:
-
- ld b, BTNA
- input_held
- jr z, @not_a REL
- call player_shoot
-@not_a:
-
-@direction_done:
- ; write collider for this frame
- ld a, [player+act_pos_y]
- ld b, a
- ld a, [player+act_pos_x]
- ld c, a
- ld a, RF_PLAYER
- ld hl, player+act_rect
- call actor_write_default_collider
-
-
- ret
-
-#define PLAYER_SPRITE_IDLE1 0x8D
-
- ; sets scroll unless scroll is diabled
-player_set_scroll:
- ; scroll can still be allowed to complete the next tile
- ; even if the scroll disable object is loaded
- ld a, [scroll_timer]
- cp a, MAP_ROW_H-1
- jr z, @scroll_anyway REL
-
- ld a, [game_flags]
- and a, GPF_NO_SCROLL
- ret nz
-
-@scroll_anyway:
- ld a, [game_flags]
- or a, GPF_SCROLL
- ld [game_flags], a
- ret
-
- ; if the player's real y position is over
- ; a certain value we should scroll the map
- ; if scrolling is allowed
-player_try_scroll_up:
- ld a, [player_next_scroll_y]
- ld b, a
- ld a, [player+act_pos_y]
- cp a, b
- jp nc, @no_scroll
-
- call player_set_scroll
-
- ; run next objects
- ; so they are loaded before a move is attempted
- call mo_exec
-
-
-@no_scroll:
- ret
-
- ; stages a move in a positive directon
- ; inputs:
- ; b/c: y/x movement
- ; returns:
- ; b/c: new y/x position
-player_stage_move_p:
- ld a, [player_sub_pixel_y]
- add a, b
- ld [player_sub_pixel_y], a
- ld a, [player+act_pos_y]
- adc a, 0
-
- jr nc, @no_overflow_y REL
- ld a, 0xFF
-@no_overflow_y:
-
- ; bottom of screen
- cp a, 0x60
- jr c, @no_edge_y REL
- ld a, 0x60
-@no_edge_y:
-
- ld b, a
-
- ld a, [player_sub_pixel_x]
- add a, c
- ld [player_sub_pixel_x], a
- ld a, [player+act_pos_x]
- adc a, 0
-
-jr nc, @no_overflow_x REL
- ld a, 0xFF
-@no_overflow_x:
-
- ; right side of screen
- cp a, 0x90
- jr c, @no_edge_x REL
- ld a, 0x90
-@no_edge_x:
-
- ld c, a
-
- ret
-
- ; stages a move in a negative directon
- ; inputs:
- ; b/c: y/x movement
- ; returns:
- ; b/c: new y/x position
-player_stage_move_n:
- ld a, [player_sub_pixel_y]
- sub a, b
- ld [player_sub_pixel_y], a
- ld a, [player+act_pos_y]
- sbc a, 0
-
- jr nc, @no_underflow_y REL
- xor a, a
-@no_underflow_y:
-
- ld b, a
-
- ld a, [player_sub_pixel_x]
- sub a, c
- ld [player_sub_pixel_x], a
- ld a, [player+act_pos_x]
- sbc a, 0
-
- jr nc, @no_underflow_x REL
- xor a, a
-@no_underflow_x:
-
- ld c, a
-
ret
- ; moves the player
- ; performs a collision check
- ; moves scroll and performs map loads if needed
- ; inputs:
- ; a: direction
- ; b/c: new y/x position
- ; returns:
- ; a: 1 == collision
- ; a: 0 no collision
-player_try_move:
- ; store direction for now
- ld [tmp_act_direction], a
-
- ; write temporary collider
- push bc
- ld a, RF_PLAYER
- ld hl, tmp_rect
- call actor_write_default_collider
-
- ; test collision agains walls and enemies
- ld a, [tmp_act_direction]
- ld b, a ; b = direction
- ld a, RF_WALL | RF_ENEMY
- ld de, player
- ld hl, actors_enemy
- ld c, ACTS_ENEMY
- call actor_test_movement
- pop bc
-
- ; hl should be NULL or an actor ptr
- push af
- call actor_col_res
- pop af
-
- cp a, 0
- ret nz
-
- ld a, b
- ld [player+act_pos_y], a
-
- ld a, c
- ld [player+act_pos_x], a
-
- xor a, a
- ret
-
- ; draws the special player actor
+ ; draws player at current location
player_draw:
- ld a, 2
- call oamalloc
-
- ; was any direction pressed this frame?
- ld a, [curr_inputs]
- and a, BTNUP | BTNDOWN | BTNLEFT | BTNRIGHT
- jp nz, @walking
-
- ; idle sprite
- ld de, player
- ld b, PLAYER_SPRITE_IDLE1
- ld c, 0
- ld a, 3
- call actor_draw
-
- jp @weapon
-@walking:
-
- ld de, player
- ld b, PLAYER_WALKING_TILE
-
- ld a, [animation_frame]
- cp a, 1
- jr z, @flip REL
-
- ld c, 0
- ld a, 3
- jr @no_flip REL
-
-@flip:
- ld c, OAM_FXFLIP
- ld a, 4
-@no_flip:
-
- call actor_draw
-
-@weapon:
- ; TODO: draw based on player weapon type
- ld de, player
- ld b, ARROW_TILE
- ld c, 0
- ld a, 0x46
- call actor_draw
- ret
-
-
-#define PLAYER_SHOOT_DELAY 32
- ; shoots the player's current weapon
-player_shoot:
- ld a, [player_shoot_delay]
- cp a, 0
- jp nz, @tick
-
- ; TODO: check player curr weapon
-
- call actor_try_add_player_projectile
- ld a, h
- or a, l
- ret z
-
- call act_spawn_projectile_arrow_player
-
- ld a, PLAYER_SHOOT_DELAY
- ld [player_shoot_delay], a
-
- ret
-@tick:
- dec a
- ld [player_shoot_delay], a
- ret
-
- ; combination of update and draw call
-player_update_and_draw:
- call player_draw
- jp player_update
-
- ; collision resolution between player
- ; and other actor
- ; inputs:
- ; de: origin actor
- ; bc: player
-player_col_res:
ret
+++ /dev/null
-
-#define ARROW_SPRITE 0x82
-
-; projectile flags
-.se 1
-.de PROJECTILE_F_PLAYER, 1
-
- ; spawns a arrow projectile for player
- ; inputs:
- ; hl: actor ptr
-act_spawn_projectile_arrow_player:
- ld a, ACT_T_ARROW_BULLET
- ld [hl], a ; write type
-
- push hl
- ld de, act_pos_y
- add hl, de
- ; hl = new pos y
-
-
- ld a, [player+act_pos_y]
- ld [hl+], a
-
- ld a, [player+act_pos_x]
- add a, 4
- ld [hl+], a
- pop hl
-
- ; set to player type
- ld de, act_p0
- add hl, de
- ld a, PROJECTILE_F_PLAYER
- ld [hl], a
-
- call play_attack_noise
-
- ret
-
- ; updates a simple arrow projectile
- ; inputs:
- ; de: actor ptr
-act_projectile_arrow_update:
- ; is this a player actor?
- ld hl, act_p0
- add hl, de
- ld a, [hl] ; load projectile p0 flags
- and a, PROJECTILE_F_PLAYER
- jp nz, @player
-
- ; TODO:
- ; enemy projectile logic
-
- ret
-@player:
- ld hl, act_pos_y
- add hl, de
-
- ld a, [hl]
- dec a
- cp a, 0xFF
- jp z, @despawn
- ld [hl+], a
-
- ; a = y pos
- ld b, a
- ld a, [hl]
- ld c, a ; c = x pos
-
- push de
-
- ld a, RF_PLAYER
- ld hl, tmp_rect
- call actor_projectile_write_default_collider
-
- ; check enemy collision
- pop de
- push de
-
- ld b, DIRUP
- ld a, RF_ENEMY | RF_WALL
- ld hl, actors_enemy
- ld c, ACTS_ENEMY
- call actor_test_movement
-
- pop de
- cp a, 1
- jr z, @collided REL
-
- ret
-
-@collided:
- push de
- call actor_col_res
- pop de
- call play_hit_noise
-@despawn:
- jp actor_despawn
-
-
- ; draws a arrow projectile
- ; inputs:
- ; de: actor
-act_projectile_arrow_draw:
- push de
- ld a, 1
- call oamalloc
- pop de
-
- ld b, ARROW_SPRITE
- ld c, 0
- ld a, 0
- jp actor_draw
-
- ; updates and draws projectile
- ; for pistosl
-act_projectile_arrow_update_and_draw:
- push de
- call act_projectile_arrow_update
- pop de
- jp act_projectile_arrow_draw
-
- ; collision resolution for arrow projectile
- ; inputs:
- ; de: event origin actor
- ; bc: projectile
-act_projectile_arrow_col_res:
- ret
-
-
- ; writes actor default projectile collider
- ; based on an input position
- ; writes the collider to dst
- ; e.g. use with actor's real rectangle
- ; if real collision should be written
- ; use with tmp_rect for collision tests before moving
- ; inputs:
- ; a: collision mask
- ; b/c: y/x position of actor
- ; hl: destination rectangle
-actor_projectile_write_default_collider:
- ld [hl+], a ; mask
- ld a, b
- add a, 4
- ld [hl+], a ; y pos
-
- ld a, c
- add a, 2
- ld [hl+], a ; x pos
-
- ld a, 4 ; width/height
- ld [hl+], a ; height
-
- ld [hl+], a ; width
- ret
+++ /dev/null
- ; limitations:
- ; positions can never wrap around a page
- ; in this engine
- ; all positions are 8-bit only
- ; great care must be taken to avoid
- ; collisions from overlapping a page swap!
-
-
- ; attempts to add a rectangle
- ; to the rectangle buffer
- ; if no slots are availabel it will simply be skipped
- ; this function always sets y_hi to 0
- ; inputs:
- ; a: flags
- ; b/c: y/x position
- ; d/e: h/w
-rect_try_add:
- push af
- push bc
- push de
- call rect_find_slot
- ld a, h
- or a, l
- cp a, 0
- ; if hl == 00 exit
- jp z, @exit
-
- pop de
- pop bc
- pop af
-
- ; write flags
- ld [hl+], a
-
- ; write y
- ld a, b
- ld [hl+], a
-
- ; wirte x
- ld a, c
- ld [hl+], a
-
- ; write h
- ld a, d
- ld [hl+], a
-
- ; write w
- ld a, e
- ld [hl], a
-
- ret
-@exit:
- pop de
- pop bc
- pop af
- ret
-
- ; attempts to find a rectangle slot where
- ; flags == 0 (unused)
- ; returns:
- ; hl: 0000 -> no slot
- ; hl: ptr -> slot
-rect_find_slot:
- ld hl, rectangles
- ld de, r_size
- ld b, RECT_MAX
-
-@loop:
- ; hl = flags
- ld a, [hl]
- ; if flags is 0 we ret
- cp a, 0
- ret z
-
- add hl, de
- dec b
- jr nz, @loop REL
-
- ; if nothing was found ld NULL
- ld hl, 0
- ret
-
-
- ; gets top left of a rectangle
- ; inputs:
- ; hl: rectangle
- ; returns:
- ; de: y/x
-rect_tl:
- inc hl ; skip mask
- ld a, [hl+]
- ld d, a ; d = y
- ld a, [hl+]
- ld e, a ; e = x
-
- ld a, [hl]
- ld l, a
- ld a, d
- sub a, l ; y-height
- capunderflow
- ld d, a ; d = top left y
- ret
-
- ; same as rect_tl
- ; bototm left
-rect_bl:
- inc hl ; skip mask
- ld a, [hl+]
- ld d, a ; d = y
- ld a, [hl]
- ld e, a ; e = x
- ret
-
- ; same as rect_tl
- ; top right
-rect_tr:
- inc hl ; skip mask
- ld a, [hl+]
- ld d, a ; d = y
- ld a, [hl+]
- ld e, a ; e = x
- inc hl ; skip height
-
- ld a, [hl]
- add a, e ; e = x + width
- capoverflow
- ld e, a
-
- dec hl ; go back to height
- ld a, [hl]
- ld l, a
- ld a, d
- sub a, l ; y-height
- capunderflow
- ld d, a ; d = top right y
- ret
-
- ; same as rect_tl
- ; bottom right
-rect_br:
- inc hl ; skip mask
- ld a, [hl+]
- ld d, a ; d = y
- ld a, [hl+]
- ld e, a ; e = x
- inc hl ; skip height
-
- ld a, [hl]
- add a, e ; e = x + width
- capoverflow
- ld e, a
- ret
-
- ; tests if a point is inside r1
- ; does not perform collision mask test
- ; bc: y/x point
- ; hl: rectangle
- ; returns:
- ; a == 1: inside
- ; a == 0: not inside
- ; hl: byte after original rectangle
-rect_point_test:
- inc hl ; skip mask
-
- ; bottom left
-
- ; load bottom left point
- ld a, [hl+]
- ld d, a ; d = y
- ld a, [hl+]
- ld e, a ; e = x
-
- ld a, b ; compare y
- cp a, d
- jr nc, @no_collision REL
- ld a, c ; compare x
- cp a, e
- jr c, @no_collision REL
-
-
- ; top left
-
- ; get top left y point
- ld a, [hl+]
- push hl
- ld l, a ; TODO: remove use of l to avoid push
- ld a, d
- sub a, l ; y-height
- capunderflow
- ld d, a ; d = top left y
- pop hl
-
- ; x was already tested in bottom left
-
- ld a, b ; compare y points
- cp a, d
- jr c, @no_collision REL
-
-
- ; top right
-
- ; get top right x
- ld a, [hl+]
- add a, e ; e = x + width
- capoverflow
- ld e, a
-
- ; top right has unique x and y positions
-
- ld a, b ; compare y
- cp a, d
- jr c, @no_collision REL
- ld a, c ; compare x position
- cp a, e
- jr nc, @no_collision REL
-
-
- ; bottom right
-
- ; original y position was already tested in bottom left
- ; x positions was already tested in top right
-
- ; all other missing x/y checks have
- ; already been performed in previous points
- ; e.g. original y is tested in bottom left
-
-@collision:
- ld a, 1
- ret
-@no_collision:
- xor a, a ; no collision
- ret
-
- ; tests rectangle mask
- ; inputs:
- ; a: mask
- ; hl: rectangle
- ; returns:
- ; z-flag: != 0 if mask is ok
- ; uses: a, c
-rect_test_mask:
- ld c, a
- ld a, [hl]
- and a, c
- ret
-
- ; removes a rectangle
- ; inputs:
- ; de: rectangle
-rect_despawn:
- xor a, a
- ld [de], a
- ret
+++ /dev/null
-
- ; tile space
-.def int TS = 0x20
-
- ; tile grass
-.def int GS = 0x62
-
- ; generic wall
-.def int WG = 0x40
-
- ; tile HARD MODE / Skull
-.def int SK = 0x0A
-
-pat_empty:
-.db TS, TS, TS, TS, TS, TS, TS, TS, TS, TS
-
-pat_center_grass:
-.db TS, TS, TS, GS, GS, GS, TS, TS, TS, TS
-
-pat_center_empty_wall:
-.db WG, WG, WG, TS, TS, TS, WG, WG, WG, WG
-
-pat_right_wall:
-.db WG, WG, WG, TS, TS, TS, TS, TS, TS, TS
-
-pat_left_wall:
-.db TS, TS, TS, TS, TS, TS, WG, WG, WG, WG
-
-pat_easy_hard_mode:
-.db TS, SK, TS, TS, WG, WG, TS, TS, TS, TS
-
-pat_center_wall:
-.db TS, TS, TS, TS, WG, WG, TS, TS, TS, TS
-
- ; enf of level row pattern
-pat_eol:
-.db 0xFF
; draws the entire UI
; only call during blank
ui_draw_all:
- call ui_clear_player_hp
- call ui_draw_player_hp
- call ui_draw_enemy_hp
- ret
-
- ; clears player hp
-ui_clear_player_hp:
- ld hl, UI_PLAYER_HP+7
- ld a, UI_WINDOW_BACKGROUND
-
-.rep i, 8, 1, ld [hl+], a
-
- ret
-
- ; draws player hp
-ui_draw_player_hp:
- ld de, UI_PLAYER_HP
- ld hl, str_player
- call puts
-
- ld hl, UI_PLAYER_HP+7
- ld a, [player+act_hp]
- ld b, a
- jp ui_draw_hp
-
- ; draws generic hp
- ; inputs:
- ; hl: address to draw to
- ; b: current hp
-ui_draw_hp:
-
-@full_loop:
- ld a, b
- cp a, 4
- jr c, @skip_full REL
-
- ld a, UI_TILE_HP_4
- ld [hl+], a
-
- ld a, b
- sub a, 4
- ld b, a
- jp z, @done
- jp c, @done ; no more hp to draw
- jr @full_loop REL
-
-@skip_full:
-
- ld a, UI_TILE_HP_1+1
- sub a, b
- ld [hl+], a
-
-@done:
-
- ; clear the next index
- ld a, UI_WINDOW_BACKGROUND
- ld [hl+], a
-
- ret
-
- ; draws boss hp
-ui_draw_enemy_hp:
- ld de, UI_ENEMY_HP
- ld hl, str_enemy
- call puts
-
- ld hl, UI_ENEMY_HP+7
- ld a, HP_MAX ; TODO: load real hp
- ld b, a
- jp ui_draw_hp
-
- ; draws current score
-ui_draw_score:
- ret
-
- ; draws continues
-ui_draw_continues:
ret
; tick rng every frame
call rand
- call video_map_adjust_scroll
+ ; clear oam
+ ; TODO: only clear used OAM
+ call shadow_oam_clear
- call player_draw
call player_update
+ call player_draw
- call actor_update_all
+ call enemy_update
- ; clear oam
- call shadow_oam_clear_rest
- ; update map
- ld a, [map_routine]
- ld l, a
- ld a, [map_routine+1]
- ld h, a
- call_hl
-
- call update_anim_timer
+ ; TODO: update map routine
ret
- ; updates the global animation timer
- ; and the animation frame
-update_anim_timer:
- ld a, [animation_timer]
- inc a
- and a, 0x1F ; max timer
- ld [animation_timer], a
- ; bail if no overflow
- ret nz
-
-@tick_frame:
- ld a, [animation_frame]
- xor a, 1
- ld [animation_frame], a
- ret
new_game:
- ld a, HP_MAX
- ld [player_hp_max], a
-
- ld de, l_main_menu
+ ld de, l1
call map_load
ld hl, update_game
ld a, [game_state+1]
ld h, a
call_hl
-
- call dbg_update
ret
; get inputs
call poll_inputs
-
- call video_map_perform_scroll
; dma previous frame's oam
call OAMDMAFN
@skip_rest:
pop_all
ret
-
- ; scrolls if the flag is set
- ; loads a new map row and sets the scroll
- ; timer
-video_map_perform_scroll:
- ld a, [scroll_timer]
- cp a, MAP_ROW_H-1
- ret nz
-
- xor a, a
- ld [scroll_timer], a
-
- call map_advance_row
-
- ret
-
- ; adjusts the scroll slowly 1 pixel each frame
- ; until the timer reaches 0
-video_map_adjust_scroll:
- ld a, [game_flags]
- and a, GPF_SCROLL
- ret z
-
- ; unset flag
- ld a, [game_flags]
- and a, ~GPF_SCROLL & 0xFF
- ld [game_flags], a
-
- ld a, [scroll_timer]
- inc a
- ld [scroll_timer], a
-
- ld a, [scroll_y]
- sub a, SCROLL_ADJUST_STEP
- ld [scroll_y], a
-
- call scroll_up_adjust
- ret
-
- ; adjusts all actors and rectangles positions
- ; when a scroll occurs
-scroll_up_adjust:
- ; only adjust enemies
- ; do not adjust projectiles or player here
- ld b, ACTS_ENEMY
- ld hl, actors_enemy
-
-@act_loop:
- push hl
-
- ld de, act_pos_y
- add hl, de
- ld a, [hl]
- add a, SCROLL_ADJUST_STEP
- ld [hl+], a ; hl = page y pos hi
-
- ; check if off-screen
- ; do not despawn if -16
- cp a, -16 & 0xFF
- jr nc, @no_act_despawn REL
-
- pop de
- push de
- push af
- cp a, 0x70
- call nc, actor_despawn
- pop af
-
-@no_act_despawn:
- pop hl
- push hl
-
- ; adjust actor rectangle position
- ld de, act_rect+r_pos_y
- add hl, de
- ld a, [hl]
- add a, SCROLL_ADJUST_STEP
- ld [hl], a
-
- pop hl
-
- dec b
- ld de, act_size
- add hl, de
- jr nz, @act_loop REL
-
-
- ; adjust player
-
- ld a, [player+act_pos_y]
- add a, SCROLL_ADJUST_STEP
- ld [player+act_pos_y], a
-
- ld a, [player+act_rect+r_pos_y]
- add a, SCROLL_ADJUST_STEP
- ld [player+act_rect+r_pos_y], a
-
- ; adjust rectangles
- ld b, RECT_MAX
- ld hl, rectangles
-
-@rect_loop:
- push hl
-
- ld de, r_pos_y
- add hl, de
-
- ld a, [hl]
- add a, SCROLL_ADJUST_STEP
- ld [hl+], a
- ; hl = r_pos_x
-
- ; rectangles never despawn
- ; unless cleared with mo_rect_clear
-@no_rect_despawn:
-
- pop hl
- dec b
- ld de, r_size
- add hl, de
- jr nz, @rect_loop REL
-
- ; adjust tmp rect
- ; (just in case we missed a vblank)
- ld a, [tmp_rect+r_pos_y]
- add a, SCROLL_ADJUST_STEP
- ld [tmp_rect+r_pos_y], a
-
- ret
; writes scroll to scroll registers
- ; only call during blank
+ ; only call during blank
scroll_write:
- ld a, [scroll_y]
- ld [RSCY], a
+ ld a, [scroll_y]
+ ld [RSCY], a
- ld a, [scroll_x]
- ld [RSCX], a
+ ld a, [scroll_x]
+ ld [RSCX], a
+ ret
- ret
; swaps the current tilebank by setting/unsetting
; LCDC_TILE_BANK
or a, c
jr nz, video_wait_n_frames REL
ret
-
- ; sets BGP | $1
- ; and then waits
- ; inputs:
- ; $1: BGP
-#macro video_fade_set_bgp
- ld a, [RBGP]
- and a, $1
- ld [RBGP], a
-
- ; wait for 5 frames
- ld bc, 0x4
- call video_wait_n_frames
-#endmacro
-
- ; fades out the background
- ; interrupts must be enabled
- ; preserved all registers
- ; disables window
- ; fades away from current RBGP
-video_fade_out:
- push_all
-
- ; disable winodw
- ld a, [RLCD]
- and a, ~LCDF_WINDOWON & 0xFF
- ld [RLCD], a
-
- video_fade_set_bgp 0b11111111
- video_fade_set_bgp 0b11111100
- video_fade_set_bgp 0b11110000
- video_fade_set_bgp 0b11000000
- video_fade_set_bgp 0b00000000
-
- call disableinterrutpts
- pop_all
- ret
-
-
-
- ; fades the background in
- ; sets RBGP to BGP
- ; interrupts must be enabled
- ; preserved all registers
- ; enables window
- ; inputs:
- ; a: new palette
-video_fade_in:
- push_all
-
- call enableinterrupts
-
- ; disable winodw
- ld a, [RLCD]
- and a, ~LCDF_WINDOWON & 0xFF
- ld [RLCD], a
-
- ; wait a few frames
- call next_vblank_wait
- call next_vblank_wait
- call next_vblank_wait
- call next_vblank_wait
- call next_vblank_wait
- call next_vblank_wait
- call next_vblank_wait
-
- ; re-enable window
- ld a, [RLCD]
- or a, LCDF_WINDOWON
- ld [RLCD], a
-
-
- pop_all
- ; push all saved new palette value
- ; so we can use it here
- ld [RBGP], a
- ret
-#undefine video_fade_set_bgp
-
; loads tilesets
; seed must never be 0
srand: .adv 2
- ; if player y < next_scroll
- ; advance if possible
-player_next_scroll_y: .adv 1
-
-player_hp_max: .adv 1
-player_shoot_delay: .adv 1
-
-; y/x sub pixel movement for player
-player_sub_pixel_y: .adv 1
-player_sub_pixel_x: .adv 1
-
- ; current player weapon
-player_curr_weapon: .adv 1
player: .adv act_size
-; actor table should follow player!
-actors:
-actors_enemy: .adv act_size * ACTS_ENEMY
-actors_enemy_projectiles: .adv act_size * ACTS_ENEMY_PROJECTILES
-actors_player_projectiles: .adv act_size * ACTS_PLAYER_PROJECTILES
-
-map_objs: .adv mo_size * MAP_OBJ_MAX
-
-rectangles: .adv r_size * RECT_MAX
-
- ; temporary rectangle used for collision checks
- ; during movement
-tmp_rect: .adv r_size
-tmp_rect_mask: .adv 1
-
- ; temporary points
- ; has enough space for 4 points
- ; store pre-calculated points here for collision checking
-tmp_rect_points: .adv 8
-
- ; temporary actor direction mask
-tmp_act_direction: .adv 1
-
- ; current row that is being drawn
-map_curr_row: .adv 1
- ; current pattern to be drawn
-map_curr_pat: .adv 2
+enemy: .adv act_size
; current map ptr
map: .adv 2
- ; ptr to the next map object
- ; that may be loaded
-map_curr_obj: .adv 2
-
- ; current bg pointer to write to
-map_scrn_ptr: .adv 2
-
- ; map update routine
- ; can be set by map object
-map_routine: .adv 2
-
- ; map animation timer
- ; use for e.g. arrow indicating scroll is enabled again
-map_anim_timer: .adv 1
- ; state data for map anim
-map_anim_state: .adv 1
- ; custom data for map anim
-map_anim_p0: .adv 1
-
- ; current debug function
-dbg_fn: .adv 1
-
- ; selected debug rect
-dbg_rect: .adv 2
-
-dbg_delay: .adv 1
-