mirror of
https://github.com/robmcmullen/fujirun.git
synced 2025-04-07 15:37:23 +00:00
WIP for actor movement
This commit is contained in:
parent
190822a0dc
commit
5e7a46c5ff
246
actors.s
Normal file
246
actors.s
Normal file
@ -0,0 +1,246 @@
|
||||
level_enemies .byte 55, 4, 5, 6, 7, 8 ;# level starts counting from 1, so dummy zeroth level info
|
||||
level_speeds .byte 255, 200, 210, 220, 230, 240 ;# increment of fractional pixel per game frame
|
||||
player_score_row .byte 2, 7, 12, 17
|
||||
|
||||
|
||||
;# sprites all use the same table. In the sample configuration, sprites 0 - 3
|
||||
;# are players, 4 and above are enemies. One is an orbiter enemy, the rest use
|
||||
;# amidar movement.
|
||||
MAX_PLAYERS = 4
|
||||
MAX_AMIDARS = VPATH_NUM + 1 ; # one enemy per vpath + one orbiter
|
||||
MAX_ACTORS = MAX_PLAYERS + MAX_AMIDARS
|
||||
FIRST_PLAYER = 0
|
||||
FIRST_AMIDAR = MAX_PLAYERS
|
||||
LAST_PLAYER = FIRST_AMIDAR - 1
|
||||
LAST_AMIDAR = MAX_ACTORS - 1
|
||||
|
||||
PLAYER_TYPE = 0
|
||||
ORBITER_TYPE = 1
|
||||
AMIDAR_TYPE = 2
|
||||
actor_type .byte PLAYER_TYPE, PLAYER_TYPE, PLAYER_TYPE, PLAYER_TYPE
|
||||
.byte ORBITER_TYPE
|
||||
.byte AMIDAR_TYPE, AMIDAR_TYPE, AMIDAR_TYPE, AMIDAR_TYPE, AMIDAR_TYPE, AMIDAR_TYPE
|
||||
actor_active .byte 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, $ff
|
||||
actor_init_func_l .byte <init_player, <init_orbiter, <init_amidar
|
||||
actor_init_func_h .byte >init_player, >init_orbiter, >init_amidar
|
||||
|
||||
STARTING_LIVES = 3
|
||||
BONUS_LIFE = 10000
|
||||
MAX_LIVES = 8
|
||||
|
||||
X_MIDPOINT = 3
|
||||
X_TILEMAX = 7
|
||||
Y_MIDPOINT = 3
|
||||
Y_TILEMAX = 8
|
||||
|
||||
NOT_VISIBLE = 0
|
||||
PLAYER_DEAD = 1
|
||||
PLAYER_ALIVE = 2
|
||||
PLAYER_EXPLODING = 3
|
||||
PLAYER_REGENERATING = 4
|
||||
AMIDAR_NORMAL = 5
|
||||
ORBITER_NORMAL = 6
|
||||
GAME_OVER = 255
|
||||
;
|
||||
;# Scores
|
||||
;
|
||||
DOT_SCORE = 10
|
||||
PAINT_SCORE_PER_LINE = 100
|
||||
|
||||
; level number in X, clobbers
|
||||
init_level nop
|
||||
stx level
|
||||
lda level_enemies,x
|
||||
clc
|
||||
adc #FIRST_AMIDAR
|
||||
sta last_enemy
|
||||
|
||||
|
||||
;
|
||||
;##### Gameplay initialization
|
||||
;
|
||||
;def init_actor():
|
||||
; # Common initialization params for all actors
|
||||
; actor_col[zp.current_actor] = MAZE_LEFT_COL
|
||||
; actor_xpixel[zp.current_actor] = 3
|
||||
; actor_xfrac[zp.current_actor] = 0
|
||||
; actor_xspeed[zp.current_actor] = 0
|
||||
; actor_row[zp.current_actor] = MAZE_BOT_ROW
|
||||
; actor_ypixel[zp.current_actor] = 3
|
||||
; actor_yfrac[zp.current_actor] = 0
|
||||
; actor_yspeed[zp.current_actor] = 0
|
||||
; actor_input_dir[zp.current_actor] = 0
|
||||
; actor_updown[zp.current_actor] = TILE_UP
|
||||
; actor_dir[zp.current_actor] = TILE_UP
|
||||
; actor_status[zp.current_actor] = NOT_VISIBLE
|
||||
; actor_frame_counter[zp.current_actor] = 0
|
||||
; actor_target_col[zp.current_actor] = 0
|
||||
; actor_input_dir[zp.current_actor] = 0
|
||||
|
||||
; actor in X
|
||||
init_actor nop
|
||||
lda #MAZE_LEFT_COL
|
||||
sta actor_col,x
|
||||
lda #3
|
||||
sta actor_xpixel,x
|
||||
sta actor_ypixel,x
|
||||
lda #0
|
||||
sta actor_xfrac,x
|
||||
sta actor_xspeed,x
|
||||
sta actor_yfrac,x
|
||||
sta actor_yspeed,x
|
||||
sta actor_input_dir,x
|
||||
sta actor_frame_counter,x
|
||||
sta actor_target_col,x
|
||||
sta actor_input_dir,x
|
||||
lda #MAZE_BOT_ROW
|
||||
sta actor_row,x
|
||||
lda #TILE_UP
|
||||
sta actor_updown,x
|
||||
sta actor_dir,x
|
||||
lda #NOT_VISIBLE
|
||||
sta actor_status,x
|
||||
rts
|
||||
|
||||
|
||||
;def init_orbiter():
|
||||
; init_actor()
|
||||
; actor_col[zp.current_actor] = ORBITER_START_COL
|
||||
; actor_row[zp.current_actor] = ORBITER_START_ROW
|
||||
; actor_dir[zp.current_actor] = TILE_UP
|
||||
; actor_status[zp.current_actor] = ORBITER_NORMAL
|
||||
; set_speed(TILE_UP)
|
||||
|
||||
; actor in X
|
||||
init_orbiter nop
|
||||
jsr init_actor
|
||||
lda #ORBITER_START_COL
|
||||
sta actor_col,x
|
||||
lda #ORBITER_START_ROW
|
||||
sta actor_row,x
|
||||
lda #TILE_UP
|
||||
sta actor_dir,x
|
||||
lda #ORBITER_NORMAL
|
||||
sta actor_status,x
|
||||
rts
|
||||
|
||||
|
||||
|
||||
;def init_amidar():
|
||||
; init_actor()
|
||||
; amidar_index = zp.current_actor - FIRST_AMIDAR - 1 # orbiter always 1st enemy
|
||||
; actor_col[zp.current_actor] = amidar_start_col[amidar_index]
|
||||
; actor_row[zp.current_actor] = MAZE_TOP_ROW
|
||||
; actor_ypixel[zp.current_actor] = 4
|
||||
; actor_updown[zp.current_actor] = TILE_DOWN
|
||||
; actor_dir[zp.current_actor] = TILE_DOWN
|
||||
; actor_status[zp.current_actor] = AMIDAR_NORMAL
|
||||
; set_speed(TILE_DOWN)
|
||||
|
||||
; actor in X
|
||||
init_amidar nop
|
||||
jsr init_actor
|
||||
txa
|
||||
sec
|
||||
sbc #FIRST_AMIDAR
|
||||
sbc #1
|
||||
tay
|
||||
lda amidar_start_col,y
|
||||
sta actor_col,x
|
||||
lda #MAZE_TOP_ROW
|
||||
sta actor_row,x
|
||||
lda #4
|
||||
sta actor_ypixel,x
|
||||
lda #TILE_DOWN
|
||||
sta actor_updown,x
|
||||
sta actor_dir,x
|
||||
lda #AMIDAR_NORMAL
|
||||
sta actor_status,x
|
||||
lda #TILE_DOWN
|
||||
jsr set_speed
|
||||
|
||||
|
||||
;def init_player():
|
||||
; init_actor()
|
||||
; addr = player_start_col[zp.num_players]
|
||||
; actor_col[zp.current_actor] = addr[zp.current_actor]
|
||||
; actor_row[zp.current_actor] = MAZE_BOT_ROW
|
||||
; actor_status[zp.current_actor] = PLAYER_ALIVE
|
||||
init_player nop
|
||||
jsr init_actor
|
||||
lda config_num_players ; 4 players max,
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc current_actor
|
||||
tay
|
||||
lda player_start_col,y
|
||||
sta actor_col,x
|
||||
lda #MAZE_BOT_ROW
|
||||
sta actor_row,x
|
||||
lda #PLAYER_ALIVE
|
||||
sta actor_status,x
|
||||
; player_lives[zp.current_actor] = STARTING_LIVES
|
||||
; player_next_target_score[zp.current_actor] = BONUS_LIFE
|
||||
lda #STARTING_LIVES
|
||||
sta player_lives,x
|
||||
lda #BONUS_LIFE
|
||||
sta player_next_target_score,x
|
||||
rts
|
||||
|
||||
|
||||
;def init_actors():
|
||||
init_actors nop
|
||||
; get_col_randomizer(amidar_start_col)
|
||||
lda #<amidar_start_col
|
||||
sta scratch_addr
|
||||
lda #>amidar_start_col
|
||||
sta scratch_addr+1
|
||||
jsr get_col_randomizer
|
||||
; get_col_randomizer(round_robin_up)
|
||||
lda #<round_robin_up
|
||||
sta scratch_addr
|
||||
lda #>round_robin_up
|
||||
sta scratch_addr+1
|
||||
jsr get_col_randomizer
|
||||
; get_col_randomizer(round_robin_down)
|
||||
lda #<round_robin_down
|
||||
sta scratch_addr
|
||||
lda #>round_robin_down
|
||||
sta scratch_addr+1
|
||||
jsr get_col_randomizer
|
||||
|
||||
lda #0
|
||||
sta round_robin_index
|
||||
sta round_robin_index+1
|
||||
|
||||
; zp.current_actor = 0
|
||||
; while zp.current_actor <= zp.last_enemy:
|
||||
; if zp.current_actor <= LAST_PLAYER:
|
||||
; if zp.current_actor < zp.num_players:
|
||||
; init_player()
|
||||
; player_lives[zp.current_actor] = STARTING_LIVES
|
||||
; player_next_target_score[zp.current_actor] = BONUS_LIFE
|
||||
; else:
|
||||
; if zp.current_actor == FIRST_AMIDAR:
|
||||
; init_orbiter()
|
||||
; else:
|
||||
; init_amidar()
|
||||
; zp.current_actor += 1
|
||||
; zp.round_robin_index[:] = [0, 0]
|
||||
lda #$ff
|
||||
sta current_actor
|
||||
init_actors_loop inc current_actor
|
||||
ldx current_actor
|
||||
lda actor_status,x
|
||||
bpl ?2 ; negative = end
|
||||
rts
|
||||
?2 beq init_actors_loop ; zero = skip
|
||||
lda actor_type,x
|
||||
tay
|
||||
lda actor_init_func_l,y
|
||||
sta init_actors_smc+1
|
||||
lda actor_init_func_h,y
|
||||
sta init_actors_smc+2
|
||||
init_actors_smc jsr $ffff
|
||||
jmp init_actors_loop
|
109
background.s
Normal file
109
background.s
Normal file
@ -0,0 +1,109 @@
|
||||
; ##### Collision detection
|
||||
;
|
||||
; # Check possible collisions between the current player and any enemies
|
||||
; def check_collisions():
|
||||
; r = actor_row[zp.current_actor]
|
||||
; c = actor_col[zp.current_actor]
|
||||
; enemy_index = FIRST_AMIDAR
|
||||
; while enemy_index <= zp.last_enemy:
|
||||
; # Will provide pac-man style bug where they could pass through each
|
||||
; # other because it's only checking tiles
|
||||
; if actor_row[enemy_index] == r and actor_col[enemy_index] == c:
|
||||
; start_exploding()
|
||||
; break
|
||||
; enemy_index += 1
|
||||
;
|
||||
; def start_exploding():
|
||||
; actor_status[zp.current_actor] = PLAYER_EXPLODING
|
||||
; actor_frame_counter[zp.current_actor] = EXPLODING_TIME
|
||||
;
|
||||
;
|
||||
; ##### Scoring routines
|
||||
;
|
||||
; def check_dots():
|
||||
; r = actor_row[zp.current_actor]
|
||||
; c = actor_col[zp.current_actor]
|
||||
; if has_dot(r, c):
|
||||
; dot_eaten_row[zp.current_actor] = r
|
||||
; dot_eaten_col[zp.current_actor] = c
|
||||
;
|
||||
; # Update maze here so we can check which player closed off a box
|
||||
; addr = mazerow(r)
|
||||
; addr[c] &= ~TILE_DOT
|
||||
;
|
||||
; player_score[zp.current_actor] += DOT_SCORE
|
||||
;
|
||||
; def update_background():
|
||||
; zp.current_actor = 0
|
||||
; while zp.current_actor < zp.num_players:
|
||||
; if dot_eaten_col[zp.current_actor] < 128:
|
||||
; # Here we update the screen; note the maze has already been updated
|
||||
; # but we don't change the background until now so sprites can
|
||||
; # restore their saved backgrounds first.
|
||||
;
|
||||
; r = dot_eaten_row[zp.current_actor]
|
||||
; c = dot_eaten_col[zp.current_actor]
|
||||
; addr = screenrow(r)
|
||||
; addr[c] &= ~TILE_DOT
|
||||
;
|
||||
; # mark as completed
|
||||
; dot_eaten_col[zp.current_actor] = 255
|
||||
; update_score()
|
||||
; zp.current_actor += 1
|
||||
;
|
||||
; paint_boxes()
|
||||
;
|
||||
; def paint_boxes():
|
||||
; x = 0
|
||||
; pad.addstr(28, 0, "Checking box:")
|
||||
; while x < NUM_BOX_PAINTING_PARAMS * 16:
|
||||
; pad.addstr(29, x, "%d " % x)
|
||||
; if box_painting[x] > 0:
|
||||
; c1 = box_painting[x]
|
||||
; r1 = box_painting[x + 1]
|
||||
; r2 = box_painting[x + 2]
|
||||
; i = box_painting[x + 3]
|
||||
; box_log.debug("Painting box line, player %d at %d,%d" % (i, r1, c1))
|
||||
; pad.addstr(30, 0, "painting box line at %d,%d" % (r1, c1))
|
||||
; addr = screenrow(r1)
|
||||
; for c in range(BOX_WIDTH):
|
||||
; if i == 0:
|
||||
; addr[c1 + c] = ord("X")
|
||||
; else:
|
||||
; addr[c1 + c] = ord(".")
|
||||
; r1 += 1
|
||||
; print "ROW", r1
|
||||
; box_painting[x + 1] = r1
|
||||
; if r1 >= r2:
|
||||
; box_painting[x] = 0
|
||||
; x += NUM_BOX_PAINTING_PARAMS
|
||||
;
|
||||
; def init_static_background():
|
||||
; zp.current_actor = 0
|
||||
; while zp.current_actor < zp.num_players:
|
||||
; row = player_score_row[zp.current_actor]
|
||||
; pad.addstr(row - 1, MAZE_SCORE_COL, " ")
|
||||
; pad.addstr(row, MAZE_SCORE_COL, "Player%d" % (zp.current_actor + 1))
|
||||
; zp.current_actor += 1
|
||||
;
|
||||
; def show_lives(row, num):
|
||||
; i = 1
|
||||
; col = SCREEN_COLS
|
||||
; while col > MAZE_SCORE_COL:
|
||||
; col -= 1
|
||||
; if i < num:
|
||||
; c = "*"
|
||||
; else:
|
||||
; c = " "
|
||||
; pad.addch(row, col, ord(c))
|
||||
; i += 1
|
||||
;
|
||||
; def update_score():
|
||||
; row = player_score_row[zp.current_actor]
|
||||
; if actor_status[zp.current_actor] == GAME_OVER:
|
||||
; pad.addstr(row - 1, MAZE_SCORE_COL, "GAME ")
|
||||
; pad.addstr(row, MAZE_SCORE_COL, " OVER")
|
||||
; else:
|
||||
; pad.addstr(row + 1, MAZE_SCORE_COL, " %06d" % player_score[zp.current_actor])
|
||||
; show_lives(row + 2, player_lives[zp.current_actor])
|
||||
;
|
386
logic.s
Normal file
386
logic.s
Normal file
@ -0,0 +1,386 @@
|
||||
EXPLODING_TIME = 50
|
||||
DEAD_TIME = 40
|
||||
REGENERATING_TIME = 60
|
||||
END_GAME_TIME = 100
|
||||
TITLE_SCREEN_TIME = 100
|
||||
|
||||
|
||||
; def draw_actors():
|
||||
; zp.current_actor = 0
|
||||
; while zp.current_actor <= zp.last_enemy:
|
||||
; r = actor_row[zp.current_actor]
|
||||
; c = actor_col[zp.current_actor]
|
||||
; get_sprite()
|
||||
; draw_sprite(r, c)
|
||||
; zp.current_actor += 1
|
||||
;
|
||||
; def get_sprite():
|
||||
; a = actor_status[zp.current_actor]
|
||||
; if a == PLAYER_ALIVE:
|
||||
; c = ord("$") + zp.current_actor
|
||||
; elif a == PLAYER_EXPLODING:
|
||||
; collision_log.debug("p%d: exploding, frame=%d" % (zp.current_actor, actor_frame_counter[zp.current_actor]))
|
||||
; c = ord(exploding_char[actor_frame_counter[zp.current_actor]])
|
||||
; actor_frame_counter[zp.current_actor] -= 1
|
||||
; if actor_frame_counter[zp.current_actor] <= 0:
|
||||
; actor_status[zp.current_actor] = PLAYER_DEAD
|
||||
; actor_frame_counter[zp.current_actor] = DEAD_TIME
|
||||
; elif a == PLAYER_DEAD:
|
||||
; collision_log.debug("p%d: dead, waiting=%d" % (zp.current_actor, actor_frame_counter[zp.current_actor]))
|
||||
; c = None
|
||||
; actor_frame_counter[zp.current_actor] -= 1
|
||||
; if actor_frame_counter[zp.current_actor] <= 0:
|
||||
; player_lives[zp.current_actor] -= 1
|
||||
; if player_lives[zp.current_actor] > 0:
|
||||
; init_player()
|
||||
; actor_status[zp.current_actor] = PLAYER_REGENERATING
|
||||
; actor_frame_counter[zp.current_actor] = REGENERATING_TIME
|
||||
; else:
|
||||
; actor_status[zp.current_actor] = GAME_OVER
|
||||
; elif a == PLAYER_REGENERATING:
|
||||
; collision_log.debug("p%d: regenerating, frame=%d" % (zp.current_actor, actor_frame_counter[zp.current_actor]))
|
||||
; if actor_frame_counter[zp.current_actor] & 1:
|
||||
; c = ord("$") + zp.current_actor
|
||||
; else:
|
||||
; c = ord(" ")
|
||||
; actor_frame_counter[zp.current_actor] -= 1
|
||||
; if actor_frame_counter[zp.current_actor] <= 0:
|
||||
; actor_status[zp.current_actor] = PLAYER_ALIVE
|
||||
; elif a == AMIDAR_NORMAL or a == ORBITER_NORMAL:
|
||||
; c = ord("0") + zp.current_actor - FIRST_AMIDAR
|
||||
; else:
|
||||
; c = None
|
||||
; zp.sprite_addr = c
|
||||
;
|
||||
;
|
||||
; ##### Game logic
|
||||
;
|
||||
; # Determine which of the 4 directions is allowed at the given row, col
|
||||
; def get_allowed_dirs(r, c):
|
||||
; addr = mazerow(r)
|
||||
; allowed = addr[c] & DIR_MASK
|
||||
; return allowed
|
||||
;
|
||||
; # See if current tile has a dot
|
||||
; def has_dot(r, c):
|
||||
; addr = mazerow(r)
|
||||
; return addr[c] & TILE_DOT
|
||||
;
|
||||
; # clear a dot
|
||||
; def clear_dot(r, c):
|
||||
; addr = mazerow(r)
|
||||
; addr[c] &= ~TILE_DOT
|
||||
;
|
||||
; # Determine the tile location given the direction of the actor's movement
|
||||
; def get_next_tile(r, c, dir):
|
||||
; if dir & TILE_UP:
|
||||
; r -= 1
|
||||
; elif dir & TILE_DOWN:
|
||||
; r += 1
|
||||
; elif dir & TILE_LEFT:
|
||||
; c -= 1
|
||||
; elif dir & TILE_RIGHT:
|
||||
; c += 1
|
||||
; else:
|
||||
; logic_log.error("bad direction % dir")
|
||||
; return r, c
|
||||
;
|
||||
; # Choose a target column for the next up/down direction at a bottom or top T
|
||||
; def get_next_round_robin(rr_table, x):
|
||||
; target_col = rr_table[zp.round_robin_index[x]]
|
||||
; logic_log.debug("target: %d, indexes=%s, table=%s" % (target_col, str(zp.round_robin_index), rr_table))
|
||||
; zp.round_robin_index[x] += 1
|
||||
; if zp.round_robin_index[x] >= VPATH_NUM:
|
||||
; zp.round_robin_index[x] = 0
|
||||
; return target_col
|
||||
;
|
||||
; # Find target column when enemy reaches top or bottom
|
||||
; def get_target_col(c, allowed_vert):
|
||||
; if allowed_vert & TILE_UP:
|
||||
; x = 1
|
||||
; rr_table = round_robin_up
|
||||
; else:
|
||||
; x = 0
|
||||
; rr_table = round_robin_down
|
||||
;
|
||||
; target_col = get_next_round_robin(rr_table, x)
|
||||
; if target_col == c:
|
||||
; # don't go back up the same column, skip to next one
|
||||
; target_col = get_next_round_robin(rr_table, x)
|
||||
;
|
||||
; if target_col < c:
|
||||
; current = TILE_LEFT
|
||||
; else:
|
||||
; current = TILE_RIGHT
|
||||
; actor_target_col[zp.current_actor] = target_col
|
||||
; return current
|
||||
;
|
||||
; def check_midpoint(current):
|
||||
; # set up decision point flag to see if we have crossed the midpoint
|
||||
; # after the movement
|
||||
; if current & TILE_VERT:
|
||||
; sub = actor_ypixel[zp.current_actor]
|
||||
; return sub == Y_MIDPOINT
|
||||
; else:
|
||||
; sub = actor_xpixel[zp.current_actor]
|
||||
; return sub == X_MIDPOINT
|
||||
;
|
||||
; # Move enemy given the enemy index
|
||||
; def move_enemy():
|
||||
; current = actor_dir[zp.current_actor]
|
||||
;
|
||||
; # check sub-pixel location to see if we've reached a decision point
|
||||
; temp = check_midpoint(current)
|
||||
; pixel_move(current)
|
||||
; # check if moved to next tile. pixel fraction stays the same to keep
|
||||
; # the speed consistent, only the pixel gets adjusted
|
||||
; if actor_xpixel[zp.current_actor] < 0:
|
||||
; actor_col[zp.current_actor] -= 1
|
||||
; actor_xpixel[zp.current_actor] += X_TILEMAX
|
||||
; elif actor_xpixel[zp.current_actor] >= X_TILEMAX:
|
||||
; actor_col[zp.current_actor] += 1
|
||||
; actor_xpixel[zp.current_actor] -= X_TILEMAX
|
||||
; elif actor_ypixel[zp.current_actor] < 0:
|
||||
; actor_row[zp.current_actor] -= 1
|
||||
; actor_ypixel[zp.current_actor] += Y_TILEMAX
|
||||
; elif actor_ypixel[zp.current_actor] >= Y_TILEMAX:
|
||||
; actor_row[zp.current_actor] += 1
|
||||
; actor_ypixel[zp.current_actor] -= Y_TILEMAX
|
||||
; s = "#%d: tile=%d,%d pix=%d,%d frac=%d,%d " % (zp.current_actor, actor_col[zp.current_actor], actor_row[zp.current_actor], actor_xpixel[zp.current_actor], actor_ypixel[zp.current_actor], actor_xfrac[zp.current_actor], actor_yfrac[zp.current_actor])
|
||||
; logic_log.debug(s)
|
||||
; pad.addstr(0 + zp.current_actor, 40, s)
|
||||
; if not temp:
|
||||
; if check_midpoint(current):
|
||||
; # crossed the midpoint! Make a decision on the next allowed direction
|
||||
; if actor_status[zp.current_actor] == ORBITER_NORMAL:
|
||||
; decide_orbiter()
|
||||
; else:
|
||||
; decide_direction()
|
||||
;
|
||||
; def pixel_move(current):
|
||||
; if current & TILE_UP:
|
||||
; actor_yfrac[zp.current_actor] -= actor_yspeed[zp.current_actor]
|
||||
; if actor_yfrac[zp.current_actor] < 0:
|
||||
; actor_ypixel[zp.current_actor] -= 1
|
||||
; actor_yfrac[zp.current_actor] += 256
|
||||
; elif current & TILE_DOWN:
|
||||
; actor_yfrac[zp.current_actor] += actor_yspeed[zp.current_actor]
|
||||
; if actor_yfrac[zp.current_actor] > 255:
|
||||
; actor_ypixel[zp.current_actor] += 1
|
||||
; actor_yfrac[zp.current_actor] -= 256
|
||||
; elif current & TILE_LEFT:
|
||||
; actor_xfrac[zp.current_actor] -= actor_xspeed[zp.current_actor]
|
||||
; if actor_xfrac[zp.current_actor] < 0:
|
||||
; actor_xpixel[zp.current_actor] -= 1
|
||||
; actor_xfrac[zp.current_actor] += 256
|
||||
; elif current & TILE_RIGHT:
|
||||
; actor_xfrac[zp.current_actor] += actor_xspeed[zp.current_actor]
|
||||
; if actor_xfrac[zp.current_actor] > 255:
|
||||
; actor_xpixel[zp.current_actor] += 1
|
||||
; actor_xfrac[zp.current_actor] -= 256
|
||||
|
||||
|
||||
; def set_speed(current):
|
||||
; if current & TILE_VERT:
|
||||
; actor_xspeed[zp.current_actor] = 0
|
||||
; actor_yspeed[zp.current_actor] = level_speeds[zp.level]
|
||||
; else:
|
||||
; actor_xspeed[zp.current_actor] = level_speeds[zp.level]
|
||||
; actor_yspeed[zp.current_actor] = 0
|
||||
|
||||
; direction in A, actor in X; clobbers all
|
||||
set_speed nop
|
||||
and #TILE_VERT
|
||||
beq ?1
|
||||
lda #0
|
||||
sta actor_xspeed,x
|
||||
ldy level
|
||||
lda level_speeds,y
|
||||
sta actor_yspeed,x
|
||||
rts
|
||||
?1 lda #0
|
||||
sta actor_yspeed,x
|
||||
ldy level
|
||||
lda level_speeds,y
|
||||
sta actor_xspeed,x
|
||||
rts
|
||||
|
||||
|
||||
|
||||
|
||||
;
|
||||
; def decide_orbiter():
|
||||
; current = actor_dir[zp.current_actor]
|
||||
; r = actor_row[zp.current_actor]
|
||||
; c = actor_col[zp.current_actor]
|
||||
; allowed = get_allowed_dirs(r, c)
|
||||
;
|
||||
; if allowed & current:
|
||||
; # Can continue the current direction, so keep on doing it
|
||||
;
|
||||
; logic_log.debug("orbiter %d: continuing %s" % (zp.current_actor, str_dirs(current)))
|
||||
; else:
|
||||
; # Can't continue, and because we must be at a corner, turn 90 degrees.
|
||||
; # So, if we are moving vertically, go horizontally, and vice versa.
|
||||
;
|
||||
; if current & TILE_VERT:
|
||||
; current = allowed & TILE_HORZ
|
||||
; else:
|
||||
; current = allowed & TILE_VERT
|
||||
; actor_dir[zp.current_actor] = current
|
||||
; set_speed(current)
|
||||
;
|
||||
; def decide_direction():
|
||||
; current = actor_dir[zp.current_actor]
|
||||
; r = actor_row[zp.current_actor]
|
||||
; c = actor_col[zp.current_actor]
|
||||
; allowed = get_allowed_dirs(r, c)
|
||||
; updown = actor_updown[zp.current_actor]
|
||||
;
|
||||
; allowed_horz = allowed & TILE_HORZ
|
||||
; allowed_vert = allowed & TILE_VERT
|
||||
; if allowed_horz:
|
||||
; # left or right is available, we must go that way, because that's the
|
||||
; # Amidar(tm) way
|
||||
;
|
||||
; if allowed_horz == TILE_HORZ:
|
||||
; # *Both* left and right are available, which means we're either in
|
||||
; # the middle of an box horz segment *or* at the top or bottom (but
|
||||
; # not at a corner)
|
||||
;
|
||||
; if allowed_vert:
|
||||
; # At a T junction at the top or bottom. What we do depends on
|
||||
; # which direction we approached from
|
||||
;
|
||||
; if current & TILE_VERT:
|
||||
; # approaching vertically means go L or R; choose direction
|
||||
; # based on a round robin so the enemy doesn't go back up
|
||||
; # the same path. Sets the target column for this enemy to
|
||||
; # be used when approaching the T horizontally
|
||||
; current = get_target_col(c, allowed_vert)
|
||||
;
|
||||
; if allowed_vert & TILE_UP:
|
||||
; logic_log.debug("enemy %d: at bot T, new dir %x, col=%d target=%d" % (zp.current_actor, current, c, actor_target_col[zp.current_actor]))
|
||||
; else:
|
||||
; logic_log.debug("enemy %d: at top T, new dir %x, col=%d target=%d" % (zp.current_actor, current, c, actor_target_col[zp.current_actor]))
|
||||
; else:
|
||||
; # approaching horizontally, so check to see if this is the
|
||||
; # vpath to use
|
||||
;
|
||||
; if actor_target_col[zp.current_actor] == c:
|
||||
; # Going vertical! Reverse desired up/down direction
|
||||
; updown = allowed_vert
|
||||
; current = allowed_vert
|
||||
;
|
||||
; if allowed_vert & TILE_UP:
|
||||
; logic_log.debug("enemy %d: at bot T, reached target=%d, going up" % (zp.current_actor, c))
|
||||
; else:
|
||||
; logic_log.debug("enemy %d: at top T, reached target=%d, going down" % (zp.current_actor, c))
|
||||
; else:
|
||||
; # skip this vertical, keep on moving
|
||||
;
|
||||
; if allowed_vert & TILE_UP:
|
||||
; logic_log.debug("enemy %d: at bot T, col=%d target=%d; skipping" % (zp.current_actor, c, actor_target_col[zp.current_actor]))
|
||||
; else:
|
||||
; logic_log.debug("enemy %d: at top T, col=%d target=%d; skipping" % (zp.current_actor, c, actor_target_col[zp.current_actor]))
|
||||
;
|
||||
; else:
|
||||
; # no up or down available, so keep marching on in the same
|
||||
; # direction.
|
||||
; logic_log.debug("enemy %d: no up/down, keep moving %s" % (zp.current_actor, str_dirs(current)))
|
||||
;
|
||||
; else:
|
||||
; # only one horizontal dir is available
|
||||
;
|
||||
; if allowed_vert == TILE_VERT:
|
||||
; # At a left or right T junction...
|
||||
;
|
||||
; if current & TILE_VERT:
|
||||
; # moving vertically. Have to take the horizontal path
|
||||
; current = allowed_horz
|
||||
; logic_log.debug("enemy %d: taking hpath, start moving %s" % (zp.current_actor, str_dirs(current)))
|
||||
; else:
|
||||
; # moving horizontally into the T, forcing a vertical turn.
|
||||
; # Go back to preferred up/down direction
|
||||
; current = updown
|
||||
; logic_log.debug("enemy %d: hpath end, start moving %s" % (zp.current_actor, str_dirs(current)))
|
||||
; else:
|
||||
; # At a corner, because this tile has exactly one vertical and
|
||||
; # one horizontal path.
|
||||
;
|
||||
; if current & TILE_VERT:
|
||||
; # moving vertically, and because this is a corner, the
|
||||
; # target column must be set up
|
||||
; current = get_target_col(c, allowed_vert)
|
||||
;
|
||||
; if allowed_horz & TILE_LEFT:
|
||||
; logic_log.debug("enemy %d: at right corner col=%d, heading left to target=%d" % (zp.current_actor, c, actor_target_col[zp.current_actor]))
|
||||
; else:
|
||||
; logic_log.debug("enemy %d: at left corner col=%d, heading right to target=%d" % (zp.current_actor, c, actor_target_col[zp.current_actor]))
|
||||
; else:
|
||||
; # moving horizontally along the top or bottom. If we get
|
||||
; # here, the target column must also be this column
|
||||
; current = allowed_vert
|
||||
; updown = allowed_vert
|
||||
; if allowed_vert & TILE_UP:
|
||||
; logic_log.debug("enemy %d: at bot corner col=%d with target %d, heading up" % (zp.current_actor, c, actor_target_col[zp.current_actor]))
|
||||
; else:
|
||||
; logic_log.debug("enemy %d: at top corner col=%d with target=%d, heading down" % (zp.current_actor, c, actor_target_col[zp.current_actor]))
|
||||
;
|
||||
; elif allowed_vert:
|
||||
; # left or right is not available, so we must be in the middle of a
|
||||
; # vpath segment. Only thing to do is keep moving
|
||||
; logic_log.debug("enemy %d: keep moving %x" % (zp.current_actor, current))
|
||||
;
|
||||
; else:
|
||||
; # only get here when moving into an illegal space
|
||||
; logic_log.debug("enemy %d: illegal move to %d,%d" % (zp.current_actor, r, c))
|
||||
; current = 0
|
||||
;
|
||||
; actor_updown[zp.current_actor] = updown
|
||||
; actor_dir[zp.current_actor] = current
|
||||
; set_speed(current)
|
||||
;
|
||||
; def move_player():
|
||||
; r = actor_row[zp.current_actor]
|
||||
; c = actor_col[zp.current_actor]
|
||||
; allowed = get_allowed_dirs(r, c)
|
||||
; current = actor_dir[zp.current_actor]
|
||||
; d = actor_input_dir[zp.current_actor]
|
||||
; pad.addstr(26, 0, "r=%d c=%d allowed=%s d=%s current=%s " % (r, c, str_dirs(allowed), str_dirs(d), str_dirs(current)))
|
||||
; if d:
|
||||
; if allowed & d:
|
||||
; # player wants to go in an allowed direction, so go!
|
||||
; actor_dir[zp.current_actor] = d
|
||||
; r, c = get_next_tile(r, c, d)
|
||||
; actor_row[zp.current_actor] = r
|
||||
; actor_col[zp.current_actor] = c
|
||||
; else:
|
||||
; # player wants to go in an illegal direction. instead, continue in
|
||||
; # direction that was last requested
|
||||
;
|
||||
; if allowed & current:
|
||||
; r, c = get_next_tile(r, c, current)
|
||||
; actor_row[zp.current_actor] = r
|
||||
; actor_col[zp.current_actor] = c
|
||||
;
|
||||
;
|
||||
; ##### Collision detection
|
||||
;
|
||||
; # Check possible collisions between the current player and any enemies
|
||||
; def check_collisions():
|
||||
; r = actor_row[zp.current_actor]
|
||||
; c = actor_col[zp.current_actor]
|
||||
; enemy_index = FIRST_AMIDAR
|
||||
; while enemy_index <= zp.last_enemy:
|
||||
; # Will provide pac-man style bug where they could pass through each
|
||||
; # other because it's only checking tiles
|
||||
; if actor_row[enemy_index] == r and actor_col[enemy_index] == c:
|
||||
; start_exploding()
|
||||
; break
|
||||
; enemy_index += 1
|
||||
;
|
||||
; def start_exploding():
|
||||
; actor_status[zp.current_actor] = PLAYER_EXPLODING
|
||||
; actor_frame_counter[zp.current_actor] = EXPLODING_TIME
|
||||
|
43
rand.s
43
rand.s
@ -62,3 +62,46 @@ get_rand_byte nop
|
||||
eor #$3f
|
||||
sta randval8
|
||||
rts
|
||||
|
||||
|
||||
; # Get random starting columns for enemies by swapping elements in a list
|
||||
; # several times
|
||||
; def get_col_randomizer(r):
|
||||
; r[:] = vpath_cols[:]
|
||||
; x = 10
|
||||
; while x >= 0:
|
||||
; i1 = get_rand_col()
|
||||
; i2 = get_rand_col()
|
||||
; old1 = r[i1]
|
||||
; r[i1] = r[i2]
|
||||
; r[i2] = old1
|
||||
; x -= 1
|
||||
|
||||
; addr in scratch_addr, clobbers all
|
||||
get_col_randomizer nop
|
||||
ldy #VPATH_NUM
|
||||
?1 dey
|
||||
lda vpath_cols,y
|
||||
sta (scratch_addr),y
|
||||
cpy #0
|
||||
bne ?1
|
||||
|
||||
lda #10
|
||||
sta scratch_count
|
||||
get_col_lp jsr get_rand_col
|
||||
sta scratch_0
|
||||
jsr get_rand_col
|
||||
sta scratch_1
|
||||
ldy scratch_0
|
||||
lda (scratch_addr),y
|
||||
sta scratch_2
|
||||
ldy scratch_1
|
||||
lda (scratch_addr),y
|
||||
ldy scratch_0
|
||||
sta (scratch_addr),y
|
||||
ldy scratch_1
|
||||
lda scratch_2
|
||||
sta (scratch_addr),y
|
||||
dex scratch_count
|
||||
bne get_col_lp
|
||||
rts
|
||||
|
140
screen.s
140
screen.s
@ -17,6 +17,7 @@ init_screen_once nop
|
||||
jsr draw_to_page1
|
||||
rts
|
||||
|
||||
|
||||
; character in A, col in X
|
||||
text_put_col nop
|
||||
sta $0400,x ; row 0
|
||||
@ -188,3 +189,142 @@ draw_to_page2 lda #$60
|
||||
|
||||
fastfont jmp $ffff
|
||||
|
||||
|
||||
|
||||
restorebg_init
|
||||
rts
|
||||
|
||||
restorebg_driver
|
||||
; copy damaged characters back to screen
|
||||
;jsr copytexthgr
|
||||
ldy #0
|
||||
sty param_count
|
||||
restorebg_loop1 ldy param_count
|
||||
cpy damageindex
|
||||
bcc restorebg_cont ; possible there's no damage, so have to check first
|
||||
ldy #0
|
||||
sty damageindex ; clear damage index for this page
|
||||
rts
|
||||
restorebg_cont lda (damageptr),y ; groups of 4 x1 -> x2, y1 -> y2
|
||||
sta param_x
|
||||
iny
|
||||
lda (damageptr),y
|
||||
sta param_col
|
||||
iny
|
||||
lda (damageptr),y
|
||||
sta param_y
|
||||
iny
|
||||
lda (damageptr),y
|
||||
sta param_row
|
||||
iny
|
||||
sty param_count
|
||||
|
||||
ldy param_y
|
||||
restorebg_row lda textrows_h,y
|
||||
sta restorebg_row_smc+2
|
||||
lda textrows_l,y
|
||||
sta restorebg_row_smc+1
|
||||
ldx param_x
|
||||
restorebg_row_smc lda $ffff,x
|
||||
jsr fastfont
|
||||
inx
|
||||
cpx param_col
|
||||
bcc restorebg_row_smc
|
||||
iny
|
||||
cpy param_row
|
||||
beq restorebg_row
|
||||
bcc restorebg_row
|
||||
bcs restorebg_loop1
|
||||
|
||||
|
||||
|
||||
; Draw sprites by looping through the list of sprites
|
||||
renderstart
|
||||
ldy #0
|
||||
sty damageindex
|
||||
|
||||
lda #sprite_l - sprite_active
|
||||
sta param_count
|
||||
inc renderroundrobin_smc+1
|
||||
|
||||
renderroundrobin_smc
|
||||
ldy #0
|
||||
sty param_index
|
||||
|
||||
renderloop
|
||||
lda param_index
|
||||
and #sprite_l - sprite_active - 1
|
||||
tay
|
||||
lda sprite_active,y
|
||||
beq renderskip ; skip if zero
|
||||
lda sprite_l,y
|
||||
sta jsrsprite_smc+1
|
||||
lda sprite_h,y
|
||||
sta jsrsprite_smc+2
|
||||
lda sprite_x,y
|
||||
sta param_x
|
||||
lda sprite_y,y
|
||||
sta param_y
|
||||
jmp jsrsprite_smc
|
||||
jsrsprite_smc
|
||||
jsr $ffff ; wish you could JSR ($nnnn)
|
||||
|
||||
ldy damageindex
|
||||
lda scratch_col ; contains the byte index into the line
|
||||
sta (damageptr),y
|
||||
iny
|
||||
clc
|
||||
adc damage_w
|
||||
sta (damageptr),y
|
||||
iny
|
||||
|
||||
; need to convert hgr y values to char rows
|
||||
lda param_y
|
||||
lsr a
|
||||
lsr a
|
||||
lsr a
|
||||
sta (damageptr),y
|
||||
iny
|
||||
lda param_y
|
||||
clc
|
||||
adc damage_h
|
||||
lsr a
|
||||
lsr a
|
||||
lsr a
|
||||
sta (damageptr),y
|
||||
iny
|
||||
sty damageindex
|
||||
|
||||
renderskip
|
||||
inc param_index
|
||||
dec param_count
|
||||
bne renderloop
|
||||
|
||||
renderend
|
||||
rts
|
||||
|
||||
|
||||
|
||||
|
||||
; Sprite data is interleaved so a simple indexed mode can be used. This is not
|
||||
; convenient to set up but makes faster accessing because you don't have to
|
||||
; increment the index register. For example, all the info about sprite #2 can
|
||||
; be indexed using Y = 2 on the indexed operators, e.g. "lda sprite_active,y",
|
||||
; "lda sprite_x,y", etc.
|
||||
;
|
||||
; Number of sprites must be a power of 2
|
||||
|
||||
sprite_active
|
||||
.byte 1, 1, 1, 1, 1, 1, 1, 1 ; 1 = active, 0 = skip
|
||||
|
||||
sprite_l
|
||||
.byte <APPLE_SPRITE9X11, <APPLE_SPRITE9X11, <APPLE_SPRITE9X11, <APPLE_SPRITE9X11, <APPLE_SPRITE9X11, <APPLE_SPRITE9X11, <APPLE_SPRITE9X11, <APPLE_SPRITE9X11
|
||||
|
||||
sprite_h
|
||||
.byte >APPLE_SPRITE9X11, >APPLE_SPRITE9X11, >APPLE_SPRITE9X11, >APPLE_SPRITE9X11, >APPLE_SPRITE9X11, >APPLE_SPRITE9X11, >APPLE_SPRITE9X11, >APPLE_SPRITE9X11
|
||||
|
||||
sprite_x
|
||||
.byte 80, 164, 33, 45, 4, 9, 180, 18
|
||||
|
||||
sprite_y
|
||||
.byte 116, 126, 40, 60, 80, 100, 9, 140
|
||||
|
21
vars.s
21
vars.s
@ -3,3 +3,24 @@
|
||||
amidar_start_col .ds VPATH_NUM
|
||||
round_robin_up .ds VPATH_NUM
|
||||
round_robin_down .ds VPATH_NUM
|
||||
|
||||
actor_col .ds MAX_ACTORS ; # current tile column
|
||||
actor_xpixel .ds MAX_ACTORS ; # current pixel offset in col
|
||||
actor_xfrac .ds MAX_ACTORS ; # current fractional pixel
|
||||
actor_xspeed .ds MAX_ACTORS ; # current speed (affects fractional)
|
||||
actor_row .ds MAX_ACTORS ; # current tile row
|
||||
actor_ypixel .ds MAX_ACTORS ; # current pixel offset in row
|
||||
actor_yfrac .ds MAX_ACTORS ; # current fractional pixel
|
||||
actor_yspeed .ds MAX_ACTORS ; # current speed (affects fractional)
|
||||
actor_updown .ds MAX_ACTORS ; # preferred direction
|
||||
actor_dir .ds MAX_ACTORS ; # actual direction
|
||||
actor_target_col .ds MAX_ACTORS ; # target column at bot or top T
|
||||
actor_status .ds MAX_ACTORS ; # alive, exploding, dead, regenerating, invulnerable, ???
|
||||
actor_frame_counter .ds MAX_ACTORS ; # frame counter for sprite changes
|
||||
actor_input_dir .ds MAX_ACTORS ; # current joystick input direction
|
||||
|
||||
dot_eaten_row .ds 4 ; # dot eaten by player
|
||||
dot_eaten_col .ds 4
|
||||
player_score .ds 4
|
||||
player_next_target_score .ds 4
|
||||
player_lives .ds 4 ; # lives remaining
|
||||
|
18
working.s
18
working.s
@ -32,6 +32,7 @@ scratch_addr .ds 2
|
||||
scratch_ptr .ds 2
|
||||
scratch_0 .ds 1
|
||||
scratch_1 .ds 1
|
||||
scratch_2 .ds 1
|
||||
scratch_index .ds 1
|
||||
scratch_count .ds 1
|
||||
scratch_col .ds 1
|
||||
@ -68,6 +69,16 @@ next_level_box .ds 1
|
||||
box_row_save .ds 1
|
||||
box_col_save .ds 1
|
||||
maze_gen_col .ds 1
|
||||
config_num_players .ds 1
|
||||
|
||||
*= $50
|
||||
current_actor .ds 1
|
||||
current_dir .ds 1
|
||||
r .ds 1
|
||||
c .ds 1
|
||||
round_robin_index .ds 2
|
||||
level .ds 1
|
||||
last_enemy .ds 1
|
||||
|
||||
|
||||
; memory map
|
||||
@ -128,6 +139,11 @@ clr1
|
||||
init_game nop
|
||||
jsr init_maze
|
||||
jsr copytexthgr
|
||||
lda #1
|
||||
jsr init_level
|
||||
lda #1
|
||||
sta config_num_players
|
||||
jsr init_actors
|
||||
rts
|
||||
|
||||
game_loop nop
|
||||
@ -137,6 +153,8 @@ game_loop nop
|
||||
.include "rand.s"
|
||||
.include "screen.s"
|
||||
.include "maze.s"
|
||||
.include "actors.s"
|
||||
.include "logic.s"
|
||||
.include "debug.s"
|
||||
|
||||
; vars must be last
|
||||
|
Loading…
x
Reference in New Issue
Block a user