Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pret
GitHub Repository: pret/pokered
Path: blob/master/engine/overworld/movement.asm
2019 views
1
DEF MAP_TILESET_SIZE EQU $60
2
3
UpdatePlayerSprite:
4
ld a, [wSpritePlayerStateData2WalkAnimationCounter]
5
and a
6
jr z, .checkIfTextBoxInFrontOfSprite
7
cp $ff
8
jr z, .disableSprite
9
dec a
10
ld [wSpritePlayerStateData2WalkAnimationCounter], a
11
jr .disableSprite
12
; check if a text box is in front of the sprite by checking if the lower left
13
; background tile the sprite is standing on is greater than $5F, which is
14
; the maximum number for map tiles
15
.checkIfTextBoxInFrontOfSprite
16
lda_coord 8, 9
17
ldh [hTilePlayerStandingOn], a
18
cp MAP_TILESET_SIZE
19
jr c, .lowerLeftTileIsMapTile
20
.disableSprite
21
ld a, $ff
22
ld [wSpritePlayerStateData1ImageIndex], a
23
ret
24
.lowerLeftTileIsMapTile
25
call DetectCollisionBetweenSprites
26
ld h, HIGH(wSpriteStateData1)
27
ld a, [wWalkCounter]
28
and a
29
jr nz, .moving
30
ld a, [wPlayerMovingDirection]
31
; check if down
32
bit PLAYER_DIR_BIT_DOWN, a
33
jr z, .checkIfUp
34
xor a ; ld a, SPRITE_FACING_DOWN
35
jr .next
36
.checkIfUp
37
bit PLAYER_DIR_BIT_UP, a
38
jr z, .checkIfLeft
39
ld a, SPRITE_FACING_UP
40
jr .next
41
.checkIfLeft
42
bit PLAYER_DIR_BIT_LEFT, a
43
jr z, .checkIfRight
44
ld a, SPRITE_FACING_LEFT
45
jr .next
46
.checkIfRight
47
bit PLAYER_DIR_BIT_RIGHT, a
48
jr z, .notMoving
49
ld a, SPRITE_FACING_RIGHT
50
jr .next
51
.notMoving
52
; zero the animation counters
53
xor a
54
ld [wSpritePlayerStateData1IntraAnimFrameCounter], a
55
ld [wSpritePlayerStateData1AnimFrameCounter], a
56
jr .calcImageIndex
57
.next
58
ld [wSpritePlayerStateData1FacingDirection], a
59
ld a, [wFontLoaded]
60
bit BIT_FONT_LOADED, a
61
jr nz, .notMoving
62
.moving
63
ld a, [wMovementFlags]
64
bit BIT_SPINNING, a
65
jr nz, .skipSpriteAnim
66
ldh a, [hCurrentSpriteOffset]
67
add $7
68
ld l, a
69
ld a, [hl]
70
inc a
71
ld [hl], a
72
cp 4
73
jr nz, .calcImageIndex
74
xor a
75
ld [hl], a
76
inc hl
77
ld a, [hl]
78
inc a
79
and $3
80
ld [hl], a
81
.calcImageIndex
82
ld a, [wSpritePlayerStateData1AnimFrameCounter]
83
ld b, a
84
ld a, [wSpritePlayerStateData1FacingDirection]
85
add b
86
ld [wSpritePlayerStateData1ImageIndex], a
87
.skipSpriteAnim
88
; If the player is standing on a grass tile, make the player's sprite have
89
; lower priority than the background so that it's partially obscured by the
90
; grass. Only the lower half of the sprite is permitted to have the priority
91
; bit set by later logic.
92
ldh a, [hTilePlayerStandingOn]
93
ld c, a
94
ld a, [wGrassTile]
95
cp c
96
ld a, 0
97
jr nz, .next2
98
ld a, OAM_PRIO
99
.next2
100
ld [wSpritePlayerStateData2GrassPriority], a
101
ret
102
103
UnusedReadSpriteDataFunction:
104
push bc
105
push af
106
ldh a, [hCurrentSpriteOffset]
107
ld c, a
108
pop af
109
add c
110
ld l, a
111
pop bc
112
ret
113
114
UpdateNPCSprite:
115
ldh a, [hCurrentSpriteOffset]
116
swap a
117
dec a
118
add a
119
ld hl, wMapSpriteData
120
add l
121
ld l, a
122
ld a, [hl] ; read movement byte 2
123
ld [wCurSpriteMovement2], a
124
ld h, HIGH(wSpriteStateData1)
125
ldh a, [hCurrentSpriteOffset]
126
ld l, a
127
inc l
128
ld a, [hl] ; x#SPRITESTATEDATA1_MOVEMENTSTATUS
129
and a
130
jp z, InitializeSpriteStatus
131
call CheckSpriteAvailability
132
ret c ; don't do anything if sprite is invisible
133
ld h, HIGH(wSpriteStateData1)
134
ldh a, [hCurrentSpriteOffset]
135
ld l, a
136
inc l
137
ld a, [hl] ; x#SPRITESTATEDATA1_MOVEMENTSTATUS
138
bit BIT_FACE_PLAYER, a
139
jp nz, MakeNPCFacePlayer
140
ld b, a
141
ld a, [wFontLoaded]
142
bit BIT_FONT_LOADED, a
143
jp nz, NotYetMoving
144
ld a, b
145
cp $2
146
jp z, UpdateSpriteMovementDelay ; [x#SPRITESTATEDATA1_MOVEMENTSTATUS] == 2
147
cp $3
148
jp z, UpdateSpriteInWalkingAnimation ; [x#SPRITESTATEDATA1_MOVEMENTSTATUS] == 3
149
ld a, [wWalkCounter]
150
and a
151
ret nz ; don't do anything yet if player is currently moving
152
call InitializeSpriteScreenPosition
153
ld h, HIGH(wSpriteStateData2)
154
ldh a, [hCurrentSpriteOffset]
155
add $6
156
ld l, a
157
ld a, [hl] ; x#SPRITESTATEDATA2_MOVEMENTBYTE1
158
inc a
159
jr z, .randomMovement ; value STAY
160
inc a
161
jr z, .randomMovement ; value WALK
162
; scripted movement
163
dec a
164
ld [hl], a ; increment movement byte 1 (movement data index)
165
dec a
166
push hl
167
ld hl, wNPCNumScriptedSteps
168
dec [hl] ; decrement wNPCNumScriptedSteps
169
pop hl
170
ld de, wNPCMovementDirections
171
call LoadDEPlusA ; a = [wNPCMovementDirections + movement byte 1]
172
cp NPC_CHANGE_FACING
173
jp z, ChangeFacingDirection
174
cp STAY
175
jr nz, .next
176
; reached end of wNPCMovementDirections list
177
ld [hl], a ; store $ff in movement byte 1, disabling scripted movement
178
ld hl, wStatusFlags5
179
res BIT_SCRIPTED_NPC_MOVEMENT, [hl]
180
xor a
181
ld [wSimulatedJoypadStatesIndex], a
182
ld [wUnusedOverrideSimulatedJoypadStatesIndex], a
183
ret
184
.next
185
cp WALK
186
jr nz, .determineDirection
187
; current NPC movement data is WALK ($fe). this seems buggy
188
ld [hl], $1 ; set movement byte 1 to $1
189
ld de, wNPCMovementDirections
190
call LoadDEPlusA ; a = [wNPCMovementDirections + $fe] (?)
191
jr .determineDirection
192
.randomMovement
193
call GetTileSpriteStandsOn
194
call Random
195
.determineDirection
196
ld b, a
197
ld a, [wCurSpriteMovement2]
198
cp DOWN
199
jr z, .moveDown
200
cp UP
201
jr z, .moveUp
202
cp LEFT
203
jr z, .moveLeft
204
cp RIGHT
205
jr z, .moveRight
206
ld a, b
207
cp NPC_MOVEMENT_UP ; NPC_MOVEMENT_DOWN <= a < NPC_MOVEMENT_UP: down (or left)
208
jr nc, .notDown
209
ld a, [wCurSpriteMovement2]
210
cp LEFT_RIGHT
211
jr z, .moveLeft
212
.moveDown
213
ld de, 2*SCREEN_WIDTH
214
add hl, de ; move tile pointer two rows down
215
lb de, 1, 0
216
lb bc, 4, SPRITE_FACING_DOWN
217
jr TryWalking
218
.notDown
219
cp NPC_MOVEMENT_LEFT ; NPC_MOVEMENT_UP <= a < NPC_MOVEMENT_LEFT: up (or right)
220
jr nc, .notUp
221
ld a, [wCurSpriteMovement2]
222
cp LEFT_RIGHT
223
jr z, .moveRight
224
.moveUp
225
ld de, -2*SCREEN_WIDTH
226
add hl, de ; move tile pointer two rows up
227
lb de, -1, 0
228
lb bc, 8, SPRITE_FACING_UP
229
jr TryWalking
230
.notUp
231
cp NPC_MOVEMENT_RIGHT ; NPC_MOVEMENT_LEFT <= a < NPC_MOVEMENT_RIGHT: left (or up)
232
jr nc, .notLeft
233
ld a, [wCurSpriteMovement2]
234
cp UP_DOWN
235
jr z, .moveUp
236
.moveLeft
237
dec hl
238
dec hl ; move tile pointer two columns left
239
lb de, 0, -1
240
lb bc, 2, SPRITE_FACING_LEFT
241
jr TryWalking
242
.notLeft ; NPC_MOVEMENT_RIGHT <= a: right (or down)
243
ld a, [wCurSpriteMovement2]
244
cp UP_DOWN
245
jr z, .moveDown
246
.moveRight
247
inc hl
248
inc hl ; move tile pointer two columns right
249
lb de, 0, 1
250
lb bc, 1, SPRITE_FACING_RIGHT
251
jr TryWalking
252
253
; changes facing direction by zeroing the movement delta and calling TryWalking
254
ChangeFacingDirection:
255
ld de, $0
256
; fall through
257
258
; b: direction (1,2,4 or 8)
259
; c: new facing direction (0,4,8 or $c)
260
; d: Y movement delta (-1, 0 or 1)
261
; e: X movement delta (-1, 0 or 1)
262
; hl: pointer to tile the sprite would walk onto
263
; set carry on failure, clears carry on success
264
TryWalking:
265
push hl
266
ld h, HIGH(wSpriteStateData1)
267
ldh a, [hCurrentSpriteOffset]
268
add $9
269
ld l, a
270
ld [hl], c ; x#SPRITESTATEDATA1_FACINGDIRECTION
271
ldh a, [hCurrentSpriteOffset]
272
add $3
273
ld l, a
274
ld [hl], d ; x#SPRITESTATEDATA1_YSTEPVECTOR
275
inc l
276
inc l
277
ld [hl], e ; x#SPRITESTATEDATA1_XSTEPVECTOR
278
pop hl
279
push de
280
ld c, [hl] ; read tile to walk onto
281
call CanWalkOntoTile
282
pop de
283
ret c ; cannot walk there (reinitialization of delay values already done)
284
ld h, HIGH(wSpriteStateData2)
285
ldh a, [hCurrentSpriteOffset]
286
add $4
287
ld l, a
288
ld a, [hl] ; x#SPRITESTATEDATA2_MAPY
289
add d
290
ld [hli], a ; update Y position
291
ld a, [hl] ; x#SPRITESTATEDATA2_MAPX
292
add e
293
ld [hl], a ; update X position
294
ldh a, [hCurrentSpriteOffset]
295
ld l, a
296
ld [hl], $10 ; [x#SPRITESTATEDATA2_WALKANIMATIONCOUNTER] = 16
297
dec h
298
inc l
299
ld [hl], $3 ; x#SPRITESTATEDATA1_MOVEMENTSTATUS
300
jp UpdateSpriteImage
301
302
; update the walking animation parameters for a sprite that is currently walking
303
UpdateSpriteInWalkingAnimation:
304
ldh a, [hCurrentSpriteOffset]
305
add $7
306
ld l, a
307
ld a, [hl] ; x#SPRITESTATEDATA1_INTRAANIMFRAMECOUNTER
308
inc a
309
ld [hl], a ; [x#SPRITESTATEDATA1_INTRAANIMFRAMECOUNTER]++
310
cp $4
311
jr nz, .noNextAnimationFrame
312
xor a
313
ld [hl], a ; [x#SPRITESTATEDATA1_INTRAANIMFRAMECOUNTER] = 0
314
inc l
315
ld a, [hl] ; x#SPRITESTATEDATA1_ANIMFRAMECOUNTER
316
inc a
317
and $3
318
ld [hl], a ; advance to next animation frame every 4 ticks (16 ticks total for one step)
319
.noNextAnimationFrame
320
ldh a, [hCurrentSpriteOffset]
321
add $3
322
ld l, a
323
ld a, [hli] ; x#SPRITESTATEDATA1_YSTEPVECTOR
324
ld b, a
325
ld a, [hl] ; x#SPRITESTATEDATA1_YPIXELS
326
add b
327
ld [hli], a ; update [x#SPRITESTATEDATA1_YPIXELS]
328
ld a, [hli] ; x#SPRITESTATEDATA1_XSTEPVECTOR
329
ld b, a
330
ld a, [hl] ; x#SPRITESTATEDATA1_XPIXELS
331
add b
332
ld [hl], a ; update [x#SPRITESTATEDATA1_XPIXELS]
333
ldh a, [hCurrentSpriteOffset]
334
ld l, a
335
inc h
336
ld a, [hl] ; x#SPRITESTATEDATA2_WALKANIMATIONCOUNTER
337
dec a
338
ld [hl], a ; update walk animation counter
339
ret nz
340
ld a, $6 ; walking finished, update state
341
add l
342
ld l, a
343
ld a, [hl] ; x#SPRITESTATEDATA2_MOVEMENTBYTE1
344
cp WALK
345
jr nc, .initNextMovementCounter ; values WALK or STAY
346
ldh a, [hCurrentSpriteOffset]
347
inc a
348
ld l, a
349
dec h
350
ld [hl], $1 ; [x#SPRITESTATEDATA1_MOVEMENTSTATUS] = 1 (movement status ready)
351
ret
352
.initNextMovementCounter
353
call Random
354
ldh a, [hCurrentSpriteOffset]
355
add $8
356
ld l, a
357
ldh a, [hRandomAdd]
358
and $7f
359
ld [hl], a ; x#SPRITESTATEDATA2_MOVEMENTDELAY:
360
; set next movement delay to a random value in [0,$7f]
361
; note that value 0 actually makes the delay $100 (bug?)
362
dec h ; HIGH(wSpriteStateData1)
363
ldh a, [hCurrentSpriteOffset]
364
inc a
365
ld l, a
366
ld [hl], $2 ; [x#SPRITESTATEDATA1_MOVEMENTSTATUS] = 2 (movement status)
367
inc l
368
inc l
369
xor a
370
ld b, [hl] ; x#SPRITESTATEDATA1_YSTEPVECTOR
371
ld [hli], a ; [x#SPRITESTATEDATA1_YSTEPVECTOR] = 0
372
inc l
373
ld c, [hl] ; x#SPRITESTATEDATA1_XSTEPVECTOR
374
ld [hl], a ; [x#SPRITESTATEDATA1_XSTEPVECTOR] = 0
375
ret
376
377
; update [x#SPRITESTATEDATA2_MOVEMENTDELAY] for sprites in the delayed state (x#SPRITESTATEDATA1_MOVEMENTSTATUS)
378
UpdateSpriteMovementDelay:
379
ld h, HIGH(wSpriteStateData2)
380
ldh a, [hCurrentSpriteOffset]
381
add $6
382
ld l, a
383
ld a, [hl] ; x#SPRITESTATEDATA2_MOVEMENTBYTE1
384
inc l
385
inc l
386
cp WALK
387
jr nc, .tickMoveCounter ; values WALK or STAY
388
ld [hl], $0
389
jr .moving
390
.tickMoveCounter
391
dec [hl] ; x#SPRITESTATEDATA2_MOVEMENTDELAY
392
jr nz, NotYetMoving
393
.moving
394
dec h
395
ldh a, [hCurrentSpriteOffset]
396
inc a
397
ld l, a
398
ld [hl], $1 ; [x#SPRITESTATEDATA1_MOVEMENTSTATUS] = 1 (mark as ready to move)
399
; fallthrough
400
NotYetMoving:
401
ld h, HIGH(wSpriteStateData1)
402
ldh a, [hCurrentSpriteOffset]
403
add SPRITESTATEDATA1_ANIMFRAMECOUNTER
404
ld l, a
405
ld [hl], $0 ; [x#SPRITESTATEDATA1_ANIMFRAMECOUNTER] = 0 (walk animation frame)
406
jp UpdateSpriteImage
407
408
MakeNPCFacePlayer:
409
; Make an NPC face the player if the player has spoken to him or her.
410
411
; Check if the behaviour of the NPC facing the player when spoken to is
412
; disabled. This is only done when rubbing the S.S. Anne captain's back.
413
ld a, [wStatusFlags3]
414
bit BIT_NO_NPC_FACE_PLAYER, a
415
jr nz, NotYetMoving
416
res BIT_FACE_PLAYER, [hl]
417
ld a, [wPlayerDirection]
418
bit PLAYER_DIR_BIT_UP, a
419
jr z, .notFacingDown
420
ld c, SPRITE_FACING_DOWN
421
jr .facingDirectionDetermined
422
.notFacingDown
423
bit PLAYER_DIR_BIT_DOWN, a
424
jr z, .notFacingUp
425
ld c, SPRITE_FACING_UP
426
jr .facingDirectionDetermined
427
.notFacingUp
428
bit PLAYER_DIR_BIT_LEFT, a
429
jr z, .notFacingRight
430
ld c, SPRITE_FACING_RIGHT
431
jr .facingDirectionDetermined
432
.notFacingRight
433
ld c, SPRITE_FACING_LEFT
434
.facingDirectionDetermined
435
ldh a, [hCurrentSpriteOffset]
436
add $9
437
ld l, a
438
ld [hl], c ; [x#SPRITESTATEDATA1_FACINGDIRECTION]: set facing direction
439
jr NotYetMoving
440
441
InitializeSpriteStatus:
442
ld [hl], $1 ; [x#SPRITESTATEDATA1_MOVEMENTSTATUS] = ready
443
inc l
444
ld [hl], $ff ; [x#SPRITESTATEDATA1_IMAGEINDEX] = invisible/off screen
445
inc h ; HIGH(wSpriteStateData2)
446
ldh a, [hCurrentSpriteOffset]
447
add $2
448
ld l, a
449
ld a, $8
450
ld [hli], a ; [x#SPRITESTATEDATA2_YDISPLACEMENT] = 8
451
ld [hl], a ; [x#SPRITESTATEDATA2_XDISPLACEMENT] = 8
452
ret
453
454
; calculates the sprite's screen position from its map position and the player position
455
InitializeSpriteScreenPosition:
456
ld h, HIGH(wSpriteStateData2)
457
ldh a, [hCurrentSpriteOffset]
458
add SPRITESTATEDATA2_MAPY
459
ld l, a
460
ld a, [wYCoord]
461
ld b, a
462
ld a, [hl] ; x#SPRITESTATEDATA2_MAPY
463
sub b ; relative to player position
464
swap a ; * 16
465
sub $4 ; - 4
466
dec h
467
ld [hli], a ; [x#SPRITESTATEDATA1_YPIXELS]
468
inc h
469
ld a, [wXCoord]
470
ld b, a
471
ld a, [hli] ; x#SPRITESTATEDATA2_MAPX
472
sub b ; relative to player position
473
swap a ; * 16
474
dec h
475
ld [hl], a ; [x#SPRITESTATEDATA1_XPIXELS]
476
ret
477
478
; tests if sprite is off screen or otherwise unable to do anything
479
CheckSpriteAvailability:
480
predef IsObjectHidden
481
ldh a, [hIsToggleableObjectOff]
482
and a
483
jp nz, .spriteInvisible
484
ld h, HIGH(wSpriteStateData2)
485
ldh a, [hCurrentSpriteOffset]
486
add SPRITESTATEDATA2_MOVEMENTBYTE1
487
ld l, a
488
ld a, [hl] ; x#SPRITESTATEDATA2_MOVEMENTBYTE1
489
cp WALK
490
jr c, .skipXVisibilityTest ; movement byte 1 < WALK (i.e. the sprite's movement is scripted)
491
ldh a, [hCurrentSpriteOffset]
492
add SPRITESTATEDATA2_MAPY
493
ld l, a
494
ld b, [hl] ; x#SPRITESTATEDATA2_MAPY
495
ld a, [wYCoord]
496
cp b
497
jr z, .skipYVisibilityTest
498
jr nc, .spriteInvisible ; above screen region
499
add SCREEN_HEIGHT / 2 - 1
500
cp b
501
jr c, .spriteInvisible ; below screen region
502
.skipYVisibilityTest
503
inc l
504
ld b, [hl] ; x#SPRITESTATEDATA2_MAPX
505
ld a, [wXCoord]
506
cp b
507
jr z, .skipXVisibilityTest
508
jr nc, .spriteInvisible ; left of screen region
509
add SCREEN_WIDTH / 2 - 1
510
cp b
511
jr c, .spriteInvisible ; right of screen region
512
.skipXVisibilityTest
513
; make the sprite invisible if a text box is in front of it
514
; $5F is the maximum number for map tiles
515
call GetTileSpriteStandsOn
516
ld d, MAP_TILESET_SIZE
517
ld a, [hli]
518
cp d
519
jr nc, .spriteInvisible ; standing on tile with ID >=MAP_TILESET_SIZE (bottom left tile)
520
ld a, [hld]
521
cp d
522
jr nc, .spriteInvisible ; standing on tile with ID >=MAP_TILESET_SIZE (bottom right tile)
523
ld bc, -SCREEN_WIDTH
524
add hl, bc ; go back one row of tiles
525
ld a, [hli]
526
cp d
527
jr nc, .spriteInvisible ; standing on tile with ID >=MAP_TILESET_SIZE (top left tile)
528
ld a, [hl]
529
cp d
530
jr c, .spriteVisible ; standing on tile with ID >=MAP_TILESET_SIZE (top right tile)
531
.spriteInvisible
532
ld h, HIGH(wSpriteStateData1)
533
ldh a, [hCurrentSpriteOffset]
534
add SPRITESTATEDATA1_IMAGEINDEX
535
ld l, a
536
ld [hl], $ff ; x#SPRITESTATEDATA1_IMAGEINDEX
537
scf
538
jr .done
539
.spriteVisible
540
ld c, a
541
ld a, [wWalkCounter]
542
and a
543
jr nz, .done ; if player is currently walking, we're done
544
call UpdateSpriteImage
545
inc h
546
ldh a, [hCurrentSpriteOffset]
547
add $7
548
ld l, a
549
ld a, [wGrassTile]
550
cp c
551
ld a, 0
552
jr nz, .notInGrass
553
ld a, OAM_PRIO
554
.notInGrass
555
ld [hl], a ; x#SPRITESTATEDATA2_GRASSPRIORITY
556
and a
557
.done
558
ret
559
560
UpdateSpriteImage:
561
ld h, HIGH(wSpriteStateData1)
562
ldh a, [hCurrentSpriteOffset]
563
add $8
564
ld l, a
565
ld a, [hli] ; x#SPRITESTATEDATA1_ANIMFRAMECOUNTER
566
ld b, a
567
ld a, [hl] ; x#SPRITESTATEDATA1_FACINGDIRECTION
568
add b
569
ld b, a
570
ldh a, [hTilePlayerStandingOn]
571
add b
572
ld b, a
573
ldh a, [hCurrentSpriteOffset]
574
add $2
575
ld l, a
576
ld [hl], b ; x#SPRITESTATEDATA1_IMAGEINDEX
577
ret
578
579
; tests if sprite can walk the specified direction
580
; b: direction (1,2,4 or 8)
581
; c: ID of tile the sprite would walk onto
582
; d: Y movement delta (-1, 0 or 1)
583
; e: X movement delta (-1, 0 or 1)
584
; set carry on failure, clears carry on success
585
CanWalkOntoTile:
586
ld h, HIGH(wSpriteStateData2)
587
ldh a, [hCurrentSpriteOffset]
588
add SPRITESTATEDATA2_MOVEMENTBYTE1
589
ld l, a
590
ld a, [hl] ; x#SPRITESTATEDATA2_MOVEMENTBYTE1
591
cp WALK
592
jr nc, .notScripted ; values WALK or STAY
593
; always allow walking if the movement is scripted
594
and a
595
ret
596
.notScripted
597
ld a, [wTilesetCollisionPtr]
598
ld l, a
599
ld a, [wTilesetCollisionPtr+1]
600
ld h, a
601
.tilePassableLoop
602
ld a, [hli]
603
cp $ff
604
jr z, .impassable
605
cp c
606
jr nz, .tilePassableLoop
607
ld h, HIGH(wSpriteStateData2)
608
ldh a, [hCurrentSpriteOffset]
609
add $6
610
ld l, a
611
ld a, [hl] ; x#SPRITESTATEDATA2_MOVEMENTBYTE1
612
inc a
613
jr z, .impassable ; if $ff, no movement allowed (however, changing direction is)
614
ld h, HIGH(wSpriteStateData1)
615
ldh a, [hCurrentSpriteOffset]
616
add SPRITESTATEDATA1_YPIXELS
617
ld l, a
618
ld a, [hli] ; x#SPRITESTATEDATA1_YPIXELS
619
add $4 ; align to blocks (Y pos is always 4 pixels off)
620
add d ; add Y delta
621
cp $80 ; if value is >$80, the destination is off screen (either $81 or $FF underflow)
622
jr nc, .impassable ; don't walk off screen
623
inc l
624
ld a, [hl] ; x#SPRITESTATEDATA1_XPIXELS
625
add e ; add X delta
626
cp $90 ; if value is >$90, the destination is off screen (either $91 or $FF underflow)
627
jr nc, .impassable ; don't walk off screen
628
push de
629
push bc
630
call DetectCollisionBetweenSprites
631
pop bc
632
pop de
633
ld h, HIGH(wSpriteStateData1)
634
ldh a, [hCurrentSpriteOffset]
635
add $c
636
ld l, a
637
ld a, [hl] ; x#SPRITESTATEDATA1_COLLISIONDATA (directions in which sprite collision would occur)
638
and b ; check against chosen direction (1,2,4 or 8)
639
jr nz, .impassable ; collision between sprites, don't go there
640
ld h, HIGH(wSpriteStateData2)
641
ldh a, [hCurrentSpriteOffset]
642
add SPRITESTATEDATA2_YDISPLACEMENT
643
ld l, a
644
ld a, [hli] ; x#SPRITESTATEDATA2_YDISPLACEMENT (initialized at $8, keep track of where a sprite did go)
645
bit 7, d ; check if going upwards (d == -1)
646
jr nz, .upwards
647
add d
648
; bug: these tests against $5 probably were supposed to prevent
649
; sprites from walking out too far, but this line makes sprites get
650
; stuck whenever they walked upwards 5 steps
651
; on the other hand, the amount a sprite can walk out to the
652
; right of bottom is not limited (until the counter overflows)
653
cp $5
654
jr c, .impassable ; if [x#SPRITESTATEDATA2_YDISPLACEMENT]+d < 5, don't go
655
jr .checkHorizontal
656
.upwards
657
sub $1
658
jr c, .impassable ; if [x#SPRITESTATEDATA2_YDISPLACEMENT] == 0, don't go
659
.checkHorizontal
660
ld d, a
661
ld a, [hl] ; x#SPRITESTATEDATA2_XDISPLACEMENT (initialized at $8, keep track of where a sprite did go)
662
bit 7, e ; check if going left (e == -1)
663
jr nz, .left
664
add e
665
cp $5 ; compare, but no conditional jump like in the vertical check above (bug?)
666
jr .passable
667
.left
668
sub $1
669
jr c, .impassable ; if [x#SPRITESTATEDATA2_XDISPLACEMENT] == 0, don't go
670
.passable
671
ld [hld], a ; update x#SPRITESTATEDATA2_XDISPLACEMENT
672
ld [hl], d ; update x#SPRITESTATEDATA2_YDISPLACEMENT
673
and a ; clear carry (marking success)
674
ret
675
.impassable
676
ld h, HIGH(wSpriteStateData1)
677
ldh a, [hCurrentSpriteOffset]
678
inc a
679
ld l, a
680
ld [hl], $2 ; [x#SPRITESTATEDATA1_MOVEMENTSTATUS] = 2 (delayed)
681
inc l
682
inc l
683
xor a
684
ld [hli], a ; [x#SPRITESTATEDATA1_YSTEPVECTOR] = 0
685
inc l
686
ld [hl], a ; [x#SPRITESTATEDATA1_XSTEPVECTOR] = 0
687
inc h
688
ldh a, [hCurrentSpriteOffset]
689
add $8
690
ld l, a
691
call Random
692
ldh a, [hRandomAdd]
693
and $7f
694
ld [hl], a ; x#SPRITESTATEDATA2_MOVEMENTDELAY: set to a random value in [0,$7f] (again with delay $100 if value is 0)
695
scf ; set carry (marking failure to walk)
696
ret
697
698
; calculates the tile pointer pointing to the tile the current sprite stands on
699
; this is always the lower left tile of the 2x2 tile blocks all sprites are snapped to
700
; hl: output pointer
701
GetTileSpriteStandsOn:
702
ld h, HIGH(wSpriteStateData1)
703
ldh a, [hCurrentSpriteOffset]
704
add SPRITESTATEDATA1_YPIXELS
705
ld l, a
706
ld a, [hli] ; x#SPRITESTATEDATA1_YPIXELS
707
add $4 ; align to 2*2 tile blocks (Y position is always off 4 pixels to the top)
708
and $f0 ; in case object is currently moving
709
srl a ; screen Y tile * 4
710
ld c, a
711
ld b, $0
712
inc l
713
ld a, [hl] ; x#SPRITESTATEDATA1_XPIXELS
714
srl a
715
srl a
716
srl a ; screen X tile
717
add SCREEN_WIDTH ; screen X tile + 20
718
ld d, $0
719
ld e, a
720
hlcoord 0, 0
721
add hl, bc
722
add hl, bc
723
add hl, bc
724
add hl, bc
725
add hl, bc
726
add hl, de ; wTileMap + 20*(screen Y tile + 1) + screen X tile
727
ret
728
729
; loads [de+a] into a
730
LoadDEPlusA:
731
add e
732
ld e, a
733
jr nc, .noCarry
734
inc d
735
.noCarry
736
ld a, [de]
737
ret
738
739
DoScriptedNPCMovement:
740
; This is an alternative method of scripting an NPC's movement and is only used
741
; a few times in the game. It is used when the NPC and player must walk together
742
; in sync, such as when the player is following the NPC somewhere. An NPC can't
743
; be moved in sync with the player using the other method.
744
ld a, [wStatusFlags5]
745
bit BIT_SCRIPTED_MOVEMENT_STATE, a
746
ret z
747
ld hl, wStatusFlags4
748
bit BIT_INIT_SCRIPTED_MOVEMENT, [hl]
749
set BIT_INIT_SCRIPTED_MOVEMENT, [hl]
750
jp z, InitScriptedNPCMovement
751
ld hl, wNPCMovementDirections2
752
ld a, [wNPCMovementDirections2Index]
753
add l
754
ld l, a
755
jr nc, .noCarry
756
inc h
757
.noCarry
758
ld a, [hl]
759
; check if moving up
760
cp NPC_MOVEMENT_UP
761
jr nz, .checkIfMovingDown
762
call GetSpriteScreenYPointer
763
ld c, SPRITE_FACING_UP
764
ld a, -2
765
jr .move
766
.checkIfMovingDown
767
cp NPC_MOVEMENT_DOWN
768
jr nz, .checkIfMovingLeft
769
call GetSpriteScreenYPointer
770
ld c, SPRITE_FACING_DOWN
771
ld a, 2
772
jr .move
773
.checkIfMovingLeft
774
cp NPC_MOVEMENT_LEFT
775
jr nz, .checkIfMovingRight
776
call GetSpriteScreenXPointer
777
ld c, SPRITE_FACING_LEFT
778
ld a, -2
779
jr .move
780
.checkIfMovingRight
781
cp NPC_MOVEMENT_RIGHT
782
jr nz, .noMatch
783
call GetSpriteScreenXPointer
784
ld c, SPRITE_FACING_RIGHT
785
ld a, 2
786
jr .move
787
.noMatch
788
cp $ff
789
ret
790
.move
791
ld b, a
792
ld a, [hl]
793
add b
794
ld [hl], a
795
ldh a, [hCurrentSpriteOffset]
796
add $9
797
ld l, a
798
ld a, c
799
ld [hl], a ; facing direction
800
call AnimScriptedNPCMovement
801
ld hl, wScriptedNPCWalkCounter
802
dec [hl]
803
ret nz
804
ld a, 8
805
ld [wScriptedNPCWalkCounter], a
806
ld hl, wNPCMovementDirections2Index
807
inc [hl]
808
ret
809
810
InitScriptedNPCMovement:
811
xor a
812
ld [wNPCMovementDirections2Index], a
813
ld a, 8
814
ld [wScriptedNPCWalkCounter], a
815
jp AnimScriptedNPCMovement
816
817
GetSpriteScreenYPointer:
818
ld a, SPRITESTATEDATA1_YPIXELS
819
ld b, a
820
jr GetSpriteScreenXYPointerCommon
821
822
GetSpriteScreenXPointer:
823
ld a, SPRITESTATEDATA1_XPIXELS
824
ld b, a
825
826
GetSpriteScreenXYPointerCommon:
827
ld hl, wSpriteStateData1
828
ldh a, [hCurrentSpriteOffset]
829
add l
830
add b
831
ld l, a
832
ret
833
834
AnimScriptedNPCMovement:
835
ld hl, wSpriteStateData2
836
ldh a, [hCurrentSpriteOffset]
837
add SPRITESTATEDATA2_IMAGEBASEOFFSET
838
ld l, a
839
ld a, [hl] ; VRAM slot
840
dec a
841
swap a
842
ld b, a
843
ld hl, wSpriteStateData1
844
ldh a, [hCurrentSpriteOffset]
845
add SPRITESTATEDATA1_FACINGDIRECTION
846
ld l, a
847
ld a, [hl] ; facing direction
848
cp SPRITE_FACING_DOWN
849
jr z, .anim
850
cp SPRITE_FACING_UP
851
jr z, .anim
852
cp SPRITE_FACING_LEFT
853
jr z, .anim
854
cp SPRITE_FACING_RIGHT
855
jr z, .anim
856
ret
857
.anim
858
add b
859
ld b, a
860
ldh [hSpriteVRAMSlotAndFacing], a
861
call AdvanceScriptedNPCAnimFrameCounter
862
ld hl, wSpriteStateData1
863
ldh a, [hCurrentSpriteOffset]
864
add SPRITESTATEDATA1_IMAGEINDEX
865
ld l, a
866
ldh a, [hSpriteVRAMSlotAndFacing]
867
ld b, a
868
ldh a, [hSpriteAnimFrameCounter]
869
add b
870
ld [hl], a
871
ret
872
873
AdvanceScriptedNPCAnimFrameCounter:
874
ldh a, [hCurrentSpriteOffset]
875
add $7
876
ld l, a
877
ld a, [hl] ; intra-animation frame counter
878
inc a
879
ld [hl], a
880
cp 4
881
ret nz
882
xor a
883
ld [hl], a ; reset intra-animation frame counter
884
inc l
885
ld a, [hl] ; animation frame counter
886
inc a
887
and $3
888
ld [hl], a
889
ldh [hSpriteAnimFrameCounter], a
890
ret
891
892