@@ -204,6 +204,11 @@ GameLoop:
204
204
call Player1MoveByControls
205
205
call SnowballvsPlayerCollision
206
206
207
+ IF 0 ; DEBUG wait for fire key after frame
208
+ .waitForFire: call ReadInputDevices : ld a , (Player1Controls) : bit JOY_BIT_FIRE , a : jr z , .waitForFire
209
+ .waitForRelease: call ReadInputDevices : ld a , (Player1Controls) : bit JOY_BIT_FIRE , a : jr nz , .waitForRelease
210
+ ENDIF
211
+
207
212
; do the GameLoop infinitely
208
213
jr GameLoop
209
214
@@ -238,6 +243,8 @@ SnowballvsPlayerCollision:
238
243
; the collision detection will use circle formula (x*x+y*y=r*r), but we will first reject
239
244
; the fine-calculation by box-check, player must be +-15px (cetre vs centre) near ball
240
245
; to do the fine centres distance test (16*16=256 => overflow in the simplified MUL logic)
246
+ bit 7 , (ix + S_SPRITE_4B_ATTR.vp at )
247
+ jr z , .skipCollisionCheck ; ball is invisible, skip the test
241
248
; read and normalize snowball pos X
242
249
ld e , (ix + S_SPRITE_4B_ATTR.x)
243
250
ld d , (ix + S_SPRITE_4B_ATTR.mrx8)
@@ -315,6 +322,60 @@ SnowballvsPlayerCollision:
315
322
ld (ix + S_SPRITE_4B_ATTR.mrx8) , a
316
323
ret
317
324
325
+ ;-------------------------------------------------------------------------------------
326
+ ; Part 7 - platforms collisions
327
+ ; These don't check the image pixels, but instead there are few columns accross
328
+ ; the screen, and for each column there can be 8 platforms defined. These data are
329
+ ; hand-tuned for the background image. Each column is 16px wide, so there are 16 columns
330
+ ; per PAPER area. But the background is actually only 192x192 (12 columns), and I will
331
+ ; define +1 column extra on each side in case some sprite is partially out of screen.
332
+ ; Single column data is 16 bytes: 8x[height of platform, extras] where extras will be
333
+ ; 8bit flags for things like ladders/etc.
334
+
335
+ GetPlatformPosUnder:
336
+ ; In: IX = sprite pointer (centre x-coordinate is used for collision, i.e. +8)
337
+ ; Out: A = platform coordinate (in sprite coordinates 0..255), C = extras byte
338
+ ; for X coordinate outside of range, or very low Y the [A:255, C:0] is always returned
339
+ push hl
340
+ call .implementation
341
+ ; returns through here only when outside of range or no platform found
342
+ ld a , 255
343
+ ld c , 0
344
+ pop hl
345
+ ret
346
+ .implementation:
347
+ bit 0 , (ix + S_SPRITE_4B_ATTR.mrx8)
348
+ ret nz ; 256..511 is invalid X range (no column data)
349
+ ld a , (ix + S_SPRITE_4B_ATTR.x)
350
+ sub 16 - 8 ; -16 to skip 16px, +8 to test centre of sprite (not left edge)
351
+ ret c ; 0..7 is invalid X range (no column data)
352
+ ; each column is 8 platforms x2 bytes = 16 bytes -> the X coordinate top 4 bits
353
+ ; are indentical to address of particular column! (no need to multiply/divide)
354
+ cp low PlatformsCollisionDataEnd
355
+ ret nc ; 224 <= (X-16) -> invalid X range (224 = 14*16) - 14 columns are defined
356
+ and - 16 ; clear the bottom four bits of X -> becomes low-byte of address
357
+ ld l , a
358
+ ld h , high PlatformsCollisionData ; HL = address of column data
359
+ ld a , (ix + S_SPRITE_4B_ATTR.y) ; raw sprite Y (top edge)
360
+ cp 255 - 13
361
+ ret nc ; already too low to even check (after +13 only 13..254 are valid for check)
362
+ add a , 13 ; the base-line coordinate, the sprite can be 2px deep into platform to "hit" it
363
+ ; now we are ready to compare against the data in column table
364
+ jr .columnDataLoopEntry
365
+ .columnDataLoop:
366
+ inc l
367
+ inc l
368
+ .columnDataLoopEntry:
369
+ cp (hl)
370
+ jr nc , .columnDataLoop ; platformY <= spriteY_base_line -> will not catch this one
371
+ ; this platform is below baseline, report it as hit
372
+ ld a , (hl)
373
+ inc l
374
+ ld c , (hl)
375
+ pop hl ; discard return address from .implementation
376
+ pop hl ; restore HL
377
+ ret ; return directly to caller with results in A and C
378
+
318
379
;-------------------------------------------------------------------------------------
319
380
; "AI" subroutines
320
381
@@ -407,15 +468,40 @@ SnowballsAI:
407
468
ld de , S_SPRITE_4B_ATTR
408
469
ld b , SNOWBALLS_CNT - 1 ; move all of them except last
409
470
. loop :
471
+ bit 7 , (ix + S_SPRITE_4B_ATTR.vp at )
472
+ jr z , .doNextSnowball ; if the sprite is not visible, don't process it
473
+ ; check the Y coordinate against platform's collisions
474
+ call GetPlatformPosUnder
475
+ sub 16 ; snowball wants platform at +16 (bottom of ball is bottom of sprite)
476
+ cp (ix + S_SPRITE_4B_ATTR.y)
477
+ jr z , .isInOrAtPlatform ; at plaform
478
+ jr c , .isInOrAtPlatform ; in platform (will reset Y to be *at*)
479
+ ld l , a ; keep the platformY for compare
480
+ ; falling, keep the previous direction (extracted from mirroX flag into C=0/1)
481
+ ld c , 0
482
+ bit 3 , (ix + S_SPRITE_4B_ATTR.mrx8) ; mirroX bit
483
+ jr z , .fallingRight
484
+ inc c ; falling left
485
+ .fallingRight
486
+ ld a , (ix + S_SPRITE_4B_ATTR.y)
487
+ inc a ; Y += 1
488
+ cp l ; did it land at platform now?
489
+ adc a , 0 ; if not, do another Y += 1 (falling at +2 speed)
490
+ ; continue with "in/at platform" code, but keeps direction, sets new fall Y
491
+ .isInOrAtPlatform:
492
+ ld (ix + S_SPRITE_4B_ATTR.y) , a
493
+ ; check if the ball is not under the screen, make it invisible then (+simpler AI)
494
+ rl (ix + S_SPRITE_4B_ATTR.vp at ) ; throw away visibility flag
495
+ cp 192 + 32 ; Fc=1 if (Y < 192+32)
496
+ rr (ix + S_SPRITE_4B_ATTR.vp at ) ; set new visibility from carry
497
+ ; if at platform, choose direction by platform extras
498
+ rr c ; extras bit 0 to carry
499
+ sbc a , a ; 0 = right, $FF = left
410
500
; HL = current X coordinate (9 bit)
411
501
ld l , (ix + S_SPRITE_4B_ATTR.x)
412
502
ld h , (ix + S_SPRITE_4B_ATTR.mrx8)
413
- ; adjust it by some +- value deducted from B (32..1 index)
414
503
ld c , 0 ; mirrorX flag = 0
415
- ld a , b
416
- and 3 ; A = 0,1,2,3
417
- sli a ; A = 1, 3, 5, 7
418
- sub 4 ; A = -3, -1, +1, +3
504
+ sli a ; Right: A=+1 Fc=0 || Left: A=-1 Fc=1
419
505
; do: HL += signed(A) (the "add hl,a" is "unsigned", so extra jump+adjust needed)
420
506
jr nc , .moveRight
421
507
dec h
@@ -434,11 +520,11 @@ SnowballsAI:
434
520
ld a , (TotalFrames)
435
521
xor b
436
522
and 7
437
- jr nz , .notEightFrameYet
523
+ jr nz , .doNextSnowball
438
524
ld a , (ix + S_SPRITE_4B_ATTR.vp at )
439
525
xor 1
440
526
ld (ix + S_SPRITE_4B_ATTR.vp at ) , a
441
- .notEightFrameYet :
527
+ .doNextSnowball :
442
528
add ix , de ; next snowball
443
529
djnz . loop ; do all of them
444
530
ret
@@ -547,6 +633,44 @@ SprSnowballs: EQU Sprites + 0*S_SPRITE_4B_ATTR ; first snowball sprite
547
633
SprPlayer: EQU Sprites + SNOWBALLS_CNT * S_SPRITE_4B_ATTR ; player sprite is here
548
634
SprCollisionFx: EQU SprPlayer + S_SPRITE_4B_ATTR
549
635
636
+ ;-------------------------------------------------------------------------------------
637
+ ; Part 7 - platforms collisions - data of platforms and their heights + extras
638
+
639
+ ALIGN 256 ; align for simpler calculation of address
640
+ PlatformsCollisionData: ; the Y-coord of platforms are in sprite coordinates! (+32: 0..255)
641
+ ; extras:
642
+ ; bit 0: 0=slope to right, 1=slope to left (to affect snowballs movement)
643
+ ; bit 1: there's ladder near the platform
644
+ ; column 0 (sprite coordinates 16..31)
645
+ DB 75 , 0 , 127 , 0 , 185 , 0 , 222 , 1 , 255 , 0 , 255 , 0 , 255 , 0 , 255 , 0
646
+ ; column 1 (sprite coordinates 32..47)
647
+ DB 75 , 0 , 128 , 0 , 186 , 0 , 222 , 1 , 255 , 0 , 255 , 0 , 255 , 0 , 255 , 0
648
+ ; column 2 (sprite coordinates 48..)
649
+ DB 75 , 0 , 108 , 1 , 129 , 0 , 167 , 1 , 187 , 0 , 222 , 1 , 255 , 0 , 255 , 0
650
+ ; column 3 (sprite coordinates 64..)
651
+ DB 75 , 0 , 107 , 3 , 130 , 2 , 166 , 3 , 188 , 2 , 222 , 1 , 255 , 0 , 255 , 0
652
+ ; column 4 (sprite coordinates 80..)
653
+ DB 51 , 1 , 75 , 0 , 106 , 1 , 131 , 0 , 165 , 1 , 189 , 0 , 222 , 1 , 255 , 0
654
+ ; column 5 (sprite coordinates 96..)
655
+ DB 51 , 1 , 75 , 0 , 105 , 1 , 132 , 0 , 164 , 1 , 190 , 0 , 222 , 1 , 255 , 0
656
+ ; column 6 (sprite coordinates 112..)
657
+ DB 51 , 3 , 75 , 2 , 104 , 1 , 133 , 0 , 163 , 1 , 191 , 0 , 221 , 1 , 255 , 0
658
+ ; column 7 (sprite coordinates 128..)
659
+ DB 51 , 3 , 75 , 2 , 103 , 1 , 134 , 0 , 162 , 1 , 192 , 0 , 220 , 1 , 255 , 0
660
+ ; column 8 (sprite coordinates 144..)
661
+ DB 76 , 0 , 102 , 1 , 135 , 0 , 161 , 1 , 193 , 0 , 219 , 1 , 255 , 0 , 255 , 0
662
+ ; column 9 (sprite coordinates 160..)
663
+ DB 77 , 0 , 101 , 1 , 136 , 0 , 160 , 1 , 194 , 0 , 218 , 1 , 255 , 0 , 255 , 0
664
+ ; column 10 (sprite coordinates 176..)
665
+ DB 78 , 2 , 100 , 3 , 137 , 2 , 159 , 3 , 195 , 2 , 217 , 3 , 255 , 0 , 255 , 0
666
+ ; column 11 (sprite coordinates 192..)
667
+ DB 79 , 0 , 99 , 1 , 138 , 0 , 158 , 1 , 196 , 0 , 216 , 1 , 255 , 0 , 255 , 0
668
+ ; column 12 (sprite coordinates 208..)
669
+ DB 98 , 1 , 157 , 1 , 215 , 1 , 255 , 0 , 255 , 0 , 255 , 0 , 255 , 0 , 255 , 0
670
+ ; column 13 (sprite coordinates 224..)
671
+ DB 97 , 1 , 156 , 1 , 214 , 1 , 255 , 0 , 255 , 0 , 255 , 0 , 255 , 0 , 255 , 0
672
+ PlatformsCollisionDataEnd:
673
+
550
674
;-------------------------------------------------------------------------------------
551
675
; reserve area for stack at $B800..$BFFF region
552
676
ORG $ B800
0 commit comments