-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmatch.z80
732 lines (663 loc) · 30.6 KB
/
match.z80
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
check_match:
; look for any and all matches
; NB if ANY tiles are in freefall, do NOT look for any matches
; because, for gameplay reasons, we only look for matches when
; all tiles are on firm ground (although this includes sliders)
; NB if we're already doing a match destruction animation,
; don't go checking for matches at the same time, that's pointless
; (and it would reset our destruction animation frame counter and
; result in an infinite loop. and that's bitter truth).
ld a, (destruct_frame)
and a
ret nz
xor a
ld (num_matches), a
; any tiles in freefall? if so, we shouldn't run the
; match checker (it waits until all blocks have stopped falling)
call any_tiles_falling ; Z = nothing falling, NZ = something falling
ret nz
; look for matches
; ALGORITHM:
; temporarily copy the grid into work area.
; ADD the tiles from sliders IF (and only if) the sliders are at an aligned edge
;
; starting top left, working left to right and top to bottom
; 1. find a tile.
; 2. set this tile's location's value to zero in the (copy of the) grid
; 3. look at the (up-to) four possible neighbours to see if they match
; this tile. If they do then:
; 3a. set this neighbour locaton value to zero in the (copy of the) grid
; 3b. put the original tile location xy AND this neighbour tile location
; into MATCH LIST
; 3c. put this neighbour tile location xy into the SEARCH STACK
; 3c. put any of the other (up to three) possible neighbours we didn't look
; at yet into the SEARCH STACK too.
; 3d. (although we don't need to put the last one into the SEARCH STACK because
; of the very next step where we take it off the SEARCH STACK, so can optimise
; by just keeping this last one in hand)
; 4. pick (and remove) an item from SEARCH STACK.
; 4a. if it's value is same as tile, then
; put THIS item into MATCH LIST too
; and set its location's value to zero in the (copy of the) grid
; and put its four possible neighbours into SEARCH STACK
; OR,
; 4b. if its value is not the same as tile then just throw this item away
; and look at the next item in SEARCH STACK (noting step 5)
; 5. continue to step 4 until the SEARCH STACK is empty
; 6. if the MATCH LIST has more than one thing in it, congrats, you have a match
; - get ready to create a new match list for the next tile
; or get ready to just reuse it if the match list had only one thing in it
; TODO - COULD ALSO CREATE A SEPARATE GRID THAT JUST CONTAINS FLAG INDICATING
; WHETHER THE TILE HAS MOVED SINCE LAST TIME WE CHECKED FOR MATCHES?
; BECAUSE THE ONLY TILES WE NEED TO CHECK (in step 1) ARE THE ONES THAT HAVE MOVED...!
; we know the border is impenetrable, start lower
; +17 because we know we don't care about the first row or column
ld hl, tiles+17
ld de, match_tiles_copy+17
ld bc, 16*12-17-16 ; exclude edges
ldir
; if sliders are mid-moving (not aligned to grid) then disregard those
; coordinates for match
; todo - surely we can make this more efficient than copying data and then removing it again later!
ld a, (slider_info)
and a
jr z, @done_sliders
ld a, (slider_xy)
ld h, tiles_flags/256
ld l, a
ld a, (hl)
and 15
; if this is non-zero then the slider is mid-move
; only if this is zero should we include the slider and stack
; TODO OPTIMIZE surely we can look this up in a single test
jr z, @done_sliders
; ok now we gotta zero out the slider and everything on top of it
; from the match check algo
ld a, (slider_stack_height)
inc a ; include the slider tile itself ; TODO optimize could just unroll a bit
ld b, a
ld h, match_tiles_copy/256
ld a, (slider_xy)
ld l, a
ld d, 0
@more_on_stack:
ld (hl), d
sub 16
ld l, a
djnz @-more_on_stack
@done_sliders:
; strategy: use ix to index into tiles array
; we'll also use bc to do the scanning (top to bottom
; and left to right)
ld bc, match_tiles_copy+17
ld de, match_list
ld hl, search_stack-1
; ensure the match list is terminated
ld a, 255
ld (de), a
; look for a valid tile to start the match checking
@keep_looking:
ld a, (bc)
cp 255 ; END MARKER
jp z, @DONE
cp NUM_NON_TILES
jr nc, @this_is_a_tile
inc bc
jr @-keep_looking
@this_is_a_tile:
; see if ANY neighbours match this tile.
; if not, we can outright ignore this as a starting point.
; BC is where we're at in the left-to-right-top-to-bottom-scan
; we'll hang onto that in bc for the entire duration of the algo
; but we'll use IX for indexing operations (starting with what
; BC is pointing to). So, start by putting BC into IX:
push bc
pop ix
; check neighbours east, south
; skip west and north because (since we've already been both west of here
; and north of here) we've already looked at those!
cp (ix+1)
jr z, @match_1
cp (ix+16)
jp z, @match_2
; cp (ix-1)
; jp z, @match_3
; cp (ix-16)
; jp z, @match_4
@nomatch:
; mark tile as done (to avoid "re-finding" it on later iterations)
xor a
ld (ix), a
inc ix
inc bc
jp @keep_looking
@match_1:
; keep a record of what tile we're looking for, since we trash reg A
ld (matched_tile), a
; make match list entry
ld (de), a ; headed with the number of the tile
inc de
ld a, ixl
ld (de), a ; and its location
ld (ix), 0 ; mark tile as done
inc de
inc a
ld (de), a ; and its neighbours location
ld (ix+1), 0 ; mark tile as done
inc de
inc hl
ld (hl), a ; and put its neighbours location into search stack
; now see if we should add remaining neighbours to search stack
ld a, (matched_tile)
@add_neighbours_1:
cp (ix+16)
jr nz, @+add_neighbours_2
ld (ix+16), 0 ; mark as done
ld a, ixl
add a, 16
ld (de), a ; add to match list
inc de
inc hl ; push onto search stack
ld (hl), a
ld a, (matched_tile)
@add_neighbours_2:
cp (ix-1)
jr nz, @+add_neighbours_3
ld (ix-1), 0 ; mark as done
ld a, ixl
dec a
ld (de), a ; add to match list
inc de
ld (hl), a
inc hl ; push onto search stack
ld a, (matched_tile)
@add_neighbours_3:
cp (ix-16)
jr nz, @next_item_from_search_stack
ld (ix-16), 0 ; mark as done
ld a, ixl
sub 16
ld (de), a ; add to match list
inc de
; ld (hl), a
; inc hl ; push onto search stack
; don't need to actually write this to the search stack
; since we're just going to take it off the search stack
; right now anyway
@search_from_here:
; a is now the index into tiles we want to continue the search from
; hl points to the top entry in the search stack (i.e. we've already popped a)
; the tile at location ix+0 is already in the match list at this point
ld ixl, a
; find any more matching neighbours
ld a, (matched_tile)
cp (ix+1)
jr nz, @add_neighbours_1
ld a, ixl
inc a
ld (de), a ; add to match list
inc de
inc hl
ld (hl), a
ld (ix+1), 0 ; mark tile as done
ld a, (matched_tile)
jp @add_neighbours_1
@next_item_from_search_stack:
; are we already past the end of the stack (i.e. stack is empty)?
ld a, (hl)
cp 255
jr z, @+done_searching ; TODO OPTIMISE just jump to keep_looking?
dec hl ; pop off the search stack
jp @-search_from_here
@done_searching:
; nothing else in the search stack starting from where we began
; (which was pointed to by BC)
; so resume the search from there
; we found all the matches here. 'terminate' the match list and
; get ready to find any more matches
ld a, 255
ld (de), a
inc de
inc bc
jp @-keep_looking
@match_2:
; keep a record of what tile we're looking for, since we trash reg a
ld (matched_tile), a
; make match list entry
ld (de), a ; headed with the number of the tile
inc de
ld a, ixl
ld (de), a ; and its location
ld (ix), 0 ; mark tile as done
inc de
add a, 16
ld (de), a ; and its neighbours location
ld (ix+16), 0 ; mark tile as done
inc de
inc hl
ld (hl), a ; and put its neighbours location into search stack
; now add remaining neighbours to search stack
ld a, (matched_tile)
jp @add_neighbours_2
; @match_3:
; ; keep a record of what tile we're looking for, since we trash reg a
; ld (matched_tile), a
; ; make match list entry
; ld (de), a ; headed with the number of the tile
; inc de
; ld a, ixl
; ld (de), a ; and its location
; ld (ix), 0 ; mark tile as done
; inc de
; dec a
; ld (de), a ; and its neighbours location
; ld (ix-1), 0 ; mark tile as done
; inc de
; inc hl
; ld (hl), a ; and put its neighbours location into search stack
; ; now add remaining neighbours to search stack
; ld a, (matched_tile)
; jp @add_neighbours_3
; @match_4:
; ; keep a record of what tile we're looking for, since we trash reg a
; ld (matched_tile), a
; ; make match list entry
; ld (de), a ; headed with the number of the tile
; inc de
; ld a, ixl
; ld (de), a ; and its location
; ld (ix), 0 ; mark tile as done
; inc de
; sub 16
; ld (de), a ; and its neighbours location
; ld (ix-16), 0 ; mark tile as done
; inc de
; ; we've already looked at all the other neighbours of ix+0
; ; and this was the only one that matched, so carry on from here
; jp @search_from_here
@DONE:
; phew!
; mark the tail of the match list, and count the number of matches
ld a, 255
ld (de), a
ld (match_list_tail), de
; find matches
ld a, (match_list)
cp 255
jr nz, @+we_found_some_matches; if the start of the list is the end, there's no matches
; we found no matches. we're done looking for matches.
; so, unlock the controller and reinitialize the bonus tier
ld a, (controller_flags_disabled)
and a
ret z; already unlocked
;
; SOMETHING IS WRONG, SHOULD I INCLUDE THIS HERE? YES? NO? CONFUSED.
call init_bonus ; TODO OPTIMIZE inline
call unlock_controller; TODO OPTIMIZE inline, also jp instead of call/ret?
;
ret
@we_found_some_matches:
; woohoo we found a match
ld a, 1
ld (destruct_frame), a
call @calculate_bonus_score
; TODO SPECIAL TREATMENT FOR MATCHES THAT INVOLVED TILES ON SLIDERS!
; If any tiles on the slider are involved in match, you need to
; 1. mark that tile as no longer on the slider
; 2. mark all other tiles ABOVE THAT TILE as also no longer on the slider
; 3. leave all other tiles below that tile alone BUT set the slider_stack_height
; to correspond to how many tiles are now remaining on the slider still
; this should all be fairly easy since we easily know the slider xy and the stack
; height and all the xy of tiles in the match, so can just compare match_x=slider_x and
; match_y between (slider_y-1) and (slider_y-stack_height)
; ....
; is controller already locked (i.e. this is a subsequent triggered match?)
ld a, (controller_flags_disabled)
and a
jr nz, @+already_locked ; TODO OPTIMIZE just ret nz?
; controller was not locked
; so: lock the controller, but also indicate that we're now
; starting a bonus calculation from the lowest tier (the tier
; is sticky but resets when all triggered matches have finished)
call lock_controller ; TODO OPTIMIZE inline
@already_locked:
ret
render_destruct:
; this will call decrement on remaining blocks upon completion
; this is the sequence
; 1. calculate bonus score amount
; 2. lock (i.e. disable) controller
; 3. flashing tiles on off: 4 frames on, 4 frames off (total 32 frames)
; 4. trigger a bonus overlay if necessary
; 5. render an explosion/shattering animation (total 24 frames)
; 6. remove the tiles from the game and playfield and update "remaining tiles"
; (6b. also triggers a falling process for tiles above the ones removed)
; 7. re-enable controller
; 8. if all tiles have been destroyed, start the 'CLEAR!' puzzle complete process
; (this will happen in the game loop though, not happen here)
;
; If nothing is destructed, do nothing (easy)
ld a, (destruct_frame)
and a
ret z
; what phase are we at?
; nb increment first; so really A *was* 1 (for frame1), but now it's 2
; counting from 1:
; 1-32: flashing tiles (i.e. phase_1)
; 33-56: shatter (handled by phase_2)
; 57-60: blank space (tile should have disappeared) (also handled by phase_2)
; but also:
; 1 : calculate bonus score (done elsewhere)
; 40 : trigger the popup of the "BONUS" overlay *IF APPLICABLE*
; 60 : update the score (using the score calculated in step 1)
; so, Using the above frame numbers, but add 1, you'll see the CP instructions below
inc a
ld (destruct_frame), a
cp 34
jp c, @phase_1 ; A is < 34 (so frame is < 33 i.e. frame <= 32)
cp 41
jr nz, @+not_frame40 ; A != 41 (so frame != 40)
; A == 41 (so frame == 40)
call z, trigger_bonus_overlay ; call not jump, because we also need to do phase2 stuff
ld a, (destruct_frame) ; since calling trigger_bonus_overlay blows away A
@not_frame40:
cp 62
jp c, @phase_2 ; A < 62 (A <= 61 so frame <= 60 i.e. frame is 33-60 inclusive)
; shouldn't get here...
ret
; phase 0
@calculate_bonus_score:
; count number of matches (the bonus logic needs to know)
; as well as the size of the match (the score and bonus logic both
; need this)
ld hl, match_list
ld de, 0 ; some counters for matches used by the bonus calculation
ld a, 255 ; we're just looking for end markers
@next_match_found:
inc hl ; skip the tile type, we don't care here
ld c, 2 ; initialize count of tiles here. we know there's at least two
inc hl ; .. so skip the first two tiles
inc hl
cp (hl) ; end marker?
jr z, @+finished_counting_this_match
@next_this_match:
; keep counting
inc hl
inc c
cp (hl)
jr nz, @-next_this_match
; b now holds the number of tiles in the match
; convert to score: 2-match is 100 points, 3-match is 200 points.
; no idea what 4-match and above might be, so I'll just say 4-match
; is 300 points, etc etc.
; this implementation assumes you can't get more than a 9-match!
@finished_counting_this_match:
; count total number of tiles matched since that drives one of the bonus
; calculations
; also count total number of matches since that drives another of the bonus
; calculations
; TODO honestly I don't think I need d for anything, it seem e is sufficient
ld a, e
add c
ld e, a ; e = total number of tiles matched
inc d ; d = total number of simultaneous matches
ld a, 255 ; get 255 back into a for next iteration
; is this now the end of the match list?
inc hl
cp (hl) ; i.e. compare (hl) with 255 still
jr nz, @-next_match_found
; now here, we've got:
; d = number of simultaneous matches
; e = number of tiles matches simultaneously
; choose the appropriate sound effect based on how many tiles matched
ld a, e
cp 3
jr c, @regular_match_sfx
jr z, @match_3_sfx
; 4 or more tiles, nice work
call trigger_sfx_destruction_4
jp @done_choosing_sfx
@match_3_sfx:
call trigger_sfx_destruction_3
jp @done_choosing_sfx
@regular_match_sfx:
call trigger_sfx_destruction ; TODO OPTIMIZE jp instead of call+ret
; fallthrough
@done_choosing_sfx:
; and calculate the bonus amount (so we can display the bonus overlay,
; as necessary)
call calculate_bonus_de
ret
@phase_1:
; in phase 1, the tiles flash (alternate between "a blank tile with
; no shape on it" and "the tile with the shape on it" every 4 frames)
ld hl, match_list
@next_match_in_phase_1:
sub 2 ; see comment below!
; this is spaghetti... on entry, A is equal to the (1-based) frame PLUS 1
; or, if you like, the (0-based) frame PLUS 2!
; if you want to do math on the frame number, fix it to be 0-based first
and 4
ld b, a
; set b to the tile: either the actual tile (=a) or the blank tile (=4)
; based on the alternating bit in b
bit 2, b
ld b, 4 ; DOES NOT AFFECT FLAGS
jr z, @+tile_anim_chosen
ld b, (hl) ; this is the tile type
@tile_anim_chosen:
@next_this_match:
inc hl
ld a, (hl) ; this is the offset into tiles
cp 255
jr z, @+END_THIS_MATCH
; convert xy into screen de
ld c, a
call grid_xy_in_C_and_A_to_screen_de
ld a, b ; the actual tile to draw
push hl
push bc
call draw_tile_at_de_tile_a
pop bc
pop hl
jp @-next_this_match
@END_THIS_MATCH:
inc hl
ld a, (hl) ; this is the tile type
cp 255
jr z, @+END_ALL_MATCHES
ld a, (destruct_frame)
jp @-next_match_in_phase_1
@END_ALL_MATCHES:
; done
ret
@phase_2:
; in phase 2, we show a sequence of frames of a 'crashing' animation
ld hl, match_list
@next_match_in_phase_2:
; set b to the tile: either the empty space (=0)
; or one of the destruction animations
; based on the destruct_frame
; Timings (based on 1-based frame numbers):
; 33-40 : blank tile i.e. the tile that looks like a tile but has no face picture
; 41-48 : frame1
; 49-52 : frame2
; 53-56 : frame3
; 57+ : none (empty space)
ld b, 4; blank tile
cp 42
jr c, @+tile_anim_chosen ; A < 42 (so frame < 41 i.e. frame is 33-40)
ld b, 0 ; no tile
cp 58
jr nc, @+tile_anim_chosen ; A >= 58 (so frame >= 57)
ld b, 13 ; frame1
cp 50
jr c, @+tile_anim_chosen ; A < 50 (so frame < 49 i.e. frame is 41-48)
ld b, 14 ; frame2
cp 54
jr c, @+tile_anim_chosen ; A < 54 (so frame < 53 i.e. frame is 49-52)
; otherwise 53+
ld b, 15 ; frame3
@tile_anim_chosen:
@next_this_match:
inc hl
ld a, (hl) ; this is the offset into tiles
cp 255
jr z, @+END_THIS_MATCH
; convert xy into screen de
ld c, a
call grid_xy_in_C_and_A_to_screen_de
ld a, b ; the actual tile to draw
push hl
push bc
call draw_tile_at_de_tile_a
pop bc
pop hl
jp @-next_this_match
@END_THIS_MATCH:
inc hl
ld a, (hl) ; this is the tile type
cp 255
jr z, @+END_ALL_MATCHES
ld a, (destruct_frame)
jp @-next_match_in_phase_2
@END_ALL_MATCHES:
; done
ret
check_final_steps_after_destruct:
; figure out the very last bits of state changing that happens
; at the end of a successful destruction sequence
; In particular
; (1) updating the score
; (2) stopping the destruction sequence (because it's finished)
ld a, (destruct_frame)
cp 60 ; A=60 (frame 59 done, frame 60 not yet rendered)
jr z, @+frame_60
cp 61 ; A=60 (frame 60 done, so finish up everything)
jr z, @+after_frame_60
ret
@frame_60:
; on frame 60 - final steps (6, 6b, 7, 8)
; we add the score
ld bc, (bonus_score)
call add_score
ret
@after_frame_60:
; disable the destruct animation
; that's finished playing out now (although a bonus overlay
; might still be happening, but that's a separate routine)
xor a
ld (destruct_frame), a
call clear_falling_tiles ; TODO is this necessary? it should already be empty
; loop thru matches decrementing remaining tiles
ld de, match_list
ld h, remaining_tiles/256 ; div
ld a, (de) ; this is the tile type
sub NUM_NON_TILES+1 ; a is now 0-indexed
@start_new_match:
add remaining_tiles\256 ; mod
ld l, a ; hl now points to the correct element in the remaining_tiles array
@next_this_match:
inc de
ld a, (de) ; this is the offset into tiles
cp 255
jr z, @+END_THIS_MATCH
; definitely a real tile. we don't care about it's coordinates
; we just care that it exists in the list of tiles to be blown up.
; So we can decrement our count of remaining tiles.
dec (hl)
jp @-next_this_match
@END_THIS_MATCH:
inc de
ld a, (de) ; this is the tile type
cp 255
jr z, @+finished_counting_remaining_tiles
sub NUM_NON_TILES+1 ; a is now 0-indexed
jp @-start_new_match
@finished_counting_remaining_tiles:
; trigger a redraw of remaining tiles
ld a, (redraw_remaining_tiles_flags)
or REMAINING_REDRAW_TRIGGER
ld (redraw_remaining_tiles_flags), a
; loop thru matches working out the falling changes etc
ld h, tiles/256
ld de, match_list
ld a, (de) ; this is the tile type
@next_this_match:
inc de
ld a, (de) ; this is the offset into tiles
cp 255
jr z, @+END_THIS_MATCH
ld l, a
xor a
ld (hl), a ; set tile to blank
dec h ; point to flags
ld (hl), a ; set flags to zero
inc h ; point back to tiles
; mark this xy as dirty (to redraw this xy as blank)
push hl
call push_dirty_tile
pop hl
; (if any of the tiles being matched are actually the one the cursor
; is locked to, clear the 'tile_captured' flag. We don't actually
; need to test the flag first, just clear it if we need to clear it.)
ld a, (controller_flags_xy_pos)
cp l
jr nz, @+not_captured
xor a
ld (controller_flags_tile_captured), a
@not_captured:
; and make the stuff above it fall
push hl
ld a, l
sub 16
ld l, a
; (but check if the stuff above is a tile... if it's
; not a tile, there's nothing to check, so don't)
ld a, (hl)
cp NUM_NON_TILES
jr c, @not_a_tile_above
call queue_check_falling_tile_l
@not_a_tile_above:
pop hl
jp @-next_this_match
@END_THIS_MATCH:
inc de
ld a, (de) ; this is the tile type
cp 255
jr z, @+END_ALL_MATCHES
jp @-next_this_match
@END_ALL_MATCHES:
; done with final sequence
; anything falling? if so, controller is still locked!!!
; we check that later, after checking the falling tiles queue
; BUT last thing we should do is recalculate the stack on top of the slider
; (because some of the above matched tiles might have been on the slider)
; We have at this point zero'd out those tiles, so it
; should be easy enough just to recalculate the slider stack 'from first principles'
call reinit_slider_stack
ret
check_controller_unlock:
ld a, (controller_flags_disabled)
and a
ret z ; not locked so nothing to do
; controller is locked. check if we're in the middle of a destruction animation
ld a, (destruct_frame)
and a
ret nz
; check if any tiles still falling. IF NOT, UNLOCK CONTROLLER
ld a, (falling_tiles) ; head
inc a ; cp 255
ret nz ; a != 255 therefore things are still falling therefore keep controller LOCKED
call unlock_controller
call init_bonus ; TODO OPTIMIZE inline ; may not be necessary to call this at all here?
ret
; match state
matched_tile: defb 0
ds ALIGN 256
match_tiles_copy: defs 16*12
end_marker: defb 255 ; this serves as both the end marker for the tiles (going up) AND the end marker for the search_stack (going down)
search_stack: defs 80 ; grows upwards
match_list: defs 8*10/2*4 ; extreme worst case. 8x10 tiles, of pairs, with 4 bytes per match (i.e. tile id, two xys, and a 255 terminator)
match_list_tail: defw 0
num_matches: defb 0