multiplayer support

This commit is contained in:
2026-07-15 23:00:52 +02:00
parent c7da1be163
commit aa44c389ba
5 changed files with 232 additions and 20 deletions
+8 -2
View File
@@ -10,12 +10,18 @@ that passes in any other lane is a **point**. Three misses end the round.
The spawn rate and the rabbits' speed ramp up with your score, just like The spawn rate and the rabbits' speed ramp up with your score, just like
in the original. in the original.
In **multiplayer** the rabbits still spawn on their own, but player 2
steers the closest one left and right, trying to run the scooter down.
## Controls ## Controls
| Input | Action | | Input | Action |
|-------|--------| |-------|--------|
| Joystick port 2 left/right, or `A` / `D` | change lane (auto-repeats while held) | | Menu: joystick up/down, or the `S` / `M` keys | pick single player / multiplayer |
| Fire (or `SPACE`) | start / retry | | Menu: fire (or `SPACE`); the `1` / `2` keys pick and start at once | start the picked mode |
| P1: joystick port 2 left/right, or `A` / `D` | move the scooter (auto-repeats while held) |
| P2: joystick port 1 left/right, or `J` / `L` | steer the closest rabbit (multiplayer) |
| Fire (or `SPACE`) | start; on the game over screen: back to the menu |
## How it works ## How it works
+48 -5
View File
@@ -206,11 +206,49 @@ rj_noright:
lda joyb lda joyb
rts rts
; fire or space held? returns A != 0 when pressed ; read joystick port 1 merged with the j/l keys (player 2),
; return active-high bits in A and joyb2
read_joy2:
lda #$ff ; no column selected: port B = joystick 1
sta CIA1_PA
lda CIA1_PB
eor #$ff
and #$1f
sta joyb2
and #$0f ; a held direction grounds the same port B
bne rj2_done ; lines the key rows use: skip the scan
lda #$ef ; 'j' = left: keyboard column 4, row bit 2
sta CIA1_PA
lda CIA1_PB
and #$04
bne rj2_noleft
lda joyb2
ora #JOY_LEFT
sta joyb2
rj2_noleft:
lda #$df ; 'l' = right: keyboard column 5, row bit 2
sta CIA1_PA
lda CIA1_PB
and #$04
bne rj2_done
lda joyb2
ora #JOY_RIGHT
sta joyb2
rj2_done:
lda joyb2
rts
; fire on either stick or space held? returns A != 0 when pressed
start_pressed: start_pressed:
jsr read_joy jsr read_joy
and #JOY_FIRE and #JOY_FIRE
bne sp_done bne sp_done
lda #$ff ; deselect the columns: port B = joystick 1
sta CIA1_PA
lda CIA1_PB
eor #$ff
and #JOY_FIRE
bne sp_done
lda #$7f ; space: keyboard column 7, row bit 4 lda #$7f ; space: keyboard column 7, row bit 4
sta CIA1_PA sta CIA1_PA
lda CIA1_PB lda CIA1_PB
@@ -546,12 +584,14 @@ txt_score: !scr "score"
txt_miss: !scr "miss" txt_miss: !scr "miss"
txt_rabbit: !scr "rabbit" txt_rabbit: !scr "rabbit"
txt_scooter: !scr "scooter" txt_scooter: !scr "scooter"
txt_press: !scr "fire = start game" txt_1p: !scr "single player"
txt_info: !scr "joystick 2 or a/d moves" txt_2p: !scr "multiplayer"
txt_info: !scr "p1: joystick 2 or a/d"
txt_info2: !scr "p2: joystick 1 or j/l"
txt_ttgname: !scr "teletype games" txt_ttgname: !scr "teletype games"
txt_presents:!scr "presents" txt_presents:!scr "presents"
txt_over: !scr "game over" txt_over: !scr "game over"
txt_retry: !scr "fire = retry" txt_return: !scr "fire = return"
txt_blank: !fill 19, $20 txt_blank: !fill 19, $20
washtbl: ; gold glow ramp (16 entries, cyclic) washtbl: ; gold glow ramp (16 entries, cyclic)
@@ -565,7 +605,10 @@ bg_top: !byte COL_SKY ; background color above / below the
bg_bot: !byte COL_GRASS ; horizon raster split bg_bot: !byte COL_GRASS ; horizon raster split
seed: !byte $a7 ; rand8 LFSR state seed: !byte $a7 ; rand8 LFSR state
fheld: !byte 0 ; fire/space edge detection state fheld: !byte 0 ; fire/space edge detection state
joyb: !byte 0 ; joystick bits from read_joy (active high) joyb: !byte 0 ; player 1 input bits from read_joy
joyb2: !byte 0 ; player 2 input bits from read_joy2
mp_mode: !byte 0 ; 0 = single player, 1 = multiplayer
k12held: !byte 0 ; menu '1'/'2' key edge detection state
en_sh: !byte 0 ; per-frame sprite register shadows en_sh: !byte 0 ; per-frame sprite register shadows
msb_sh: !byte 0 msb_sh: !byte 0
xex_sh: !byte 0 xex_sh: !byte 0
+4 -4
View File
@@ -32,17 +32,17 @@ over_init:
rts rts
over_update: over_update:
; blink the retry prompt ; blink the return prompt
lda framectr lda framectr
and #32 and #32
bne ou_hide bne ou_hide
+prtext 16, 14, YELLOW, txt_retry, 12 +prtext 16, 13, YELLOW, txt_return, 13
jmp ou_start jmp ou_start
ou_hide: ou_hide:
+prtext 16, 14, YELLOW, txt_blank, 12 +prtext 16, 13, YELLOW, txt_blank, 13
ou_start: ou_start:
jsr start_edge jsr start_edge
beq ou_done beq ou_done
jmp play_init ; its rts returns to the main loop jmp title_init ; back to the menu, mode can be re-picked
ou_done: ou_done:
rts rts
+80
View File
@@ -27,6 +27,8 @@ play_init:
sta spawnt sta spawnt
sta lcnt sta lcnt
sta rcnt sta rcnt
sta p2lcnt
sta p2rcnt
jsr draw_score jsr draw_score
lda #0 ; draw_score leaves a digit code in A lda #0 ; draw_score leaves a digit code in A
@@ -113,6 +115,52 @@ pu_rrel:
sta rcnt sta rcnt
pu_rend: pu_rend:
; -- multiplayer: player 2 steers the closest rabbit --
lda mp_mode
beq pu_nop2
jsr read_joy2
lda joyb2
and #JOY_LEFT
beq pu_p2lrel
inc p2lcnt
lda p2lcnt
cmp #1
beq pu_p2lmove
cmp #REP_HOLD+REP_PERIOD
bne pu_p2lend
lda #REP_HOLD
sta p2lcnt
pu_p2lmove:
lda #$ff ; one lane to the left
sta rtmp2
jsr steer_closest
jmp pu_p2lend
pu_p2lrel:
lda #0
sta p2lcnt
pu_p2lend:
lda joyb2
and #JOY_RIGHT
beq pu_p2rrel
inc p2rcnt
lda p2rcnt
cmp #1
beq pu_p2rmove
cmp #REP_HOLD+REP_PERIOD
bne pu_p2rend
lda #REP_HOLD
sta p2rcnt
pu_p2rmove:
lda #1 ; one lane to the right
sta rtmp2
jsr steer_closest
jmp pu_p2rend
pu_p2rrel:
lda #0
sta p2rcnt
pu_p2rend:
pu_nop2:
; -- shadow init: player sprite bit + its x MSB -- ; -- shadow init: player sprite bit + its x MSB --
ldx player_lane ldx player_lane
lda pxlo,x lda pxlo,x
@@ -197,6 +245,36 @@ pu_rnext:
pu_done: pu_done:
rts rts
; ---- shift the closest live rabbit's lane by rtmp2 (+/-1) ----
steer_closest:
ldx #$ff ; best slot so far
lda #0
sta rtmp ; best t so far
ldy #NRABBITS-1
stc_find:
lda r_act,y
beq stc_next
lda r_thi,y
cmp rtmp
bcc stc_next
sta rtmp
tya
tax
stc_next:
dey
bpl stc_find
cpx #$ff
beq stc_done ; no rabbit on the road
lda r_lane,x
clc
adc rtmp2
bmi stc_done ; already on the leftmost lane
cmp #LANES
bcs stc_done ; ... or the rightmost one
sta r_lane,x
stc_done:
rts
; ---- claim a free rabbit slot and roll its lane + speed ---- ; ---- claim a free rabbit slot and roll its lane + speed ----
spawn_rabbit: spawn_rabbit:
ldx #NRABBITS-1 ldx #NRABBITS-1
@@ -298,6 +376,8 @@ flash: !byte 0 ; frames left of the post-hit blink
spawnt: !byte 0 ; frames since the last spawn spawnt: !byte 0 ; frames since the last spawn
lcnt: !byte 0 ; held-direction counters for the auto-repeat lcnt: !byte 0 ; held-direction counters for the auto-repeat
rcnt: !byte 0 rcnt: !byte 0
p2lcnt: !byte 0 ; the same for player 2's steering
p2rcnt: !byte 0
; ---- rabbit slots (hardware sprites 1-7) ---- ; ---- rabbit slots (hardware sprites 1-7) ----
r_act: !fill NRABBITS, 0 r_act: !fill NRABBITS, 0
+92 -9
View File
@@ -1,7 +1,7 @@
; ----------------------------------------------------------- ; -----------------------------------------------------------
; title.asm - full-screen menu card: big RABBIT / SCOOTER ; title.asm - full-screen menu card: big RABBIT / SCOOTER
; logo flanked by two hopping rabbits, the scooter waiting at ; logo flanked by two hopping rabbits, single/multiplayer
; the bottom, blinking "fire = start game" ; picker (joystick up/down + fire, or the 1 / 2 keys)
; ----------------------------------------------------------- ; -----------------------------------------------------------
title_init: title_init:
@@ -12,7 +12,8 @@ title_init:
+prbig 2, 8, RED, txt_rabbit, 6 +prbig 2, 8, RED, txt_rabbit, 6
+prbig 8, 6, WHITE, txt_scooter, 7 +prbig 8, 6, WHITE, txt_scooter, 7
+prtext 18, 8, WHITE, txt_info, 23 +prtext 18, 9, LGREY, txt_info, 21
+prtext 19, 9, LGREY, txt_info2, 21
; decoration: white rabbits flank the logo (sprites 1-2, ; decoration: white rabbits flank the logo (sprites 1-2,
; double-sized), the red scooter waits at the bottom ; double-sized), the red scooter waits at the bottom
@@ -65,17 +66,99 @@ title_update:
adc #SPRP_BIG adc #SPRP_BIG
sta SPRPTR+2 sta SPRPTR+2
; blink the start line ; joystick up/down picks the mode
jsr read_joy
lda joyb
and #1 ; up
beq tu_noup
lda #0
sta mp_mode
tu_noup:
lda joyb
and #2 ; down
beq tu_nodown
lda #1
sta mp_mode
tu_nodown:
; the 's' / 'm' keys pick the mode (fire still starts)
lda #$fd ; 's': keyboard column 1, row bit 5
sta CIA1_PA
lda CIA1_PB
and #$20
bne tu_nos
lda #0
sta mp_mode
tu_nos:
lda #$ef ; 'm': keyboard column 4, row bit 4
sta CIA1_PA
lda CIA1_PB
and #$10
bne tu_nom
lda #1
sta mp_mode
tu_nom:
; the 1 / 2 keys pick and start right away (edge detected)
lda #$7f ; keyboard column 7: '1' = bit 0, '2' = bit 3
sta CIA1_PA
lda CIA1_PB
and #$09
cmp #$09
beq tu_keyup ; neither key held
ldx k12held
bne tu_menu ; still held from an earlier frame
inx
stx k12held
and #$01
bne tu_key2
lda #0
sta mp_mode
jmp title_go
tu_key2:
lda #1
sta mp_mode
jmp title_go
tu_keyup:
lda #0
sta k12held
tu_menu:
; the two options: the picked line is yellow
lda mp_mode
bne tm_multi
+prtext 14, 14, YELLOW, txt_1p, 13
+prtext 16, 14, WHITE, txt_2p, 11
jmp tm_cursor
tm_multi:
+prtext 14, 14, WHITE, txt_1p, 13
+prtext 16, 14, YELLOW, txt_2p, 11
tm_cursor:
; blinking '>' cursor in front of the picked line
lda #$20
sta SCREEN+14*40+12
sta SCREEN+16*40+12
lda framectr lda framectr
and #32 and #16
bne tu_hide bne tu_start
+prtext 15, 11, YELLOW, txt_press, 17 lda mp_mode
bne tm_cur2
lda #62 ; '>'
sta SCREEN+14*40+12
lda #YELLOW
sta COLRAM+14*40+12
jmp tu_start jmp tu_start
tu_hide: tm_cur2:
+prtext 15, 11, YELLOW, txt_blank, 17 lda #62
sta SCREEN+16*40+12
lda #YELLOW
sta COLRAM+16*40+12
tu_start: tu_start:
; fire or space starts the picked mode
jsr start_edge jsr start_edge
beq tu_done beq tu_done
title_go:
jmp play_init ; its rts returns to the main loop jmp play_init ; its rts returns to the main loop
tu_done: tu_done:
rts rts