mirror of
https://github.com/robmcmullen/fujirun.git
synced 2024-12-26 10:32:52 +00:00
Added comments; changed zero page so it will mostly work on the atari
This commit is contained in:
parent
51ef7374a9
commit
b1a142d0f0
@ -5,6 +5,14 @@ Fujirun
|
||||
My (winning!) entry in the `KansasFest <https://www.kansasfest.org/>`_ 2017 `HackFest <https://www.kansasfest.org/hackfest/>`_ competition.
|
||||
|
||||
|
||||
Code Walkthrough
|
||||
================
|
||||
|
||||
TBD
|
||||
|
||||
* any place you see the "_smc" extension, that's a target for self-modifying code. Got that from Quinn Dunki.
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
|
4
actors.s
4
actors.s
@ -1,7 +1,7 @@
|
||||
level_enemies .byte 55, 4, 5, 6, 7, 8 ;# level starts counting from 1, so dummy zeroth level info
|
||||
level_speed_l .byte 255, 200, 210, 220, 230, 240 ;# increment of fractional pixel per game frame
|
||||
level_speed_h .byte 2, 2, 2, 2, 2, 2
|
||||
;level_speed_h .byte 0, 0, 0, 0, 0, 0
|
||||
level_speed_h .byte 2, 2, 2, 2, 2, 2 ; increment of whole pixels
|
||||
|
||||
player_score_row .byte 2, 7, 12, 17
|
||||
player_lives_row .byte 3, 8, 13, 18
|
||||
player_score_l .byte 0, 0, 0, 0
|
||||
|
67
logic.s
67
logic.s
@ -1,51 +1,4 @@
|
||||
|
||||
; 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
|
||||
|
||||
; check the player and change its state if necessary
|
||||
evaluate_status nop
|
||||
; update pixel position
|
||||
lda actor_status,x
|
||||
@ -100,7 +53,7 @@ evaluate_status nop
|
||||
sta actor_status,x
|
||||
?end rts
|
||||
|
||||
|
||||
; convert tile and sub-tile position into coordinate on screen
|
||||
get_sprite nop
|
||||
lda actor_row,x
|
||||
tay
|
||||
@ -114,17 +67,10 @@ get_sprite nop
|
||||
clc
|
||||
adc actor_xpixel,x
|
||||
sta actor_x,x
|
||||
|
||||
?end
|
||||
rts
|
||||
|
||||
?game_over
|
||||
rts
|
||||
|
||||
;
|
||||
;
|
||||
; ##### Game logic
|
||||
;
|
||||
; # Determine which of the 4 directions is allowed at the given row, col
|
||||
; def get_allowed_dirs(r, c):
|
||||
; addr = mazerow(r)
|
||||
@ -140,7 +86,6 @@ get_allowed_dirs nop
|
||||
rts
|
||||
|
||||
|
||||
|
||||
; # See if current tile has a dot
|
||||
; def has_dot(r, c):
|
||||
; addr = mazerow(r)
|
||||
@ -154,11 +99,6 @@ has_dot nop
|
||||
rts
|
||||
|
||||
|
||||
; # 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:
|
||||
@ -270,7 +210,8 @@ get_target_col nop
|
||||
rts
|
||||
; 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
|
||||
|
47
main.s
47
main.s
@ -16,7 +16,7 @@ param_index .ds 1
|
||||
param_count .ds 1
|
||||
param_save .ds 1
|
||||
|
||||
*= $0010
|
||||
*= $0080
|
||||
; scratch areas: these may be modified by child subroutines
|
||||
scratch_addr .ds 2
|
||||
scratch_ptr .ds 2
|
||||
@ -28,8 +28,8 @@ scratch_count .ds 1
|
||||
scratch_col .ds 1
|
||||
scratch_row .ds 1
|
||||
|
||||
*= $0020
|
||||
; required variables for HiSprite
|
||||
*= $0090
|
||||
; required variables for HiSprite/asmgen
|
||||
damageindex .ds 1
|
||||
damageindex1 .ds 1
|
||||
damageindex2 .ds 1
|
||||
@ -40,7 +40,7 @@ damageptr .ds 2
|
||||
damageptr1 .ds 2
|
||||
damageptr2 .ds 2
|
||||
|
||||
*= $0030
|
||||
*= $00a0
|
||||
tdamageindex .ds 1
|
||||
tdamageindex1 .ds 1
|
||||
tdamageindex2 .ds 1
|
||||
@ -51,7 +51,7 @@ end .ds 2
|
||||
count .ds 2
|
||||
delta .ds 2
|
||||
|
||||
*= $0040
|
||||
*= $00b0
|
||||
; global variables for this program
|
||||
rendercount .ds 1
|
||||
drawpage .ds 1 ; pos = page1, neg = page2
|
||||
@ -65,7 +65,7 @@ temprow .ds 1
|
||||
tempcol .ds 1
|
||||
tempcheck .ds 1
|
||||
|
||||
*= $0050
|
||||
*= $00c0
|
||||
mazeaddr .ds 2
|
||||
next_level_box .ds 1
|
||||
box_row_save .ds 1
|
||||
@ -77,7 +77,7 @@ frame_count .ds 2
|
||||
countdown_time .ds 1
|
||||
still_alive .ds 1
|
||||
|
||||
*= $0060
|
||||
*= $00d0
|
||||
current_actor .ds 1
|
||||
current .ds 1 ; current direction
|
||||
last_dir .ds 1
|
||||
@ -95,7 +95,7 @@ c2 .ds 1
|
||||
size .ds 1
|
||||
dot .ds 1
|
||||
|
||||
* = $0070
|
||||
* = $00e0
|
||||
before .ds 1
|
||||
crossed .ds 1
|
||||
round_robin_index .ds 2
|
||||
@ -130,21 +130,19 @@ start jsr set_hires ; start with HGR page 1, full screen
|
||||
sta dst+1
|
||||
jsr unpack_lz4
|
||||
|
||||
;jsr clrscr
|
||||
jsr init_once
|
||||
|
||||
restart jsr title_screen
|
||||
restart jsr init_once
|
||||
jsr title_screen
|
||||
jsr init_game
|
||||
jsr game_loop
|
||||
|
||||
check_restart ldx #34
|
||||
check_restart ldx #34 ; x coord on screen for "GAME"
|
||||
ldy player_score_row
|
||||
lda #<game_text
|
||||
sta scratch_ptr
|
||||
lda #>game_text
|
||||
sta scratch_ptr+1
|
||||
jsr printstr
|
||||
ldx #35
|
||||
ldx #35 ; x coordinate for "OVER"
|
||||
ldy player_lives_row
|
||||
lda #<over_text
|
||||
sta scratch_ptr
|
||||
@ -196,8 +194,19 @@ initbackground jsr init_damage
|
||||
jsr copy2to1
|
||||
rts
|
||||
|
||||
; main game loop. Rather than optimize things and unroll all this stuff into
|
||||
; a single big function, I'm calling a bunch of subroutines in hopes that
|
||||
; it is easier to follow.
|
||||
;
|
||||
; The "actor" is either a player or an enemy, and the actor number is placed
|
||||
; in the X register. Subroutines must save the X register and restore it
|
||||
; upon return if they modify it.
|
||||
;
|
||||
; All of the game logic routines expect the player/enemy number in X as they
|
||||
; use it as an index into whatever specific data they need, like position or
|
||||
; direction.
|
||||
game_loop nop
|
||||
inc frame_count
|
||||
inc frame_count ; frame count isn't used for anything other than debugging
|
||||
bne ?1
|
||||
inc frame_count+1
|
||||
?1 jsr userinput
|
||||
@ -234,7 +243,8 @@ game_loop nop
|
||||
|
||||
; if any player is still alive, the game continues. Once the last player
|
||||
; dies, the countdown timer allows the enemies to continue to move a
|
||||
; little while before the game ends
|
||||
; little while before the game ends. Returning from the game loop means
|
||||
; that the game is over.
|
||||
?alive lda still_alive
|
||||
bne ?draw
|
||||
dec countdown_time
|
||||
@ -254,8 +264,7 @@ game_loop nop
|
||||
jmp game_loop
|
||||
|
||||
|
||||
|
||||
|
||||
; updates the game state based on player status changes
|
||||
handle_player nop
|
||||
; if actor_status[zp.current_actor] == PLAYER_REGENERATING:
|
||||
; # If regenerating, change to alive if the player starts to move
|
||||
@ -283,7 +292,6 @@ handle_player nop
|
||||
; # only check for points if still alive
|
||||
; check_dots()
|
||||
; check_boxes()
|
||||
|
||||
?dots lda actor_status,x
|
||||
cmp #PLAYER_ALIVE
|
||||
bne ?final
|
||||
@ -293,7 +301,6 @@ handle_player nop
|
||||
; if actor_status[zp.current_actor] != GAME_OVER:
|
||||
; still_alive += 1
|
||||
; zp.current_actor += 1
|
||||
|
||||
?final lda actor_status,x
|
||||
cmp #GAME_OVER
|
||||
beq ?end
|
||||
|
@ -78,7 +78,7 @@ input_up lda #TILE_UP
|
||||
|
||||
check_down cmp #$af ; down arrow
|
||||
beq input_down
|
||||
cmp #$bb ; ';' key (dvorak keyboards)
|
||||
cmp #$bb ; ';' key (dvorak keyboards!)
|
||||
beq input_down
|
||||
cmp #$da ; 'Z' key
|
||||
beq input_down
|
||||
@ -159,6 +159,9 @@ wait_inner ; inner loop: 14 + 2 + 3
|
||||
bne wait_outer
|
||||
rts
|
||||
|
||||
; Offsets in pixels from upper left corner of sprite to where it should
|
||||
; appear on screen given the tile coords of where the game logic thinks
|
||||
; the sprite is.
|
||||
|
||||
; [i*7-3 for i in range(40)]
|
||||
player_col_to_x .byte 0, 3, 10, 17, 24, 31, 38, 45, 52, 59, 66, 73, 80, 87, 94, 101, 108, 115, 122, 129, 136, 143, 150, 157, 164, 171, 178, 185, 192, 199, 206, 213, 220, 227, 234, 241, 248, 248, 248, 248, 248,
|
||||
@ -168,15 +171,14 @@ player_col_to_x .byte 0, 3, 10, 17, 24, 31, 38, 45, 52, 59, 66, 73, 80, 87, 94,
|
||||
player_row_to_y .byte 0, 3, 11, 19, 27, 35, 43, 51, 59, 67, 75, 83, 91, 99, 107, 115, 123, 131, 139, 147, 155, 163, 171, 179
|
||||
|
||||
|
||||
; defines the zone around the midpoint where the player can change to any direction, not just backtracking.
|
||||
; defines the zone around the midpoint where the player can change to any
|
||||
; direction, not just backtracking. On the apple, there are 7 x positions but
|
||||
; 8 y positions
|
||||
x_allowed_turn .byte 0, 0, 1, 1, 1, 0, 0
|
||||
y_allowed_turn .byte 0, 0, 1, 1, 1, 0, 0, 0
|
||||
|
||||
|
||||
;# Returns address of tile in col 0 of row y
|
||||
;def mazerow(y):
|
||||
; return maze[y]
|
||||
|
||||
; Returns address of tile in col 0 of row y
|
||||
; row in Y
|
||||
mazerow lda textrows_l,y
|
||||
sta mazeaddr
|
||||
@ -185,8 +187,7 @@ mazerow lda textrows_l,y
|
||||
rts
|
||||
|
||||
|
||||
; text & hgr screen utils
|
||||
|
||||
; initialize screen to draw to page 1 (displays page 2)
|
||||
init_screen_once nop
|
||||
lda #0
|
||||
sta KBDSTROBE
|
||||
@ -195,6 +196,8 @@ init_screen_once nop
|
||||
rts
|
||||
|
||||
|
||||
; set an entire column (24 rows) of the HGR screen to the specified character
|
||||
; on page 1
|
||||
; character in A, col in X
|
||||
text_put_col nop
|
||||
sta $0400,x ; row 0
|
||||
@ -223,6 +226,7 @@ text_put_col nop
|
||||
sta $07d0,x ; row 23
|
||||
rts
|
||||
|
||||
; ditto except for page 2
|
||||
text_put_col2 nop
|
||||
sta $0800,x ; row 0
|
||||
sta $0880,x ; row 1
|
||||
@ -259,6 +263,8 @@ textrows_h .byte $04, $04, $05, $05, $06, $06, $07, $07
|
||||
.byte $04, $04, $05, $05, $06, $06, $07, $07
|
||||
|
||||
|
||||
; sets HGR page 1 to all white using a vertical wipe starting from the
|
||||
; top moving down
|
||||
wipeclear1 ldy #0
|
||||
sty param_y
|
||||
wipeclear1_loop lda HGRROWS_L,y
|
||||
@ -285,6 +291,9 @@ wipeclear1_wait nop
|
||||
bcc wipeclear1_loop
|
||||
rts
|
||||
|
||||
|
||||
; sets HGR page 1 to the contents of page 2 using a vertical wipe starting
|
||||
; from the top moving down
|
||||
wipe2to1 ldy #0
|
||||
sty param_y
|
||||
wipe2to1_loop lda HGRROWS_H2,y
|
||||
@ -314,6 +323,8 @@ wipe2to1_wait nop
|
||||
bcc wipe2to1_loop
|
||||
rts
|
||||
|
||||
|
||||
; simple (slow, non-optimized) copy of the data from HGR page 2 to HGR page 1
|
||||
copy2to1 lda #$40
|
||||
sta ?source+2
|
||||
lda #$20
|
||||
@ -331,6 +342,9 @@ copy2to1 lda #$40
|
||||
rts
|
||||
|
||||
|
||||
; copy the entire text screen to the current HGR page. The dest_smc address
|
||||
; is set in the pageflip routine below so they always point to the page
|
||||
; in back, not the page currently being shown.
|
||||
copytexthgr nop
|
||||
ldy #0 ; y is rows
|
||||
copytexthgr_outer
|
||||
@ -353,6 +367,8 @@ copytexthgr_dest_smc
|
||||
rts
|
||||
|
||||
|
||||
; change pages based on the state of the drawpage variable. drawpage == 0
|
||||
; is page 1, drawpage == $80 is page 2
|
||||
pageflip nop
|
||||
lda drawpage
|
||||
eor #$80
|
||||
|
Loading…
Reference in New Issue
Block a user