-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfalling.z80
667 lines (606 loc) · 29.9 KB
/
falling.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
END_OF_LIST: equ 255
DEAD_ENTRY: equ 254
falling_tiles:
defs 80 + 2 ; worst case
defb END_OF_LIST
falling_tiles_tail: defw falling_tiles
push_falling_tiles:
; modifies: HL, A
; a contains tile x and y (YYYYxxxx)
ld hl, (falling_tiles_tail)
ld (hl), a
inc hl
ld (hl), END_OF_LIST ; end marker
ld (falling_tiles_tail), hl
ret
clear_falling_tiles:
; modifies: HL
ld hl, falling_tiles
ld (hl), END_OF_LIST ; end marker
ld (falling_tiles_tail), hl
ret
queue_tiles_to_check:
defs 80 + 2
defb END_OF_LIST
queue_tiles_to_check_tail: defw queue_tiles_to_check
clear_queue_tiles_to_check:
; modifies: HL
ld hl, queue_tiles_to_check
ld (hl), END_OF_LIST
ld (queue_tiles_to_check_tail), hl
ret
queue_check_falling_tile_l:
; modifies: HL, A
ld a, l
ld hl, (queue_tiles_to_check_tail)
ld (hl), a
inc hl
ld (hl), END_OF_LIST
ld (queue_tiles_to_check_tail), hl
ret
check_falling_tile_hl:
; check a tile. adds it to the list of 'falling' tiles if it is falling
; Call this whenever a tile has been moved, including for tiles above
; the tile-that-was-moved.
; Handles checks for tiles that end up on-a-slider.
; modifies: HL, A
; is the tile pointed (in tiles array) by hl, suspended in midair
; if so, update the state to say it is falling (and add it to the
; list of falling tiles)
; QUESTION: WHAT TO DO ABOUT SLIDERS?
; A: 1. if the block currently in midair actually has a slider
; (or, the top of the slider stack) immediately below it
; then add this tile to the top of the slider stack.
; This means the tile actually somewhat magically jumps down
; onto the top of the stack, rather than falling gracefully,
; but that's what the original game seems to really do, so I'm
; just replicating that
; 2. if there is a slider but not *immediately* below this tile,
; then allow the tile to fall. We will probably end up
; calling this same routine again later.
;
; first checks - is this even a tile? if not (weird), do nothing
ld a, (hl)
cp NUM_NON_TILES
ret c
; actually minimize how many checks we do here, since we'll do them
; all again/anyway inside update_falling_tiles
; and I want to avoid having two copies of all the landing/slider/etc logic!
ld a, IS_FALLING_MASK ; bit 4 = falling
dec h ; (point to tiles_flags)
ld (hl), a
ld a, l
jp push_falling_tiles
check_falling_tiles_queue:
ld bc, queue_tiles_to_check
@next_queue:
ld a, (bc)
cp 255
jr z, @+DONE
ld l, a
ld h, tiles/256
call check_falling_tile_hl
inc bc
jp @next_queue
@DONE:
jp clear_queue_tiles_to_check
; use a list to say which tiles "have fallen" this frame, keeping a record of their
; previous screen memory location. This means we can efficiently blank out their previous
; position (just need to blank out the 2-pixel rows from where they used to be higher-up-the-screen)
; and overwrite their new location, rather than needing to blank-and-redraw the whole thing
; You could (and I did) consider putting the update-and-redraw into a single routing but I wanted
; to split the update- from the redraw- and this seemed like the easiest way:
; 1. Put falling tiles into linked list
; 2. For each falling tile, work out its screen location
; 3. Update the y-location of the falling tiles. if the y-location changed, put the OLD
; y-location into the 'falling_rects_to_clear' list
; 4. go through the list in 3. and clear out on-screen 2 pixel rows
; for all the falling tiles that moved
; 5. (after doing any other on-screen erasing/updating/etc) draw the tiles in new location
; This is actually kindof equivalent to putting the "erase" into the "update" logic,
; while still having separate 'draw', which is obviously better than putting "update and erase and draw"
; into a single blob of code that's harder to interleave with other drawing operations
falling_rects_to_clear: defs 80
falling_rects_to_clear_tail: defw falling_rects_to_clear
temp_screen_de: defw 0 ; we use this while processing each falling tile to remember "where it was" on screen, for blanking purposes
update_falling_tiles:
; step through list of falling tiles (x,y) coords
; and update the falling state / land them on solid blocks / land them on sliders
ld hl, falling_tiles
@keep_seeing_if_any_found:
ld a, (hl)
; is it end marker?
cp END_OF_LIST
jr z, @none_found_and_cleanup
; is it a dead entry (something was falling, and has stopped now,
; but we've still got other falling things in this list)
cp DEAD_ENTRY
jr nz, @+not_dead
inc hl
jr @-keep_seeing_if_any_found
@none_found_and_cleanup:
; on this pass we went through the whole list of falling tiles and found none
; Great , list can be truncated
jp clear_falling_tiles ; this RETs
@redraw_tile_and_next:
; blank old
ld de, (temp_screen_de)
push hl
call defer_fillrect_16x16_black_at_de
pop hl
push hl
; get new tile location
ld c, (hl)
ld b, tiles_flags/256
ld a, (bc)
ld b, a
call grid_xy_in_C_flags_in_B_to_screen_de
; then get new tile type
ld c, (hl)
ld b, tiles/256
ld a, (bc)
; queue this up for drawing
call defer_draw_tile_at_de_tile_a
pop hl
inc hl
; fall through to @_next_fall
@_next_fall:
ld a, (hl)
; is it end marker?
cp END_OF_LIST
ret z ; DONE
; is it a dead entry
cp DEAD_ENTRY
jr nz, @+not_dead
inc hl
jp @_next_fall
@make_dead_entry:
ld (hl), DEAD_ENTRY
inc hl
jp @_next_fall
@falling_now_stacked:
; little routine used inline in falling code,
; for when a tile lands on the slider stack.
; updates all the slider stack properties.
; Assumes you have already set the tiles' flags (slider and yoffset/xoffset) appropriately
; since that's quite case-dependent and doesn't happen here
; blank old and draw new
; (slider routine won't blank out an old tile, but will handle blanking while the tile is part of the slider stack)
; (since the "update_falling_tiles" routine is after the slider routine, we need to draw the tile in the new position here)
push hl
ld de, (temp_screen_de)
call defer_fillrect_16x16_black_at_de
; get new tile location
ld c, (hl)
ld b, tiles_flags/256
ld a, (bc)
ld b, a
call grid_xy_in_C_flags_in_B_to_screen_de
; then get new tile type
ld c, (hl)
ld b, tiles/256
ld a, (bc)
; queue this up for drawing
call defer_draw_tile_at_de_tile_a
pop hl
; update slider properties. tile has now stopped falling
ld a, (slider_stack_height)
inc a
ld (slider_stack_height), a
ld a, (screen_y_top_of_slider_stack+1) ; z80 little endian. want D not E.
sub 8
ld (screen_y_top_of_slider_stack+1), a
jp @make_dead_entry
@not_dead:
; a = YYYYxxxx
; for screen: multiply both x and y by 8 (and add GRID_X_OFFSET in x direction)
; and put into de
; for tiles: a is (conveniently) already the low byte offset into table
; so we can just put a into c (since bc is aligned to 256-bytes)
;
; if this is already sliding, skip entirely the falling checks
; because it's ON THE SLIDER ALREADY (this can happen when moving a tile onto a slider
; if (for any reason) that tile is also marked as one to check for falling tiles)
ld c, a
ld b, tiles_flags/256
ld a, (bc)
bit IS_SLIDER_BIT, a
ret nz
; calc DE screen location of the where the tile is now (i.e. where it 'was', if it moves during this iteration)
; (later code will trigger the blanking of this screen location, "if the tile moved")
push bc
ld b, a
call grid_xy_in_C_flags_in_B_to_screen_de
ld (temp_screen_de), de
pop bc
; otherwise:
; 1. tile is in midair (yoffset != 0, on entry here, so tile is between grid-aligned cells)
; 1a. if level has no vertical slider, this tile must simply be falling, so let it fall.
; After falling 2 more pixels, it might NOW be grid aligned, so do the "grid aligned checks"
; 1b. if level has vertical slider, it might be that the vslider is directly below us
; (i.e. partially in the same cell that we are also partially in. this can only happen if the slider
; rose by one pixel as we fell by 2 pixels, entering the same cell, or if the slider fell by
; one pixel previously and now we fell by 2 pixels and entered the same cell)
; 1b1. If the vslider is directly below us and rising, we hop on top
; After falling 2 more pixels, it might NOW be grid aligned, so do the "grid aligned checks"
;
; 2. tile is already grid-aligned (yoffset == 0, on entry here, and might still be falling, or might have now landed on something)
; Note that the tile probably would have landed on something on the previous frame, if yoffset is already 0 here, but that's not
; always the case e.g. maybe a new tile got shoved underneath, or an hslider rolled underneath, or indeed
; this could be one of those 'suspended in mid air as a vslider comes up below it'
;
;
; pseudocode:
;
; if yoffset == 0:
; [CASES A]
; if solid ground or tile directly under us, stop falling
; if level has vslider and slider is (partially in the) cell immediately below us:
; if slider is rising, then our tile just floats here 'still falling' but not moving. Exhibit: 4:18 @ https://www.youtube.com/watch?v=Wpy5U2_O55E
; if slider is falling, then hop on. Exhibit: 3:40 @ https://www.youtube.com/watch?v=Wpy5U2_O55E
; if level has vslider, and slider is exactly touching our tile, hop onto the slider.
; if level has hslider and slider is exactly or partially touching the cell below our tile:
; if slider is exactly touching our tile, hop onto the slider.
; if slider is slightly left of our tile, and we have space to hop left, hop left (onto the slider)
; if slider is slightly right of our tile, and we have space to hop right, hop right (onto the slider)
;
; else if yoffset != 0:
; [CASES B]
; if level has no vslider, then just fall [*NOTE!]
; else: check if vslider is in the same cell as the (bottom of) this tile. if so, hop on top
; [*NOTE] if yoffset is now == 0 after the fall, nothing new happens, but we'll
; do all those other checks (from the yoffset==0 cases above) NEXT TIME
; EXHIBIT: 4:40 @ https://www.youtube.com/watch?v=Wpy5U2_O55E the green pipe lands off-kilter on the slider for
; one frame, and hops left on the next frame
ld a, (bc)
and 15
jp z, @grid_aligned_fall ; [CASES A]
; [CASES B]
; midair: check vslider
ld a, (slider_info)
and a
jp z, @midfall_finished_vslider_checks
bit SLIDER_AXIS_BIT, a
jp nz, @midfall_finished_vslider_checks ; i.e. if hslider, don't do vslider check
@midfall_do_vslider_checks:
; check if the slider is immediately below us
; if so we hop on top (and then we're done).
ld a, c
add 16
ld c, a
ld a, (bc)
bit IS_SLIDER_BIT, a
jp z, @midfall_finished_vslider_checks ; not a slider below, so, proceed with the other basic falling checks
; vslider immediately below
; copy the slider bit and offset to our tile, and mark this tile as no longer falling
; The yoffset of the slider, and the yoffset of our tile, now being the same, means
; the tiles will appear as stacked
; no need to check/change captured cursor, since we only track the cursor xy
; and since the tile xy didn't change (only yoffset) then cursor xy doesn't change either
ld d, a
ld a, c
sub 16
ld c, a
ld a, d
ld b, tiles_flags/256
ld (bc), a
jp @falling_now_stacked
@midfall_finished_vslider_checks:
; so just some plain old falling then
; (because various codepaths end up here, we're not sure what bc is really pointing to, so reload b and c)
ld c, (hl)
ld b, tiles_flags/256
ld a, (bc)
; fall down by 2 pixels:
and 15
add a, 2
cp 16 ; are we now 16 pixels lower?
jp nc, @midfall_fall_now_grid_aligned ; if yes, this is where we move the whole thing one row lower
; otherwise we're just a few pixels lower, update our state
; in TILES FLAGS, and update our new DE location (to draw the tile in new location)
or IS_FALLING_MASK ; keep the flag that says 'falling' set
ld (bc), a ; write its new y offset back
jp @redraw_tile_and_next
@midfall_fall_now_grid_aligned:
; We fell 16 pixels
; This is where we move the whole thing one row lower
; (and reset the falling yoffset back to zero)
; Move the cursor too, if it is captured
; (it feels like this logic is in totally the wrong place)
ld a, (controller_flags_tile_captured)
and a
jr z, @done_checking_captured_tile
ld a, (controller_flags_xy_pos)
cp (hl)
jr nz, @done_checking_captured_tile
; this IS the captured tile
; move the cursor to this new spot
add 16 ; add 1 to Y
ld (controller_flags_xy_pos), a
@done_checking_captured_tile:
; this xy now has zero offset (but don't clear falling flag)
xor a
ld (bc), a
; 4a. if there's a tile above, it is now falling
ld a, c
sub 16 ; check ONE ROW UP
ld c, a
inc b ; point to tiles
ld a, (bc)
dec b ; point back to tile flags TODO OPTIMIZE, WE INC THEN DEC THEN INC AGAIN
cp NUM_NON_TILES
jr c, @finished_checking_falling_above ; jump if tile above was a non-moveable-tile
; tile above me can now fall into empty space, great!
ld a, IS_FALLING_MASK ; falling but zero offset for now ; TODO IS THIS RIGHT?
ld (bc), a
; point at the xy coord of the above tile and add to the falling list
; hl is still valid, but we can trash it here (we pop later when we need it)
ld a, c
push hl
call push_falling_tiles
pop hl
@finished_checking_falling_above:
; update state
; (1c we need to put tile A into the new xy)
; now go back down one row (to where we are/were)
ld a, c
add 16
ld c, a
; 1b. move the actual tile from previous xy to new xy
inc b ; point to tiles
ld a, (bc)
ld d, a ; REMEMBER THIS FOR LATER JUST BELOW. D = THE OLD TILE
xor a ; space is now blank
ld (bc), a
ld a, c
add 16 ; this is the new location of the tile
ld c, a
ld (hl), a ; take the opportunity to also update the coords in the falling_list (it's still falling, we want to find this tile next iteration)
ld a, d ; recover the tile from just earlier
ld (bc) , a ; write the old tile to the NEW space
; finally update the flags for the NEW space, mark as zero offset and (still) falling
dec b
ld a, IS_FALLING_MASK+0
ld (bc), a
; finally, we need to draw this tile in its new location
; this uses C and A, both of which are set correctly at this point
jp @redraw_tile_and_next
@grid_aligned_fall:
; [CASES A]
; [A1] if solid ground or tile directly under us, stop falling
; [A2] if level has vslider and slider is (partially in the) cell immediately below us:
; [A2_a] if slider is rising, then our tile just floats here 'still falling' but not moving. Exhibit: 4:18 @ https://www.youtube.com/watch?v=Wpy5U2_O55E
; [A2_b] if slider is falling, then hop on. Exhibit: 3:40 @ https://www.youtube.com/watch?v=Wpy5U2_O55E
; [A3] if level has vslider, and slider is exactly touching our tile, hop onto the slider.
; [A4] if level has hslider and slider is exactly or partially touching the cell below our tile:
; [A4_a] if slider is exactly touching our tile, hop onto the slider.
; [A4_b] if slider is slightly left of our tile, and we have space to hop left, hop left (onto the slider)
; [A4_c] if slider is slightly right of our tile, and we have space to hop right, hop right (onto the slider)
ld a, (slider_info)
and a
jr nz, @_slider_checks
; no slider, so check case [A1] only
@_basic_check:
; [A1] - check TILES of one row below
push bc
ld a, c
add 16
ld c, a
inc b
ld a, (bc)
pop bc
and a
jr z, @_basic_check_ok_to_keep_falling
; otherwise it's dead i.e. comes to rest
xor a
ld (bc), a ; FLAGS of our falling tile (now set to zero)
jp @make_dead_entry
@_basic_check_ok_to_keep_falling:
; Increase the yoffset (tile flags) by 2
ld a, IS_FALLING_MASK + 2
ld (bc), a
jp @redraw_tile_and_next
@_slider_checks:
bit SLIDER_AXIS_BIT, a
jp z, @_gridalign_fall_vert_check
; else, this level must have a horizontal slider
@_gridalign_fall_horiz_check:
fish1:
; [A4] - check FLAGS of one row below, and the cell next to it (to its left)
push bc ; remember where our tile is
ld a, c
add 16
ld c, a
ld a, (bc)
bit IS_SLIDER_BIT, a
jr nz, @_horiz_slider_here_below
dec c
ld a, (bc)
bit IS_SLIDER_BIT, a
jr z, @_no_horiz_slider_here ; literally no slider
and 15
jr z, @_no_horiz_slider_here ; there is a slider but it's tucked away with xoffset=0 so there's space for us to fall
@_horiz_slider_here_below_left:
fish2:
; horiz slider (below us) is just to our left
; do we have space there to hop left?
pop bc
dec c
inc b ; TILES
ld a, (bc)
and a
jr z, @_hop_left
; otherwise we just stay here waiting
inc hl ; point to next entry in falling tiles for @next loop
jp @_next_fall
@_hop_left:
; (when hopping left, both the xoffset and the x-coord changes)
; meaning we need to copy over the tile and the flags to the new xy
; AND do the checks for the captured cursor)
ld a, (controller_flags_tile_captured)
and a
jr z, @hop_left_done_checking_captured_tile
ld a, (controller_flags_xy_pos)
cp (hl)
jr nz, @hop_left_done_checking_captured_tile
; this IS the captured tile
; move the cursor to this new spot
dec a ; subtract 1 from X
ld (controller_flags_xy_pos), a
@hop_left_done_checking_captured_tile:
; 4a. if there's a tile above, it is now falling
; bc still points to TILES but for the cell to the LEFT of where we currently are
ld a, c
sub 15 ; check ONE ROW UP and BACK ONE CELL RIGHT i.e. one row directly above our current cell
ld c, a
ld a, (bc)
dec b ; point back to flags TODO OPTIMIZE, WE INC THEN DEC THEN INC AGAIN
cp NUM_NON_TILES
jr c, @hop_left_finished_checking_falling_above ; jump if tile above was a non-moveable-tile
; tile above me can now fall into empty space, great!
ld a, IS_FALLING_MASK ; falling but zero offset for now ; TODO IS THIS RIGHT?
ld (bc), a
; point at the xy coord of the above tile and add to the falling list
; hl is still valid, but we can trash it here (we pop later when we need it)
ld a, c
push hl
call push_falling_tiles
pop hl
@hop_left_finished_checking_falling_above:
; update state
; (1c we need to put tile A into the new xy)
; now go back down one row (to where we are/were)
ld a, c
add 16
ld c, a
; zero the flags of the old location
xor a
ld (bc), a
; 1b. move the actual tile from previous xy to new xy
inc b ; point to tiles
ld a, (bc)
ld d, a ; REMEMBER THIS FOR LATER JUST BELOW. D = THE OLD TILE
xor a ; space is now blank
ld (bc), a
dec c ; this is the new location of the tile
ld (hl), c ; take the opportunity to also update the coords in the falling_list (it's still falling, we want to find this tile next iteration)
ld a, d ; recover the tile from just earlier
ld (bc) , a ; write the old tile to the NEW space
; finally update the flags for the NEW space, set equal to the flags of the slider
dec b ; flags
push bc
ld a, c
add 16
ld c, a
ld a, (bc)
pop bc
ld (bc), a
; finally, add this tile in its new location to the draw list and update the slider stack
jp @falling_now_stacked
@_no_horiz_slider_here:
; proceed with basic checks
pop bc
jp @_basic_check
@_horiz_slider_here_below:
fish3:
; check case [A4_a]
and 15
jr nz, @_horiz_A4_c
@_horiz_A4_a:
fish5:
pop bc
ld a, IS_SLIDER_MASK + 0
ld (bc), a
jp @falling_now_stacked
@_horiz_A4_c:
; horiz slider (below us) is just to our right
; do we have space there to hop right?
ld d, a ; a = the xoffset from the slider. hang on to this for later
pop bc
inc c
inc b ; TILES
ld a, (bc)
and a
jr z, @_hop_right
; otherwise we just stay here waiting
inc hl ; point to next entry in falling tiles for @next loop
jp @_next_fall
@_hop_right:
; (when hopping right, it's just the xoffset that changes)
; no need to update controller captured tile since xy doesn't change
dec b ; FLAGS
dec c ; back to where our tile is
ld a, d
or IS_SLIDER_MASK
ld (bc), a
; 4a. if there's a tile above, it is now falling
ld a, c
sub 16 ; check ONE ROW UP
ld c, a
inc b ; point to tiles
ld a, (bc)
dec b ; point back to tile flags TODO OPTIMIZE, WE INC THEN DEC THEN INC AGAIN
cp NUM_NON_TILES
jr c, @hop_right_finished_checking_falling_above ; jump if tile above was a non-moveable-tile
; tile above me can now fall into empty space, great!
ld a, IS_FALLING_MASK ; falling but zero offset for now ; TODO IS THIS RIGHT?
ld (bc), a
; point at the xy coord of the above tile and add to the falling list
; hl is still valid, but we can trash it here (we pop later when we need it)
ld a, c
push hl
call push_falling_tiles
pop hl
@hop_right_finished_checking_falling_above:
jp @falling_now_stacked
@_gridalign_fall_vert_check:
; [CASE A3]
; is the tile immediately below us a slider?
; if so hop on
push bc ; remember where our tile is. BC points to FLAGS
ld a, c
add 16
ld c, a
ld a, (bc)
bit IS_SLIDER_BIT, a
jr z, @_gridalign_fall_vert_check_no_slider
; is it a slider and perfect aligned?
and 15
jr z, @_gridalign_fall_vert_check_hop_on
; otherwise, it's a slider, not perfectly aligned.
; if it's rising, we stay still
; if it's falling, we hop on
ld a, (slider_info)
; which direction are we going?
; (=0: y is decreasing=UP, =1: y is increasing=DOWN)
bit SLIDER_DIRECTION_BIT, a
jr nz, @_gridalign_fall_vert_check_hop_on
; else, y is decreasing i.e. slider going up.
; we just wait still
pop bc
inc hl
jp @_next_fall
@_gridalign_fall_vert_check_hop_on:
; copy the slider yoffset and flags to our tile
ld a, (bc)
pop bc
ld (bc), a
jp @falling_now_stacked
@_gridalign_fall_vert_check_no_slider:
pop bc
jp @_basic_check
any_tiles_falling:
; modifies HL, A
; Z = nothing falling, NZ = something falling
ld hl, falling_tiles
@keep_seeing_if_any_found:
ld a, (hl)
; is it a valid xy coord?
; END_OF_LIST means end-of-list and
; DEAD_ENTRY means a-dead-entry-you-can-skip
cp END_OF_LIST
ret z ; end of list, nothing found, so nothing in freefall
cp DEAD_ENTRY
ret nz ; != dead_entry, so a real xy => something in freefall
inc hl
jr @-keep_seeing_if_any_found