2 ; Copyright (C) 2010 Kent Hansen.
4 ; This program is free software; you can redistribute it and/or modify
5 ; it under the terms of the GNU General Public License as published by
6 ; the Free Software Foundation; either version 3 of the License, or
7 ; (at your option) any later version.
9 ; This program is distributed in the hope that it will be useful,
10 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 ; GNU General Public License for more details.
14 ; You should have received a copy of the GNU General Public License
15 ; along with this program. If not, see <http://www.gnu.org/licenses/>.
18 .
include "common/fade.h"
19 .
include "common/joypad.h"
20 .
include "common/ldc.h"
21 .
include "common/fixedpoint.h"
22 .
include "common/palette.h"
23 .
include "common/ppu.h"
24 .
include "common/ppubuffer.h"
25 .
include "common/ptr.h"
26 .
include "sound/mixer.h"
27 .
include "sound/sequencer.h"
28 .
include "sound/sound.h"
29 .
include "common/sprite.h"
30 .
include "common/timer.h"
44 ; 0 = normal time, 1 = bullet time
48 ; 0 = full length, 1 = clip
53 displayed_energy_level .
db[2]
55 display_letter_index .
db
57 damage_blink_counter .
db
58 screen_shake_counter .
db
60 ; 0 - normal, 1 - dead, 2 - done
66 ; Number of targets until the next heart should be spawned
67 heart_spawn_counter .
db
68 HEART_SPAWN_INTERVAL .
equ 32
69 spawned_heart_state .
db
76 ; Holds mapping from virtual to physical button, for 2 players.
77 button_mapping .
byte[5*2]
78 .
public button_mapping
80 ; The extent of the vertical area where hits are acknowledged, in pixels.
82 ; The Y position where the hit area begins.
85 ; Mask of the lanes mapped to each player.
88 normal_target_attributes .
db[5]
90 ; Calculated each frame from joypad input.
91 lane_input_posedge .
db[2]
94 LOCKED_LANES_MAX .
equ 2
98 locked_lane_timers .
db[5]
100 ; array of targets, used to populate the linked list
101 targets_1 .target_1
[MAX_TARGETS
]
102 targets_2 .target_2
[MAX_TARGETS
]
107 free_targets_list .
db
108 active_targets_head .
db
109 active_targets_tail .
db
112 missed_targets_head .
db
113 missed_targets_tail .
db
115 .
public free_targets_list
116 .
public active_targets_head
117 .
public active_targets_tail
119 TARGET_DATA_DELAY_WIDTH .
equ 3 ; number of bits for delay
121 target_data_speed .
db ; frames per data timer decrement
122 target_data_timer .
db
123 should_load_targets .
db
124 target_data_chunk_length .
db ; Number of targets per progress increment
129 target_song_order_skip .
db
130 target_song_row_skip .
db
132 ; The player's state.
136 stat_changed .
db ; b0: score, b1: top score, b2: progress, b3: energy, b4: lives, b5: points level, b6: letters
139 progress_countdown .
db
144 selected_menu_item .
db
146 lane_switch_request .
db
147 lane_switcher_offset .
db
149 speed_bump_request .
db
153 saved_muted_channels .
db
165 ; ### move to its own file
166 AC0 .
db ; initial dividend & resulting quotient
169 XTND0 .
db ; remainder
187 ; Pointer to the data that describes targets+timings
189 target_data_bit_ctr .
db
194 ;.define DEBUG_TARGET_DATA_TIMER
196 .
public go_to_title_screen
197 .
public go_to_game_type_select
199 .
public setup_normal_play
200 .
public setup_clip_play
205 .
public game_paused_main
208 .
public rock_score_table
209 .
public draw_lane_indicator
211 .
public set_default_button_mapping
213 .
public set_emu_button_mapping
214 .
public set_guitar_button_mapping
221 .
public initialize_target_lists
223 .
public add_to_active_targets_list
225 .extrn
update_ampdisplay:proc
226 .extrn
play_dmc_sample:proc
228 .extrn
game_ui_data:label
229 .extrn
pad3d_data:label
230 .extrn
main_cycle:byte
231 .extrn
selected_song:byte
232 .extrn
target_data_table:label
233 .extrn
frame_count:byte
234 .extrn
bitmasktable:byte
235 .extrn
global_transpose:byte
236 .extrn
volume_table:byte
237 .extrn
game_type:byte
239 .
proc go_to_title_screen
240 lda #
1 : sta main_cycle
241 lda #
0 : jmp swap_bank
244 .
proc go_to_game_type_select
245 lda #
24 : sta main_cycle
246 lda #
6 : jmp swap_bank
249 .
proc setup_normal_play
255 .
proc setup_clip_play
268 ; Reads the next N bits from the target data stream.
269 ; In: A = number of bits to read (1..8)
270 ; Out: A = value of N bits (upper bits zero)
272 .
proc read_target_data_bits
274 lda #
0 ; output will be shifted into here
276 dec target_data_bit_ctr
278 ; reset counter, read next byte
283 sta target_data_bit_ctr
293 + asl target_data_bits
300 .
proc fetch_target_data_byte
302 jmp read_target_data_bits
305 .
proc set_target_data_timer
306 sta target_data_timer
310 target_data_timer_table:
311 .
db 1,2,4,8,12,16,24,32
313 .
proc maybe_seek_one_target_pattern
314 lda target_song_order_skip
316 dec target_song_order_skip
317 lda #
0 ; seek one pattern
318 jsr sequencer_seek_order_relative
322 .
proc maybe_seek_to_target_pattern
326 jsr maybe_seek_one_target_pattern
327 jsr maybe_seek_one_target_pattern
328 jsr maybe_seek_one_target_pattern
329 jsr maybe_seek_one_target_pattern
330 jsr maybe_seek_one_target_pattern
331 jsr maybe_seek_one_target_pattern
335 .
proc init_begin_song_timer_lo
338 lda target_data_speed
341 + sta begin_song_timer
+1
345 .
proc maybe_begin_song
346 lda begin_song_timer
+0
347 ora begin_song_timer
+1
350 + jsr maybe_seek_to_target_pattern
351 dec begin_song_timer
+1
354 + lda begin_song_timer
+0
360 + jsr on_pattern_row_change
361 dec begin_song_timer
+0
363 jmp init_begin_song_timer_lo
365 ldcay on_pattern_row_change
366 jmp set_pattern_row_callback
371 .
db $0f,$10,$16,$27 ; logo, hearts, VU
373 .
db $0f,$2B,$20,$10 ; points, progress bar
374 .
db $0f,$38,$10,$00 ; pad, VU
376 ;.db $0f,$02,$12,$32 ; target
377 .
db $0f,$02,$22,$30 ; target - blue
378 .
db $0f,$0A,$2A,$30 ; target - green
379 ;.db $0f,$04,$24,$30 ; target - pink
380 .
db $0f,$08,$28,$30 ; target - yellow
381 .
db $0f,$06,$16,$30 ; target - red
382 ;.db $0f,$08,$28,$2A ; joypad indicator, points level indicator
383 ;.db $0f,$20,$20,$20 ; pause menu
384 ;.db $0f,$20,$20,$20 ; pause menu
391 jsr fill_all_nametables
395 lda #
(PPU_CTRL0_SPRITE_SIZE_8x16 | PPU_CTRL0_BG_TABLE_0000 | PPU_CTRL0_SPRITE_TABLE_1000
)
397 lda #
(PPU_CTRL1_BG_CLIP_ON | PPU_CTRL1_SPRITE_CLIP_ON
)
414 jsr start_song
; mute
418 lda #
16 : sta chr_banks
[0]
419 lda #
18 : sta chr_banks
[1]
420 lda #
20 : sta chr_banks
[2]
421 lda #
21 : sta chr_banks
[3]
422 lda #
22 : sta chr_banks
[4]
423 lda #
23 : sta chr_banks
[5]
428 jsr swap_bank
; for game ui data
430 jsr write_ppu_data_at
432 jsr write_ppu_data_at
437 jsr initialize_target_lists
444 lda player.checkpoint_score
+0
446 lda player.checkpoint_score
+1
448 lda player.checkpoint_score
+2
455 sta player.current_streak
+0
456 sta player.current_streak
+1
457 sta player.longest_streak
+0
458 sta player.longest_streak
+1
459 sta player.missed_count
+0
460 sta player.missed_count
+1
461 sta player.hit_count
+0
462 sta player.hit_count
+1
463 sta player.err_count
+0
464 sta player.err_count
+1
465 sta player.acquired_letters
466 sta player.skull_hit_count
467 sta player.pow_hit_count
468 sta player.star_hit_count
469 sta player.clock_hit_count
470 sta player.fake_skull_hit_count
471 sta player.heart_spawn_count
472 sta player.skull_miss_count
473 sta player.pow_miss_count
474 sta player.star_miss_count
475 sta player.clock_miss_count
476 sta player.fake_skull_miss_count
477 sta player.points_level
479 sta player.letter_index
483 sta spawned_heart_state
488 sta display_letter_index
489 sta damage_blink_counter
490 sta screen_shake_counter
491 sta lane_switch_request
492 sta speed_bump_request
494 sta lane_switcher_offset
495 lda #HEART_SPAWN_INTERVAL
496 sta heart_spawn_counter
497 lda player.difficulty
498 sta player.speed_level
501 lda #
3 : sta miss_damage
502 lda #
2 : sta error_damage
503 lda #
6 : sta skull_damage
507 ; in clip mode, bump speed and double damage levels
508 inc player.speed_level
521 ; sta player.difficulty
524 ; sta player.speed_level
536 ldy player.difficulty
537 lda
@@clip_mode_chunk_lengths,y
538 sta target_data_chunk_length
539 sta progress_countdown
544 lda clips
+1,y
; marker
545 + jsr init_target_data
548 jsr mixer_set_muted_channels
561 jmp start_fade_from_black
563 @@clip_mode_chunk_lengths:
571 .
db 0,3 ; 20-24 ocean, chorus
572 .
db 2,1 ; 05-07 ripe, 2nd verse
573 .
db 3,1 ; 0D-0F break, chorus
574 .
db 4,1 ; 18-19 count, "bluesy" part
575 .
db 5,1 ; 10-11 levva livet, chorus
577 .
db 1,1 ; 0D-0E burn, "instrumental"
578 .
db 0,2 ; 18-1C ocean, "a capella"
579 .
db 3,2 ; 10-13 break, solo
580 .
db 2,2 ; 0E-10 ripe, take-off
581 .
db 5,2 ; 18-19 levva livet solo
582 .
db 4,2 ; 1A-1D count, two guitars
584 .
db 0,1 ; 10-14 ocean, solo
585 .
db 2,3 ; 10-12 ripe, next-to-last part
586 .
db 1,2 ; 15-18 burn, "hunk-a-hunk"
587 .
db 4,3 ; 1E-22 count, chorus last time
596 .
proc initialize_target_lists
598 sta active_targets_head
599 sta active_targets_tail
602 sta missed_targets_head
603 sta missed_targets_tail
605 stx free_targets_list
611 cpx #
(sizeof target_1
* (MAX_TARGETS
-1))
620 ; set hit pos and extent according to speed level
621 ldy player.speed_level
622 lda
@@hit_extent_table,y
624 lda
@@hit_start_y_table,y
628 .
db 14,16,18,21,24,28,32,34
630 .
db 197,196,195,194,192,190,188,186
633 ; A = marker (0 = beginning)
634 .
proc init_target_data
638 asl
: asl
: asl
: asl
; each entry is 16 bytes
640 lda target_data_table
+1,y
644 ora player.speed_level
646 lda target_data_table
+2,x
; delay until song is started
648 lda target_data_table
+0,y
; 16K bank to load @ $8000
651 ; set target data pointer based on difficulty
654 adc player.difficulty
655 adc player.difficulty
657 lda target_data_table
+10,y
659 lda target_data_table
+11,y
663 sta target_data_bit_ctr
668 ; Target data header: speed (1 byte), chunk length (1 byte)
669 jsr fetch_target_data_byte
670 sta target_data_speed
671 jsr fetch_target_data_byte
674 sta target_data_chunk_length
675 sta progress_countdown
678 jsr set_target_data_timer
681 jsr mixer_set_muted_channels
682 ; the song is started later, to be in sync with the target data
684 sta begin_song_timer
+0
685 jsr init_begin_song_timer_lo
690 jsr fetch_target_data_byte
692 jsr fetch_target_data_byte
698 lda
[target_data
],y
; offset low
701 lda
[target_data
],y
; offset high
704 lda
[target_data
],y
; order start
705 sta target_song_order_skip
707 lda
[target_data
],y
; pattern row start
708 sta target_song_row_skip
714 ; divide by 8 (since the offset is in bits)
736 jsr read_target_data_bits
740 ; Initialization that's specific to the game type.
742 ; for 1-player, target attributes are fixed
743 ; for 2-player, they must be in sync with lane mapping
745 sta normal_target_attributes
+0 ; left
746 sta normal_target_attributes
+1 ; right
749 lda #
2 ; 1 player: middle targets always yellow
750 + sta normal_target_attributes
+2 ; middle
752 sta normal_target_attributes
+3 ; B
753 sta normal_target_attributes
+4 ; A
756 sta player.energy_level
+0
757 sta displayed_energy_level
+0
762 + sta player.energy_level
+1
763 sta displayed_energy_level
+1
766 beq
+ ; no score in versus
776 beq
@@one_player_init
780 sta player_lanes
+0 ; player 1 lanes
782 sta player_lanes
+1 ; player 2 lanes
786 beq
@@write_normal_interface
788 ; who starts with middle lane is determined by play count
789 lda play_count
: and #
2 : asl
795 lda play_count
: and #
1
804 + jsr sync_normal_target_attributes
806 sta palette
+5 ; gray instead of dark green
807 ldcay
@@versus_interface_data
808 jmp write_ppu_data_at
812 sta player_lanes
+0 ; player 1 lanes
814 sta player_lanes
+1 ; player 2 lanes
815 @@write_normal_interface:
816 ldcay
@@normal_interface_data
817 jmp write_ppu_data_at
819 @@versus_interface_data:
820 ; progress indicator (initially "empty")
822 ; energy bars (initially full)
829 @@normal_interface_data:
831 .
db $20,$4B,$03,$01,$02,$03
832 ; D-PAD HERO (letter placeholders)
833 .
db $20,$55,$0A,$04,$8E,$05,$06,$04,$00,$07,$08,$09,$8D
835 .
db $20,$56,$03,$03,$04,$05 ; TOP
837 ; progress indicator (initially "empty")
839 ; energy bar (initially full)
842 ; the X left of life count
850 .
proc set_default_button_mapping
859 ; LEFT, RIGHT, SELECT, B, A
864 .
proc set_emu_button_mapping
873 ; B, A, SELECT, LEFT, RIGHT
877 .
proc set_guitar_button_mapping
890 ; Y = lane index (0..4)
892 .
proc draw_lane_indicator
894 jsr next_sprite_index
898 lda lane_slot_y_coords
,y
900 lda lane_slot_x_coords
,y
905 jsr next_sprite_index
909 lda lane_slot_y_coords
,y
911 lda lane_slot_x_coords
,y
921 ; LEFT, RIGHT, SELECT, B, A
922 .
db 30-6,72+1,120,175-7,224-8
924 .
db 207,207,207,207,207
926 ; Puts sprites that show which of the lanes are "pressed".
927 .
proc draw_pressed_lanes
929 ; beq + ; only draw buttons if we're in play mode
937 jsr draw_lane_indicator
943 .
proc on_pattern_row_change
944 dec target_data_timer
945 .ifdef DEBUG_TARGET_DATA_TIMER
949 jsr print_target_data_timer
957 sta should_load_targets
961 .
proc maybe_load_targets
962 lda should_load_targets
966 sta should_load_targets
967 jmp process_target_data
970 ; X = offset of target being added
972 .
proc add_to_active_targets_list
975 ldy active_targets_tail
976 stx active_targets_tail
979 stx active_targets_head
987 ; A = lane (bits 2..0), type (bits 5..3)
989 ; Returns: X = offset of added target
991 ; grab target from free list
993 ldx free_targets_list
996 ; fatal, no more free targets
998 + lda targets_2.next
,x
999 sta free_targets_list
1001 ; initialize the target
1002 sta targets_1.state
,x
1004 sta targets_2.duration
,x
1006 sta targets_1.pos_y.frac
,x
1007 sta targets_1.pos_x.frac
,x
1008 lda #
48 ; initial Y position
1009 sta targets_1.pos_y.
int,x
1010 lda targets_1.state
,x
1014 sta targets_1.pos_x.
int,x
1016 lda player.speed_level
1018 sta targets_2.speed_x.frac
,x
; temp
1020 ora targets_2.speed_x.frac
,x
1023 sta targets_2.speed_x.frac
,x
1025 sta targets_2.speed_x.
int,x
1027 lda player.speed_level
1030 lda
@@speed_y_table+0,y
1031 sta targets_2.speed_y.
int,x
1032 lda
@@speed_y_table+1,y
1033 sta targets_2.speed_y.frac
,x
1034 jmp add_to_active_targets_list
1035 ; LEFT, RIGHT, SELECT, B, A
1037 .
db 103,112,120,128,137
1040 .
db $FF,$FF,$00,$00,$00 : .
db 0,0,0
1042 .
db $FF,$FF,$00,$00,$00 : .
db 0,0,0
1044 .
db $FF,$FF,$00,$00,$01 : .
db 0,0,0
1046 .
db $FE,$FF,$00,$00,$01 : .
db 0,0,0
1048 .
db $FE,$FF,$00,$00,$01 : .
db 0,0,0
1050 .
db $FE,$FF,$00,$00,$01 : .
db 0,0,0
1052 .
db $FE,$FF,$00,$01,$02 : .
db 0,0,0
1054 .
db $FD,$FE,$00,$01,$02 : .
db 0,0,0
1057 .
db $80,$C0,$00,$40,$80 : .
db 0,0,0
1059 .
db $40,$A0,$00,$60,$C0 : .
db 0,0,0
1061 .
db $00,$80,$00,$80,$00 : .
db 0,0,0
1063 .
db $C0,$60,$00,$A0,$40 : .
db 0,0,0
1065 .
db $80,$40,$00,$C0,$80 : .
db 0,0,0
1067 .
db $40,$20,$00,$E0,$C0 : .
db 0,0,0
1069 .
db $00,$00,$00,$00,$00 : .
db 0,0,0
1071 .
db $C0,$E0,$00,$20,$40 : .
db 0,0,0
1084 .
proc maybe_request_lane_switch
1088 lda lane_switcher_offset
1090 bne
+ ; don't request lane switch if there is already a switcher
1092 sta lane_switch_request
1096 .
proc maybe_request_speed_bump
1104 lda player.speed_level
1108 sta speed_bump_request
1112 .
proc play_speed_bump_sfx
1117 ; Reads target data and adds targets to lanes accordingly.
1118 ; Sets the timer for the next data processing.
1119 .
proc process_target_data
1121 jsr read_target_data_bits
; lanes specifier
1133 beq process_target_data
; ignore if not in clip mode
1141 lda
@@lanes_specifier_mask,y
1146 pha
; save lanes mask
1148 jsr read_target_data_bits
; read normal/extended type bit
1149 lsr
; if it's zero, it's a normal target (type 0)
1153 jsr read_target_data_bits
; type - 1
1163 sbc #
5 ; letter -> normal, fake skull -> skull
1175 ; TODO - in boss mode, it becomes a skull (ora #$08) -- unless it is a lane switcher
1178 lda lane_switch_request
1182 cmp #
2 ; lane switcher only occurs in middle lane
1184 stx lane_switcher_offset
; this is the lane switcher target!
1186 sta lane_switch_request
1189 tax
; restore lane index
1190 pla
; restore lanes mask
1196 dec progress_countdown
1197 bne
@@set_next_delay
1199 jsr maybe_request_lane_switch
1200 jsr maybe_request_speed_bump
1204 lda target_data_chunk_length
1205 sta progress_countdown
1208 lda #TARGET_DATA_DELAY_WIDTH
1209 jsr read_target_data_bits
1211 lda target_data_timer_table
,y
1212 ldy speed_bump_request
1216 sta speed_bump_request
1217 ; Add the difference between next speed level and this one,
1218 ; in order to keep the target data in sync.
1220 asl
: asl
: asl
: asl
; * 16
1221 ora player.speed_level
1222 inc player.speed_level
1224 lda target_data_table
+2,y
1226 sbc target_data_table
+3,y
1230 jsr play_speed_bump_sfx
1232 + jsr set_target_data_timer
1233 .ifdef DEBUG_TARGET_DATA_TIMER
1234 jmp print_target_data_timer
1239 @@lanes_specifier_mask:
1240 .
db %00000 ; 0 - none
1241 .
db %00001 ; 1 - left
1242 .
db %00010 ; 2 - right
1243 .
db %00100 ; 3 - select
1246 .
db %01001 ; 6 - left + B
1247 .
db %10001 ; 7 - left + A
1248 .
db %01010 ; 8 - right + B
1249 .
db %10010 ; 9 - right + A
1250 .
db %11000 ; 10 - B + A
1253 .ifdef DEBUG_TARGET_DATA_TIMER
1254 .
proc print_target_data_timer
1255 lda target_data_timer
: sta AC0
1256 lda #
0 : sta AC1
: sta AC2
1257 ldx #
2 : lda #
$22 : ldy #
$10
1262 .
proc toggle_time_mode
1266 lda global_transpose
1268 sta global_transpose
1270 sta was_music_paused
1273 + jsr mixer_get_muted_channels
1274 sta saved_muted_channels
1276 jsr mixer_set_muted_channels
1282 sta transition_timer
1286 ; Counts number of 1 bits in A.
1287 ; Returns: X=number of 1 bits
1300 ; X = player (0 or 1)
1301 .
proc check_for_errors
1304 eor lane_input_posedge
,x
1305 and lane_input_posedge
,x
1314 + jsr deal_error_pain
1316 jsr reset_points_level
1322 ; The most important piece of game logic.
1323 ; Finds out which targets are hittable and hit,
1324 ; explodes hit targets unless no errors (cheating),
1326 ; Moves missed targets to missed list.
1327 .
proc process_active_targets
1334 ldy active_targets_head
1337 cpy #
$FF ; end of list?
1341 jsr check_for_errors
1343 jsr check_for_errors
1344 jmp sweep_active_targets
1350 lda targets_1.state
,y
1356 jmp @@next ; we already checked this lane, target can't possible be within hit range
1357 + lda bitmasktable
,x
1360 ; not hittable, hittable or missed?
1361 lda targets_1.pos_y.
int,y
1364 bcc
@@next ; not hittable
1372 ; is it actually hit?
1373 lda lane_input_posedge
+0
1375 bne
@@hit_by_player_1
1376 - lda lane_input_posedge
+1
1378 bne
@@hit_by_player_2
1379 ; if the lane is locked, hit anyway unless it's a skull
1380 -- lda bitmasktable
,x
1383 lda targets_1.state
,y
1392 beq
- ; ignore, player 1 does not control this lane
1398 beq
-- ; ignore, player 2 does not control this lane
1406 lda targets_2.next
,y
1412 lda targets_1.state
,y
1426 beq
@@miss_fake_skull
1429 ; TODO - in boss mode, hurt the player!
1430 jmp @@move_to_missed_list
1433 inc player.pow_miss_count
1434 jmp @@move_to_missed_list
1437 inc player.star_miss_count
1438 jmp @@move_to_missed_list
1441 inc player.clock_miss_count
1442 jmp @@move_to_missed_list
1445 jmp @@move_to_missed_list
1448 inc player.fake_skull_miss_count
1453 jmp @@move_to_missed_list
1456 ; missing a normal target is punished - if player(s) alive
1457 lda player.energy_level
+0
1458 beq
@@move_to_missed_list
1461 ora player.energy_level
+1 ; versus && player 2 dead
1462 beq
@@move_to_missed_list
1463 cpy lane_switcher_offset
1465 ; fake a hit, to enable automatic lane switch
1466 lda #
4 ; middle lane mask
1470 sta lane_switcher_offset
1471 jsr switch_player_lane_mapping
1474 + jsr inc_missed_count
1475 jsr reset_points_level
1479 ldx #
0 ; default: 1st player
1483 ; in versus mode, hurt player is determined by lane
1484 lda targets_1.state
,y
1489 jsr sub_energy_with_pain
1491 ; turn off the sound channel
1492 jsr mixer_get_muted_channels
1494 jsr mixer_set_muted_channels
1496 @@move_to_missed_list:
1497 lda targets_2.next
,y
1500 sta targets_2.next
,y
1501 ldx missed_targets_tail
1502 sty missed_targets_tail
1505 sty missed_targets_head
1508 sta targets_2.next
,x
1511 cpy active_targets_tail
1513 stx active_targets_tail
1517 sty active_targets_head
1519 + sta targets_2.next
,x
1523 ; In: X = lane index
1525 .
proc player_for_lane
1534 ; X = player (0 or 1)
1535 .
proc deal_error_pain
1542 jsr sub_energy_with_pain
1550 ; process_active_targets() helper function.
1551 ; Explodes active targets that were hit and moves them to the hit list.
1552 .
proc sweep_active_targets
1555 ldy active_targets_head
1562 lda targets_1.state
,y
1568 lda targets_2.next
,y
1579 ; type determines what happens
1580 lda targets_1.state
,y
1595 beq
@@hit_fake_skull
1598 inc player.skull_hit_count
1599 ; TODO - in boss mode, always subtract from 2nd player (boss), and shake screen
1601 lda targets_1.state
,y
1606 jsr sub_energy_with_pain
1612 inc player.pow_hit_count
1613 jsr sync_powable_lanes
1618 jmp pow_active_targets
1621 inc player.star_hit_count
1630 jmp @@explode_in_place
1633 inc player.clock_hit_count
1638 jsr toggle_time_mode
1641 + jmp @@explode_in_place
1644 ldx player.letter_index
1646 bcs
+ ; it's really an error in the mapping, there shouldn't be more than eight
1648 ora player.acquired_letters
1649 sta player.acquired_letters
1650 inc player.letter_index
1658 + jmp @@explode_in_place
1661 inc player.fake_skull_hit_count
1666 jmp @@explode_in_place
1669 ; TODO - in boss mode, hitting normal targets is bad
1671 jsr on_normal_target_hit
1672 cpy lane_switcher_offset
1675 sta lane_switcher_offset
1676 jsr switch_player_lane_mapping
1677 jmp @@explode_in_place
1679 cmp #
2 ; 2 player versus?
1680 beq
@@explode_in_place ; if so, no hearts ever spawned
1681 lda targets_1.state
,y
1684 jsr maybe_spawn_heart
1688 sta targets_1.pos_y
,y
1693 ; Y = offset of POW target
1694 .
proc sync_powable_lanes
1698 ; in 1-player and co-op, all lanes are POWable
1703 ; in versus, only the lanes of the player that
1704 ; hit the POW are POWable
1705 + lda targets_1.state
,y
1714 ; sync attributes based on player 1 mapping
1715 ; (mapped to player 1 is blue, otherwise red)
1717 .
proc sync_normal_target_attributes
1721 and #
1 : eor #
1 : cmp #
1 : rol ; 0 or 3
1722 sta normal_target_attributes
,x
1731 .
proc switch_player_lane_mapping
1733 ; "random" switch (makes it very difficult)
1743 lda
@@player_1_lanes,x
1749 + sta player_lanes
+0
1750 lda
@@player_2_lanes,x
1768 jsr sync_normal_target_attributes
1793 .
proc on_normal_target_hit
1794 lda player.energy_level
1796 rts
; dead, don't care
1797 ; turn on the sound channel
1798 + jsr mixer_get_muted_channels
1800 jsr mixer_set_muted_channels
1805 lda player.current_streak
+0
1816 lda player.points_level
1831 ; Explodes target with offset in Y.
1832 ; Variable prev must contain the offset of the previous target,
1833 ; or $FF if it's the head of the list.
1835 .
proc explode_target
1836 lda targets_1.state
,y
1837 ora #
$80 ; bit 7 indicates that the target is exploded (used by draw routine)
1838 and #~
$38 ; clear type bits
1839 sta targets_1.state
,y
1841 lda targets_2.next
,y
1844 sta targets_2.next
,y
1845 ldx hit_targets_tail
1846 sty hit_targets_tail
1849 sty hit_targets_head
1852 sta targets_2.next
,x
1855 cpy active_targets_tail
1857 stx active_targets_tail
1861 sty active_targets_head
1863 + sta targets_2.next
,x
1867 .
proc pow_active_targets
1870 ldy active_targets_head
1872 cpy #
$FF ; end of list?
1875 + lda targets_1.state
,y
1879 ; don't pow letters and fake skulls
1881 lda targets_2.next
,y
1887 cpy lane_switcher_offset
1889 lda targets_1.state
,y
1895 jsr on_normal_target_hit
1896 + jsr explode_target
1897 ; Y contains next pointer
1902 ; Y = offset of target to draw
1905 jsr next_sprite_index
1907 lda targets_1.pos_x.
int,y
1909 lda targets_1.pos_y.
int,y
1911 lda targets_1.state
,y
1912 bpl
+ ; jump if not exploding
1914 lsr
: lsr
; explosion frame * 4
1917 + lda targets_1.state
,y
1919 cmp #
$28 ; special orb?
1925 lda targets_1.pos_y.
int,y
; sprite ("size") is determined by Y
1927 sbc #
48 ; initial Y pos
1937 ++ sta sprites.tile
,x
1940 lda targets_1.state
,y
1941 bmi
+ ; jump if exploding
1944 ; for special types, the attributes are determined by the type
1949 lda
@@special_type_attributes-1,y
1951 ; for normal targets, the attributes are determined by lane number
1952 ; -- unless it's a lane switch indicator, which is always 2
1953 + cpy lane_switcher_offset
1957 + lda targets_1.state
,y
1960 lda normal_target_attributes
,y
1961 ++ sta sprites.attr
,x
1965 jsr next_sprite_index
1967 lda targets_1.pos_x.
int,y
1971 lda targets_1.pos_y.
int,y
1973 lda targets_1.state
,y
1974 bpl
+ ; jump if not exploding
1976 lsr
: lsr
; explosion frame * 4
1979 + lda targets_1.state
,y
1981 cmp #
$28 ; special orb?
1987 lda targets_1.pos_y.
int,y
; sprite ("size") is determined by Y
1989 sbc #
48 ; initial Y position
1999 ++ sta sprites.tile
,x
2002 lda targets_1.state
,y
2003 bmi
+ ; jump if exploding
2006 ; for special types, the attributes are determined by the type
2011 lda
@@special_type_attributes-1,y
2013 ; for normal targets, the attributes are determined by lane number
2014 ; -- unless it's a lane switch indicator, which is always 2
2015 + cpy lane_switcher_offset
2019 + lda targets_1.state
,y
2022 lda normal_target_attributes
,y
2023 ++ sta sprites.attr
,x
2028 @@special_type_attributes:
2034 .
db $80 |
0 ; fake skull
2038 ; Y = offset of target to move
2043 ; temporarily divide speed by two
2044 lda targets_2.speed_y.
int,y
2047 sta targets_2.speed_y.
int,y
2048 lda targets_2.speed_y.frac
,y
2050 sta targets_2.speed_y.frac
,y
2051 lda targets_2.speed_x.
int,y
2054 sta targets_2.speed_x.
int,y
2055 lda targets_2.speed_x.frac
,y
2057 sta targets_2.speed_x.frac
,y
2060 + lda targets_1.pos_y.frac
,y
2062 adc targets_2.speed_y.frac
,y
2063 sta targets_1.pos_y.frac
,y
2064 lda targets_1.pos_y.
int,y
2065 adc targets_2.speed_y.
int,y
2066 sta targets_1.pos_y.
int,y
2068 lda targets_1.pos_x.frac
,y
2070 adc targets_2.speed_x.frac
,y
2071 sta targets_1.pos_x.frac
,y
2072 lda targets_1.pos_x.
int,y
2073 adc targets_2.speed_x.
int,y
2074 sta targets_1.pos_x.
int,y
2078 ; restore speed (multiply by 2)
2079 lda targets_2.speed_y.frac
,y
2081 sta targets_2.speed_y.frac
,y
2082 lda targets_2.speed_y.
int,y
2084 sta targets_2.speed_y.
int,y
2085 lda targets_2.speed_x.frac
,y
2087 sta targets_2.speed_x.frac
,y
2088 lda targets_2.speed_x.
int,y
2090 sta targets_2.speed_x.
int,y
2094 .
proc process_hit_targets
2097 ldy hit_targets_head
2106 lda targets_1.state
,y
2108 cmp #
$70 ; reached last frame?
2110 lda targets_1.state
,y
2112 adc #
$10 ; advance to next frame
2113 sta targets_1.state
,y
2114 + lda targets_2.next
,y
2119 lda targets_2.next
,y
2122 lda free_targets_list
2123 sta targets_2.next
,y
2124 sty free_targets_list
2126 ; remove from hit targets list
2127 cpy hit_targets_tail
2129 sta hit_targets_tail
2134 sty hit_targets_head
2136 + sta targets_2.next
,x
2140 .
proc process_missed_targets
2143 ldy missed_targets_head
2151 lda targets_1.pos_y.
int,y
2154 lda targets_2.next
,y
2160 lda targets_2.next
,y
2163 lda free_targets_list
2164 sta targets_2.next
,y
2165 sty free_targets_list
2167 ; remove from missed targets list
2168 cpy missed_targets_tail
2170 sta missed_targets_tail
2175 sty missed_targets_head
2177 + sta targets_2.next
,x
2182 inc player.hit_count
+0
2184 inc player.hit_count
+1
2188 .
proc inc_missed_count
2189 inc player.missed_count
+0
2191 inc player.missed_count
+1
2195 .
proc inc_error_count
2196 inc player.err_count
+0
2198 inc player.err_count
+1
2203 inc player.current_streak
+0
2205 inc player.current_streak
+1
2208 beq
+ ; no points level
2209 lda player.current_streak
+1
2211 lda player.current_streak
+0
2213 beq
@@inc_points_level
2215 beq
@@inc_points_level
2219 inc player.points_level
2223 + jmp sync_longest_streak
2226 .
proc sync_longest_streak
2227 lda player.longest_streak
+0
2229 sbc player.current_streak
+0
2230 lda player.longest_streak
+1
2231 sbc player.current_streak
+1
2233 ; new longest streak
2234 lda player.current_streak
+0
2235 sta player.longest_streak
+0
2236 lda player.current_streak
+1
2237 sta player.longest_streak
+1
2241 .
proc reset_points_level
2243 sta player.points_level
2252 sta player.current_streak
+0
2253 sta player.current_streak
+1
2273 ; X = player (0 or 1)
2278 adc player.energy_level
,x
2280 lda player.energy_level
,x
2286 sta player.energy_level
,x
2294 ; X = player (0 or 1)
2295 .
proc sub_energy_with_pain
2298 sta damage_blink_counter
2301 jsr play_dmc_sample
; "ouch!"
2308 adc player.energy_level
2315 sta player.energy_level
2345 ; Adds number in A,Y to score.
2368 ++ lda #
$9F : sta player.score
+0
2369 lda #
$86 : sta player.score
+1
2370 lda #
$01 : sta player.score
+2
2379 ; beq + ; only sync the top score if we're in play mode
2382 ; ### possibly award extra life
2383 .ifndef NO_TOP_SCORE
2390 .ifndef NO_TOP_SCORE
2391 .
proc sync_top_score
2392 lda player.top_score
2395 lda player.top_score
+1
2397 lda player.top_score
+2
2402 sta player.top_score
2404 sta player.top_score
+1
2406 sta player.top_score
+2
2416 lda
@@sprite_data_offsets,y
2418 - lda
@@sprite_data+0,y
2421 + jsr next_sprite_index
2423 lda
@@sprite_data+0,y
2425 lda
@@sprite_data+1,y
2427 lda
@@sprite_data+2,y
2429 lda
@@sprite_data+3,y
2436 @@sprite_data_offsets:
2437 .
db @@l0-@@sprite_data
2438 .
db @@l1-@@sprite_data
2439 .
db @@l2-@@sprite_data
2440 .
db @@l3-@@sprite_data
2441 .
db @@l4-@@sprite_data
2442 .
db @@l5-@@sprite_data
2443 .
db @@l6-@@sprite_data
2444 .
db @@l7-@@sprite_data
2445 .
db @@l8-@@sprite_data
2446 .
db @@l9-@@sprite_data
2447 .
db @@l10-@@sprite_data
2448 .
db @@l11-@@sprite_data
2449 .
db @@l12-@@sprite_data
2452 .
db 170-99,$D7,0,194
2453 .
db 170-99,$D9,0,194+8
2454 .
db 170-99,$DB,0,194+16
2457 .
db 167-99,$DD,0,195
2458 .
db 167-99,$DF,0,195+8
2459 .
db 167-99,$E1,0,195+16
2462 .
db 163-99,$D1,0,198
2463 .
db 163-99,$D3,0,198+8
2464 .
db 163-99+8,$D5,0,198+16
2467 .
db 161-99,$E3,0,202
2468 .
db 161-99+8,$E5,0,202+8
2471 .
db 160-99,$E7,0,205
2472 .
db 160-99+16,$E9,0,205+8
2475 .
db 160-99,$EB,0,210
2476 .
db 160-99+16,$ED,0,210
2479 .
db 159-99,$EF,0,215
2480 .
db 159-99+16,$F1,0,215
2483 .
db 160-99,$EB,$40+0,210+3
2484 .
db 160-99+16,$ED,$40+0,210+3
2487 .
db 160-99,$E7,$40+0,205+13
2488 .
db 160-99+16,$E9,$40+0,205+13-8
2491 .
db 161-99,$E3,$40+0,202+19
2492 .
db 161-99+8,$E5,$40+0,202+19-8
2495 .
db 163-99,$D1,$40+0,198+27
2496 .
db 163-99,$D3,$40+0,198+27-8
2497 .
db 163-99+8,$D5,$40+0,198+27-16
2500 .
db 167-99,$DD,$40+0,195+33
2501 .
db 167-99,$DF,$40+0,195+33-8
2502 .
db 167-99,$E1,$40+0,195+33-16
2505 .
db 170-99,$D7,$40+0,196+33
2506 .
db 170-99,$D9,$40+0,196+33-8
2507 .
db 170-99,$DB,$40+0,196+33-16
2511 .
proc draw_points_level_indicator
2512 lda player.points_level
2515 lda
@@points_level_indicator_data_table+0,y
2517 lda
@@points_level_indicator_data_table+1,y
2521 jmp copy_bytes_to_ppu_buffer
2523 @@points_level_indicator_data_table:
2524 .
dw @@points_level_0_data
2525 .
dw @@points_level_1_data
2526 .
dw @@points_level_2_data
2527 .
dw @@points_level_3_data
2528 @@points_level_0_data:
2529 .
db $21,$7A,$02,$00,$00
2530 .
db $21,$9A,$02,$00,$00
2531 @@points_level_1_data:
2532 .
db $21,$7A,$02,$BC,$BE
2533 .
db $21,$9A,$02,$BD,$BF
2534 @@points_level_2_data:
2535 .
db $21,$7A,$02,$BC,$C0
2536 .
db $21,$9A,$02,$BD,$C1
2537 @@points_level_3_data:
2538 .
db $21,$7A,$02,$BC,$C2
2539 .
db $21,$9A,$02,$BD,$C3
2542 .
proc update_points_level_indicator
2550 jmp draw_points_level_indicator
2554 .
proc update_lives_display
2562 jmp print_life_count
2565 .
proc print_life_count
2566 ldy #
$20 : lda #
$68 : ldx #
1
2567 jsr begin_ppu_string
2568 lda player.life_count
2570 jsr put_ppu_string_byte
2575 .
proc update_score_displays
2579 .ifndef NO_TOP_SCORE
2581 bcs
@@update_top_score
2588 .ifndef NO_TOP_SCORE
2598 lda player.score
+0 : sta AC0
2599 lda player.score
+1 : sta AC1
2600 lda player.score
+2 : sta AC2
2601 ldx #
5 : lda #
$20 : ldy #
$4E
2605 .ifndef NO_TOP_SCORE
2606 .
proc print_top_score
2607 lda player.top_score
+0 : sta AC0
2608 lda player.top_score
+1 : sta AC1
2609 lda player.top_score
+2 : sta AC2
2610 ldx #
6 : lda #
$20 : ldy #
$59
2615 ; AC0, AC1, AC2 = value to print
2616 ; X = # of digits to output
2617 ; A = PPU high address
2618 ; Y = PPU low address
2621 ldx ppu_buffer_offset
2638 ; figure out how many digits to print
2665 .
db $0A,$64,$E8,$10,$A0,$40
2667 .
db $00,$00,$03,$27,$86,$42
2669 .
db $00,$00,$00,$00,$01,$0F
2682 - asl AC0
;DIVIDEND/2, CLEAR QUOTIENT BIT
2688 lda XTND0
;TRY SUBTRACTING DIVISOR
2697 bcc
+ ;TOO SMALL, QBIT=0
2698 stx XTND1
;OKAY, STORE REMAINDER
2702 inc AC0
;SET QUOTIENT BIT = 1
2712 .
proc update_progress_display
2732 sbc #
$40 ; 2 rows up
2735 jsr begin_ppu_string
2737 and #
1 ; odd or even
2740 jsr put_ppu_string_byte
2744 .
proc update_energy_display
2753 jsr update_player_energy_meter
2759 jmp update_player_energy_meter
2762 ; Y = player (0 or 1)
2763 .
proc update_player_energy_meter
2764 lda player.energy_level
,y
2765 cmp displayed_energy_level
,y
2768 lda player.energy_level
,y
2771 sbc displayed_energy_level
,y
2775 ; fill full hearts on the left
2776 ora #
$40 ; set RLE bit
2778 tya
: pha
; save player index
2779 lda displayed_energy_level
,y
2791 jsr begin_ppu_string
2792 lda #
$0A ; full heart
2793 jsr put_ppu_string_byte
2795 pla
: tay
; restore player index
2799 lda displayed_energy_level
,y
2806 sbc player.energy_level
,y
2810 ; fill empty hearts on the right
2811 ora #
$40 ; set RLE bit
2813 tya
: pha
; save player index
2814 lda player.energy_level
,y
2826 jsr begin_ppu_string
2827 lda #
$0C ; empty heart
2828 jsr put_ppu_string_byte
2830 pla
: tay
; restore player index
2833 tya
: pha
; save player index
2834 lda player.energy_level
,y
2844 + ldy #
$20 : ldx #
$01
2845 jsr begin_ppu_string
2846 pla
: tay
; restore player index
2847 pha
; save player index
2848 lda player.energy_level
,y
2849 ldy #
$0C ; empty heart
2857 jsr put_ppu_string_byte
2859 pla
: tay
; restore player index
2860 lda player.energy_level
,y
2861 sta displayed_energy_level
,y
2865 .
proc update_letters_display
2874 - ldy display_letter_index
2875 cpy player.letter_index
2884 inx
; for the hyphen
2886 jsr begin_ppu_string
2887 ldy display_letter_index
2891 jsr put_ppu_string_byte
2893 jsr put_ppu_string_byte
2895 inc display_letter_index
2901 .
db $99,$9A,$9B,$99,$9C,$9D,$9E,$9F
2906 and #JOYPAD_BUTTON_START
2912 jsr palette_to_temp_palette
2916 sta was_music_paused
2919 + jsr mixer_get_muted_channels
2920 sta saved_muted_channels
2922 jsr mixer_set_muted_channels
2928 sta selected_menu_item
2930 inc main_cycle
; game_paused_main
2931 pla
: pla
; skip the rest of the game loop!
2935 .
proc game_paused_main
2938 ; jsr draw_pause_menu
2939 ; jsr draw_all_targets
2940 jmp check_pause_input
2943 .
proc check_pause_input
2945 and #
(JOYPAD_BUTTON_START
) ; | JOYPAD_BUTTON_A)
2946 bne
@@select_menu_item
2950 and #
(JOYPAD_BUTTON_UP | JOYPAD_BUTTON_DOWN
)
2951 bne
@@change_menu_item
2958 and #JOYPAD_BUTTON_UP
2961 lda selected_menu_item
2964 inc selected_menu_item
2969 lda selected_menu_item
2971 dec selected_menu_item
2977 ldy selected_menu_item
2987 lda was_music_paused
2990 + lda saved_muted_channels
2991 jsr mixer_set_muted_channels
2992 dec main_cycle
; game main
2998 jsr mixer_set_muted_channels
3008 ldcay
@@really_restart
3009 jsr set_timer_callback
3015 jmp start_fade_to_black
3019 jsr mixer_set_muted_channels
3030 jsr set_timer_callback
3036 jmp start_fade_to_black
3053 .
proc draw_pause_menu
3068 + jsr next_sprite_index
3070 lda selected_menu_item
3076 ++ lda
@@text_data,y
3099 .charmap
"data/pausemenu.tbl"
3100 .char
"RESUME" : .
db 0
3101 .char
"RESTART" : .
db 0
3102 .char
"QUIT" : .
db 0
3109 lda active_targets_head
3113 + lda hit_targets_head
3117 + lda missed_targets_head
3121 + lda target_data.lo
3126 + jsr mixer_get_muted_channels
3128 jsr mixer_set_muted_channels
3130 sta saved_muted_channels
; in case we are in bullet time
3131 jsr set_pattern_row_callback
3135 ; make bullet time expire
3136 lda #
1 : sta bullet_timer
3143 ldy #
$20 : lda #
$2B : ldx #
$54
3144 jsr begin_ppu_string
3146 jsr put_ppu_string_byte
3148 lda target_data_chunk_length
3149 sta progress_countdown
3152 jsr init_target_data
3162 cmp #
15 ; all clips played?
3168 lda clips
+1,y
; marker
3169 jsr init_target_data
3171 jsr mixer_set_muted_channels
3175 + lda player.energy_level
3176 sta player.final_energy_level
3179 lda #
38 : ldy #
6 ; ### customize delay for song?
3181 ldcay
@@add_points_for_energy
3182 jsr set_timer_callback
3183 ; lda player.energy_level
3185 ; and #~3 ; ### CHECKME
3186 ; sta player.energy_level
3191 @@add_points_for_energy:
3193 jsr maybe_start_song
; mute
3194 lda player.energy_level
3195 beq
@@done_adding_points_for_energy
3205 ldcay
@@add_points_for_energy
3206 jmp set_timer_callback
3208 @@done_adding_points_for_energy:
3211 sta global_transpose
3213 jsr compute_completed_challenges
3214 sta player.last_completed_challenges
3216 ; calculate new completed challenges
3218 lda player.completed_challenges
,y
3219 eor player.last_completed_challenges
3220 and player.last_completed_challenges
3221 sta player.new_completed_challenges
3223 ; calculate earned credit
3224 lda player.new_completed_challenges
3226 stx player.won_credit
3232 jmp set_timer_callback
3237 ldcay
@@goto_stats_screen
3238 jsr set_timer_callback
3243 jmp start_fade_to_black
3245 @@goto_stats_screen:
3253 ; Computes the set of challenges that were completed, based
3254 ; on the stats gathered from the game.
3255 ; Returns: A = set of completed challenges (1-bit = completed, 0-bit = not completed)
3256 .
proc compute_completed_challenges
3258 ; 1. Make it to the end: unconditonal
3262 ; 2. High scorer: player.score > rock score
3264 lda player.difficulty
3265 asl
: asl
: asl
: asl
: asl
; 8*4=32 bytes per difficulty
3266 adc selected_song
; four
3267 adc selected_song
; bytes
3268 adc selected_song
; per
3269 adc selected_song
; score
3273 sbc rock_score_table
+0,y
3275 sbc rock_score_table
+1,y
3277 sbc rock_score_table
+2,y
3281 ; 3. Streaker: player.longest_streak > 100
3283 lda player.longest_streak
+0
3286 lda player.longest_streak
+1
3291 ; 4. Letters: player.acquired_letters == FF
3293 lda player.acquired_letters
3298 ; 5. 3 fake skulls: fake_skull_hit_count == 3
3300 lda player.fake_skull_hit_count
3305 ; 6. Blow up all POWs: pow_miss_count == 0
3307 lda player.pow_miss_count
3313 ; 7. No special items: pow_hit_count | star_hit_count | clock_hit_count == 0
3315 lda player.pow_hit_count
3316 ora player.star_hit_count
3317 ora player.clock_hit_count
3323 ; 8. finish with full energy: energy_level == ENERGY_MAX
3325 lda player.final_energy_level
3326 cmp #
(ENERGY_MAX
-2) ; -2 because that level is displayed as a full heart
3334 ; LED LOVE WHIP FREE DETH LIFE
3335 .
dd 30000,20000,20000,20000,25000,25000,20000,20000 ; easy
3336 .
dd 50000,25000,30000,30000,30000,40000,30000,30000 ; normal
3337 .
dd 50000,30000,40000,35000,50000,50000,50000,50000 ; hard
3340 .
proc game_done_main
3349 .
proc update_moving_background_step
3350 lda moving_bg_column
3354 sta moving_bg_column
3355 inc moving_bg_offset
3356 + inc moving_bg_column
3366 jsr begin_ppu_string
3367 lda moving_bg_offset
3370 jsr put_ppu_string_byte
3387 .
proc update_moving_background
3388 jsr update_moving_background_step
3389 jsr update_moving_background_step
3390 jmp update_moving_background_step
3393 .
proc update_damage_blink
3394 lda damage_blink_counter
3399 ; start the blink - set palette red
3400 dec damage_blink_counter
3401 ldy #
$3F : lda #
$00 : ldx #
$01
3402 jsr begin_ppu_string
3404 jsr put_ppu_string_byte
3406 + dec damage_blink_counter
3408 ; do nothing (palette remains red)
3410 ; end the blink - set palette black
3411 + ldy #
$3F : lda #
$00 : ldx #
$01
3412 jsr begin_ppu_string
3414 jsr put_ppu_string_byte
3418 ; X = lane to spawn in
3419 .
proc maybe_spawn_heart
3420 dec heart_spawn_counter
3423 + lda #HEART_SPAWN_INTERVAL
3424 sta heart_spawn_counter
3425 lda player.energy_level
3430 + inc player.heart_spawn_count
3433 sta spawned_heart_state
3443 .
proc update_spawned_heart
3444 lda spawned_heart_state
3455 + lda spawned_heart_state
3458 lda spawned_heart_state
3460 sta spawned_heart_state
3466 ora spawned_heart_state
3467 sta spawned_heart_state
3471 lda spawned_heart_state
3472 and #
$60 ; iteration
3474 lda spawned_heart_state
3476 sta spawned_heart_state
3479 adc #
$20 ; next iteration
3481 ora spawned_heart_state
3482 sta spawned_heart_state
3483 + lda spawned_heart_state
3489 sta spawned_heart_state
3493 .
proc draw_big_heart
3495 jsr next_sprite_index
3497 lda spawned_heart_state
3503 lda spawned_heart_state
3508 adc lane_slot_y_coords
,y
3512 lda lane_slot_x_coords
,y
3517 jsr next_sprite_index
3522 lda spawned_heart_state
3527 adc lane_slot_y_coords
,y
3531 lda lane_slot_x_coords
,y
3540 .
proc get_player_1_input
3542 sta lane_input_posedge
+0
3546 lda button_mapping
,x
3553 ora lane_input_posedge
+0
3554 sta lane_input_posedge
+0
3567 .
proc get_player_2_input
3569 sta lane_input_posedge
+1
3573 ; one-player, no points in reading 2nd input
3577 lda button_mapping
+5,x
3584 ora lane_input_posedge
+1
3585 sta lane_input_posedge
+1
3598 .
proc process_locked_lanes
3603 - lda bitmasktable
,x
3609 dec locked_lane_timers
,x
3621 .
proc process_lockable_lanes
3627 lda lane_input_posedge
+0
3628 ora lane_input_posedge
+1
3640 sta locked_lane_timers
,x
3642 pha
; save lane index
3647 cpx #LOCKED_LANES_MAX
3649 ; no more lanes can be locked
3653 tax
; restore lane index
3660 .
proc draw_lockable_lanes
3662 - lda bitmasktable
,x
3675 jsr draw_lane_indicator
3683 .
proc draw_locked_lanes
3689 lda locked_lane_timers
,x
3692 ; blink when it's about to time out
3700 jsr draw_lane_indicator
3709 .
proc draw_active_targets
3710 ldy active_targets_head
3712 cpy #
$FF ; end of list?
3715 lda targets_2.next
,y
3717 jmp draw_target_list
3722 .
proc draw_hit_targets
3723 ldy hit_targets_head
3724 jmp draw_target_list
3727 .
proc draw_missed_targets
3728 ldy missed_targets_head
3729 jmp draw_target_list
3732 .
proc process_bullet_time
3746 + jmp toggle_time_mode
3749 .
proc draw_all_targets
3750 jsr draw_active_targets
3751 jsr draw_hit_targets
3752 jmp draw_missed_targets
3756 .
proc draw_player_lane_indicator
3757 jsr next_sprite_index
3761 lda lane_slot_y_coords
,y
3764 lda lane_slot_x_coords
,y
3771 + sta sprites.attr
,x
3775 .
proc maybe_draw_player_lane_indicators
3777 bne
+ ; only if 2-player
3780 - jsr draw_player_lane_indicator
3787 lda player.energy_level
3791 ora player.energy_level
+1
3792 beq
@@die ; versus && player 2 health == 0
3799 ldcay
@@death_delay_done
3800 jsr set_timer_callback
3802 jsr mixer_set_muted_channels
3810 ldcay
@@fade_out_done
3811 jsr set_timer_callback
3814 jmp start_fade_to_black
3818 dec player.life_count
3821 dec main_cycle
; game_init
3828 sta global_transpose
3829 jsr start_song
; mute
3831 jsr mixer_set_muted_channels
3833 jsr set_pattern_row_callback
3837 lda #
12 ; game over init
3841 lda #
28 ; winner/loser screen
3846 .
proc update_screen_shake
3847 lda screen_shake_counter
3854 + dec screen_shake_counter
3855 lda screen_shake_counter
3861 ; A = number of frames to shake
3863 sta screen_shake_counter
3867 .
proc update_palette_for_time_mode
3868 jsr palette_fade_in_progress
3869 beq
+ ; only if there is no fading in progress
3871 + lda transition_timer
3876 bne
@@fade_towards_green
3878 ; fade towards game palette
3893 @@fade_towards_green:
3913 @@bullet_time_palette:
3915 .
db $0f,$1A,$19,$2A ; logo, hearts, VU
3916 .
db $0f,$1B,$12,$22 ; hearts
3917 .
db $0f,$2A,$3A,$19 ; points, progress bar
3918 .
db $0f,$39,$29,$09 ; pad, VU
3920 .
db $0f,$01,$21,$30 ; target - blue
3921 .
db $0f,$0B,$2B,$30 ; target - green
3922 .
db $0f,$07,$27,$30 ; target - yellow
3923 .
db $0f,$05,$15,$30 ; target - red
3927 .
proc set_palette_for_time_mode
3928 ; we need to fix up palette entry 5
3929 ; if we are in Versus mode...
3938 sta palette
+5 ; gray instead of dark green
3939 ldy #
$3F : lda #
$05 : ldx #
1
3940 jsr begin_ppu_string
3942 jsr put_ppu_string_byte
3947 jsr process_lockable_lanes
3948 jsr draw_lockable_lanes
3949 jsr process_locked_lanes
3950 jsr draw_locked_lanes
3951 jsr process_bullet_time
3952 jsr maybe_begin_song
3953 jsr maybe_load_targets
3954 jsr process_active_targets
3955 jsr process_hit_targets
3956 jsr process_missed_targets
3957 jsr update_score_displays
3958 jsr update_progress_display
3959 jsr update_energy_display
3960 jsr update_screen_shake
3962 jsr update_lives_display
3964 jsr update_moving_background
3965 jsr update_ampdisplay
3966 jsr update_damage_blink
3967 jsr update_spawned_heart
3968 jsr update_points_level_indicator
3969 jsr update_letters_display
3970 jsr maybe_draw_player_lane_indicators
3974 ;.define PLAYER_2_CPU
3976 .
proc lock_player_2_lanes
3982 sta locked_lane_timers
+0
3983 sta locked_lane_timers
+1
3984 sta locked_lane_timers
+2
3985 sta locked_lane_timers
+3
3986 sta locked_lane_timers
+4
3993 lda transition_timer
3994 bne
@@update_transition
3999 sta lane_input_posedge
+0
4000 sta lane_input_posedge
+1
4003 @@update_transition:
4004 jsr draw_all_targets
4005 jsr draw_locked_lanes
4007 jsr maybe_draw_player_lane_indicators
4008 jsr update_palette_for_time_mode
4009 dec transition_timer
4012 ; back to normal game mode
4013 + jsr set_palette_for_time_mode
4014 lda was_music_paused
4017 + lda saved_muted_channels
4018 jsr mixer_set_muted_channels
4022 sta bullet_timer
; how long bullet time shall last
4026 jsr get_player_1_input
4027 jsr get_player_2_input
4030 jsr lock_player_2_lanes
4032 jsr draw_pressed_lanes
4036 ; turn on for dynamic "profiling" (the screen goes black when processing is done)
4039 and #~PPU_CTRL1_BG_VISIBLE