Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pret
GitHub Repository: pret/pokered
Path: blob/master/engine/overworld/movement.asm
1271 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
notYetMoving:
400
ld h, HIGH(wSpriteStateData1)
401
ldh a, [hCurrentSpriteOffset]
402
add SPRITESTATEDATA1_ANIMFRAMECOUNTER
403
ld l, a
404
ld [hl], $0 ; [x#SPRITESTATEDATA1_ANIMFRAMECOUNTER] = 0 (walk animation frame)
405
jp UpdateSpriteImage
406
407
MakeNPCFacePlayer:
408
; Make an NPC face the player if the player has spoken to him or her.
409
410
; Check if the behaviour of the NPC facing the player when spoken to is
411
; disabled. This is only done when rubbing the S.S. Anne captain's back.
412
ld a, [wStatusFlags3]
413
bit BIT_NO_NPC_FACE_PLAYER, a
414
jr nz, notYetMoving
415
res BIT_FACE_PLAYER, [hl]
416
ld a, [wPlayerDirection]
417
bit PLAYER_DIR_BIT_UP, a
418
jr z, .notFacingDown
419
ld c, SPRITE_FACING_DOWN
420
jr .facingDirectionDetermined
421
.notFacingDown
422
bit PLAYER_DIR_BIT_DOWN, a
423
jr z, .notFacingUp
424
ld c, SPRITE_FACING_UP
425
jr .facingDirectionDetermined
426
.notFacingUp
427
bit PLAYER_DIR_BIT_LEFT, a
428
jr z, .notFacingRight
429
ld c, SPRITE_FACING_RIGHT
430
jr .facingDirectionDetermined
431
.notFacingRight
432
ld c, SPRITE_FACING_LEFT
433
.facingDirectionDetermined
434
ldh a, [hCurrentSpriteOffset]
435
add $9
436
ld l, a
437
ld [hl], c ; [x#SPRITESTATEDATA1_FACINGDIRECTION]: set facing direction
438
jr notYetMoving
439
440
InitializeSpriteStatus:
441
ld [hl], $1 ; [x#SPRITESTATEDATA1_MOVEMENTSTATUS] = ready
442
inc l
443
ld [hl], $ff ; [x#SPRITESTATEDATA1_IMAGEINDEX] = invisible/off screen
444
inc h ; HIGH(wSpriteStateData2)
445
ldh a, [hCurrentSpriteOffset]
446
add $2
447
ld l, a
448
ld a, $8
449
ld [hli], a ; [x#SPRITESTATEDATA2_YDISPLACEMENT] = 8
450
ld [hl], a ; [x#SPRITESTATEDATA2_XDISPLACEMENT] = 8
451
ret
452
453
; calculates the sprite's screen position from its map position and the player position
454
InitializeSpriteScreenPosition:
455
ld h, HIGH(wSpriteStateData2)
456
ldh a, [hCurrentSpriteOffset]
457
add SPRITESTATEDATA2_MAPY
458
ld l, a
459
ld a, [wYCoord]
460
ld b, a
461
ld a, [hl] ; x#SPRITESTATEDATA2_MAPY
462
sub b ; relative to player position
463
swap a ; * 16
464
sub $4 ; - 4
465
dec h
466
ld [hli], a ; [x#SPRITESTATEDATA1_YPIXELS]
467
inc h
468
ld a, [wXCoord]
469
ld b, a
470
ld a, [hli] ; x#SPRITESTATEDATA2_MAPX
471
sub b ; relative to player position
472
swap a ; * 16
473
dec h
474
ld [hl], a ; [x#SPRITESTATEDATA1_XPIXELS]
475
ret
476
477
; tests if sprite is off screen or otherwise unable to do anything
478
CheckSpriteAvailability:
479
predef IsObjectHidden
480
ldh a, [hIsHiddenMissableObject]
481
and a
482
jp nz, .spriteInvisible
483
ld h, HIGH(wSpriteStateData2)
484
ldh a, [hCurrentSpriteOffset]
485
add SPRITESTATEDATA2_MOVEMENTBYTE1
486
ld l, a
487
ld a, [hl] ; x#SPRITESTATEDATA2_MOVEMENTBYTE1
488
cp WALK
489
jr c, .skipXVisibilityTest ; movement byte 1 < WALK (i.e. the sprite's movement is scripted)
490
ldh a, [hCurrentSpriteOffset]
491
add SPRITESTATEDATA2_MAPY
492
ld l, a
493
ld b, [hl] ; x#SPRITESTATEDATA2_MAPY
494
ld a, [wYCoord]
495
cp b
496
jr z, .skipYVisibilityTest
497
jr nc, .spriteInvisible ; above screen region
498
add SCREEN_HEIGHT / 2 - 1
499
cp b
500
jr c, .spriteInvisible ; below screen region
501
.skipYVisibilityTest
502
inc l
503
ld b, [hl] ; x#SPRITESTATEDATA2_MAPX
504
ld a, [wXCoord]
505
cp b
506
jr z, .skipXVisibilityTest
507
jr nc, .spriteInvisible ; left of screen region
508
add SCREEN_WIDTH / 2 - 1
509
cp b
510
jr c, .spriteInvisible ; right of screen region
511
.skipXVisibilityTest
512
; make the sprite invisible if a text box is in front of it
513
; $5F is the maximum number for map tiles
514
call GetTileSpriteStandsOn
515
ld d, MAP_TILESET_SIZE
516
ld a, [hli]
517
cp d
518
jr nc, .spriteInvisible ; standing on tile with ID >=MAP_TILESET_SIZE (bottom left tile)
519
ld a, [hld]
520
cp d
521
jr nc, .spriteInvisible ; standing on tile with ID >=MAP_TILESET_SIZE (bottom right tile)
522
ld bc, -SCREEN_WIDTH
523
add hl, bc ; go back one row of tiles
524
ld a, [hli]
525
cp d
526
jr nc, .spriteInvisible ; standing on tile with ID >=MAP_TILESET_SIZE (top left tile)
527
ld a, [hl]
528
cp d
529
jr c, .spriteVisible ; standing on tile with ID >=MAP_TILESET_SIZE (top right tile)
530
.spriteInvisible
531
ld h, HIGH(wSpriteStateData1)
532
ldh a, [hCurrentSpriteOffset]
533
add SPRITESTATEDATA1_IMAGEINDEX
534
ld l, a
535
ld [hl], $ff ; x#SPRITESTATEDATA1_IMAGEINDEX
536
scf
537
jr .done
538
.spriteVisible
539
ld c, a
540
ld a, [wWalkCounter]
541
and a
542
jr nz, .done ; if player is currently walking, we're done
543
call UpdateSpriteImage
544
inc h
545
ldh a, [hCurrentSpriteOffset]
546
add $7
547
ld l, a
548
ld a, [wGrassTile]
549
cp c
550
ld a, 0
551
jr nz, .notInGrass
552
ld a, OAM_PRIO
553
.notInGrass
554
ld [hl], a ; x#SPRITESTATEDATA2_GRASSPRIORITY
555
and a
556
.done
557
ret
558
559
UpdateSpriteImage:
560
ld h, HIGH(wSpriteStateData1)
561
ldh a, [hCurrentSpriteOffset]
562
add $8
563
ld l, a
564
ld a, [hli] ; x#SPRITESTATEDATA1_ANIMFRAMECOUNTER
565
ld b, a
566
ld a, [hl] ; x#SPRITESTATEDATA1_FACINGDIRECTION
567
add b
568
ld b, a
569
ldh a, [hTilePlayerStandingOn]
570
add b
571
ld b, a
572
ldh a, [hCurrentSpriteOffset]
573
add $2
574
ld l, a
575
ld [hl], b ; x#SPRITESTATEDATA1_IMAGEINDEX
576
ret
577
578
; tests if sprite can walk the specified direction
579
; b: direction (1,2,4 or 8)
580
; c: ID of tile the sprite would walk onto
581
; d: Y movement delta (-1, 0 or 1)
582
; e: X movement delta (-1, 0 or 1)
583
; set carry on failure, clears carry on success
584
CanWalkOntoTile:
585
ld h, HIGH(wSpriteStateData2)
586
ldh a, [hCurrentSpriteOffset]
587
add SPRITESTATEDATA2_MOVEMENTBYTE1
588
ld l, a
589
ld a, [hl] ; x#SPRITESTATEDATA2_MOVEMENTBYTE1
590
cp WALK
591
jr nc, .notScripted ; values WALK or STAY
592
; always allow walking if the movement is scripted
593
and a
594
ret
595
.notScripted
596
ld a, [wTilesetCollisionPtr]
597
ld l, a
598
ld a, [wTilesetCollisionPtr+1]
599
ld h, a
600
.tilePassableLoop
601
ld a, [hli]
602
cp $ff
603
jr z, .impassable
604
cp c
605
jr nz, .tilePassableLoop
606
ld h, HIGH(wSpriteStateData2)
607
ldh a, [hCurrentSpriteOffset]
608
add $6
609
ld l, a
610
ld a, [hl] ; x#SPRITESTATEDATA2_MOVEMENTBYTE1
611
inc a
612
jr z, .impassable ; if $ff, no movement allowed (however, changing direction is)
613
ld h, HIGH(wSpriteStateData1)
614
ldh a, [hCurrentSpriteOffset]
615
add SPRITESTATEDATA1_YPIXELS
616
ld l, a
617
ld a, [hli] ; x#SPRITESTATEDATA1_YPIXELS
618
add $4 ; align to blocks (Y pos is always 4 pixels off)
619
add d ; add Y delta
620
cp $80 ; if value is >$80, the destination is off screen (either $81 or $FF underflow)
621
jr nc, .impassable ; don't walk off screen
622
inc l
623
ld a, [hl] ; x#SPRITESTATEDATA1_XPIXELS
624
add e ; add X delta
625
cp $90 ; if value is >$90, the destination is off screen (either $91 or $FF underflow)
626
jr nc, .impassable ; don't walk off screen
627
push de
628
push bc
629
call DetectCollisionBetweenSprites
630
pop bc
631
pop de
632
ld h, HIGH(wSpriteStateData1)
633
ldh a, [hCurrentSpriteOffset]
634
add $c
635
ld l, a
636
ld a, [hl] ; x#SPRITESTATEDATA1_COLLISIONDATA (directions in which sprite collision would occur)
637
and b ; check against chosen direction (1,2,4 or 8)
638
jr nz, .impassable ; collision between sprites, don't go there
639
ld h, HIGH(wSpriteStateData2)
640
ldh a, [hCurrentSpriteOffset]
641
add SPRITESTATEDATA2_YDISPLACEMENT
642
ld l, a
643
ld a, [hli] ; x#SPRITESTATEDATA2_YDISPLACEMENT (initialized at $8, keep track of where a sprite did go)
644
bit 7, d ; check if going upwards (d == -1)
645
jr nz, .upwards
646
add d
647
; bug: these tests against $5 probably were supposed to prevent
648
; sprites from walking out too far, but this line makes sprites get
649
; stuck whenever they walked upwards 5 steps
650
; on the other hand, the amount a sprite can walk out to the
651
; right of bottom is not limited (until the counter overflows)
652
cp $5
653
jr c, .impassable ; if [x#SPRITESTATEDATA2_YDISPLACEMENT]+d < 5, don't go
654
jr .checkHorizontal
655
.upwards
656
sub $1
657
jr c, .impassable ; if [x#SPRITESTATEDATA2_YDISPLACEMENT] == 0, don't go
658
.checkHorizontal
659
ld d, a
660
ld a, [hl] ; x#SPRITESTATEDATA2_XDISPLACEMENT (initialized at $8, keep track of where a sprite did go)
661
bit 7, e ; check if going left (e == -1)
662
jr nz, .left
663
add e
664
cp $5 ; compare, but no conditional jump like in the vertical check above (bug?)
665
jr .passable
666
.left
667
sub $1
668
jr c, .impassable ; if [x#SPRITESTATEDATA2_XDISPLACEMENT] == 0, don't go
669
.passable
670
ld [hld], a ; update x#SPRITESTATEDATA2_XDISPLACEMENT
671
ld [hl], d ; update x#SPRITESTATEDATA2_YDISPLACEMENT
672
and a ; clear carry (marking success)
673
ret
674
.impassable
675
ld h, HIGH(wSpriteStateData1)
676
ldh a, [hCurrentSpriteOffset]
677
inc a
678
ld l, a
679
ld [hl], $2 ; [x#SPRITESTATEDATA1_MOVEMENTSTATUS] = 2 (delayed)
680
inc l
681
inc l
682
xor a
683
ld [hli], a ; [x#SPRITESTATEDATA1_YSTEPVECTOR] = 0
684
inc l
685
ld [hl], a ; [x#SPRITESTATEDATA1_XSTEPVECTOR] = 0
686
inc h
687
ldh a, [hCurrentSpriteOffset]
688
add $8
689
ld l, a
690
call Random
691
ldh a, [hRandomAdd]
692
and $7f
693
ld [hl], a ; x#SPRITESTATEDATA2_MOVEMENTDELAY: set to a random value in [0,$7f] (again with delay $100 if value is 0)
694
scf ; set carry (marking failure to walk)
695
ret
696
697
; calculates the tile pointer pointing to the tile the current sprite stands on
698
; this is always the lower left tile of the 2x2 tile blocks all sprites are snapped to
699
; hl: output pointer
700
GetTileSpriteStandsOn:
701
ld h, HIGH(wSpriteStateData1)
702
ldh a, [hCurrentSpriteOffset]
703
add SPRITESTATEDATA1_YPIXELS
704
ld l, a
705
ld a, [hli] ; x#SPRITESTATEDATA1_YPIXELS
706
add $4 ; align to 2*2 tile blocks (Y position is always off 4 pixels to the top)
707
and $f0 ; in case object is currently moving
708
srl a ; screen Y tile * 4
709
ld c, a
710
ld b, $0
711
inc l
712
ld a, [hl] ; x#SPRITESTATEDATA1_XPIXELS
713
srl a
714
srl a
715
srl a ; screen X tile
716
add SCREEN_WIDTH ; screen X tile + 20
717
ld d, $0
718
ld e, a
719
hlcoord 0, 0
720
add hl, bc
721
add hl, bc
722
add hl, bc
723
add hl, bc
724
add hl, bc
725
add hl, de ; wTileMap + 20*(screen Y tile + 1) + screen X tile
726
ret
727
728
; loads [de+a] into a
729
LoadDEPlusA:
730
add e
731
ld e, a
732
jr nc, .noCarry
733
inc d
734
.noCarry
735
ld a, [de]
736
ret
737
738
DoScriptedNPCMovement:
739
; This is an alternative method of scripting an NPC's movement and is only used
740
; a few times in the game. It is used when the NPC and player must walk together
741
; in sync, such as when the player is following the NPC somewhere. An NPC can't
742
; be moved in sync with the player using the other method.
743
ld a, [wStatusFlags5]
744
bit BIT_SCRIPTED_MOVEMENT_STATE, a
745
ret z
746
ld hl, wStatusFlags4
747
bit BIT_INIT_SCRIPTED_MOVEMENT, [hl]
748
set BIT_INIT_SCRIPTED_MOVEMENT, [hl]
749
jp z, InitScriptedNPCMovement
750
ld hl, wNPCMovementDirections2
751
ld a, [wNPCMovementDirections2Index]
752
add l
753
ld l, a
754
jr nc, .noCarry
755
inc h
756
.noCarry
757
ld a, [hl]
758
; check if moving up
759
cp NPC_MOVEMENT_UP
760
jr nz, .checkIfMovingDown
761
call GetSpriteScreenYPointer
762
ld c, SPRITE_FACING_UP
763
ld a, -2
764
jr .move
765
.checkIfMovingDown
766
cp NPC_MOVEMENT_DOWN
767
jr nz, .checkIfMovingLeft
768
call GetSpriteScreenYPointer
769
ld c, SPRITE_FACING_DOWN
770
ld a, 2
771
jr .move
772
.checkIfMovingLeft
773
cp NPC_MOVEMENT_LEFT
774
jr nz, .checkIfMovingRight
775
call GetSpriteScreenXPointer
776
ld c, SPRITE_FACING_LEFT
777
ld a, -2
778
jr .move
779
.checkIfMovingRight
780
cp NPC_MOVEMENT_RIGHT
781
jr nz, .noMatch
782
call GetSpriteScreenXPointer
783
ld c, SPRITE_FACING_RIGHT
784
ld a, 2
785
jr .move
786
.noMatch
787
cp $ff
788
ret
789
.move
790
ld b, a
791
ld a, [hl]
792
add b
793
ld [hl], a
794
ldh a, [hCurrentSpriteOffset]
795
add $9
796
ld l, a
797
ld a, c
798
ld [hl], a ; facing direction
799
call AnimScriptedNPCMovement
800
ld hl, wScriptedNPCWalkCounter
801
dec [hl]
802
ret nz
803
ld a, 8
804
ld [wScriptedNPCWalkCounter], a
805
ld hl, wNPCMovementDirections2Index
806
inc [hl]
807
ret
808
809
InitScriptedNPCMovement:
810
xor a
811
ld [wNPCMovementDirections2Index], a
812
ld a, 8
813
ld [wScriptedNPCWalkCounter], a
814
jp AnimScriptedNPCMovement
815
816
GetSpriteScreenYPointer:
817
ld a, SPRITESTATEDATA1_YPIXELS
818
ld b, a
819
jr GetSpriteScreenXYPointerCommon
820
821
GetSpriteScreenXPointer:
822
ld a, SPRITESTATEDATA1_XPIXELS
823
ld b, a
824
825
GetSpriteScreenXYPointerCommon:
826
ld hl, wSpriteStateData1
827
ldh a, [hCurrentSpriteOffset]
828
add l
829
add b
830
ld l, a
831
ret
832
833
AnimScriptedNPCMovement:
834
ld hl, wSpriteStateData2
835
ldh a, [hCurrentSpriteOffset]
836
add SPRITESTATEDATA2_IMAGEBASEOFFSET
837
ld l, a
838
ld a, [hl] ; VRAM slot
839
dec a
840
swap a
841
ld b, a
842
ld hl, wSpriteStateData1
843
ldh a, [hCurrentSpriteOffset]
844
add SPRITESTATEDATA1_FACINGDIRECTION
845
ld l, a
846
ld a, [hl] ; facing direction
847
cp SPRITE_FACING_DOWN
848
jr z, .anim
849
cp SPRITE_FACING_UP
850
jr z, .anim
851
cp SPRITE_FACING_LEFT
852
jr z, .anim
853
cp SPRITE_FACING_RIGHT
854
jr z, .anim
855
ret
856
.anim
857
add b
858
ld b, a
859
ldh [hSpriteVRAMSlotAndFacing], a
860
call AdvanceScriptedNPCAnimFrameCounter
861
ld hl, wSpriteStateData1
862
ldh a, [hCurrentSpriteOffset]
863
add SPRITESTATEDATA1_IMAGEINDEX
864
ld l, a
865
ldh a, [hSpriteVRAMSlotAndFacing]
866
ld b, a
867
ldh a, [hSpriteAnimFrameCounter]
868
add b
869
ld [hl], a
870
ret
871
872
AdvanceScriptedNPCAnimFrameCounter:
873
ldh a, [hCurrentSpriteOffset]
874
add $7
875
ld l, a
876
ld a, [hl] ; intra-animation frame counter
877
inc a
878
ld [hl], a
879
cp 4
880
ret nz
881
xor a
882
ld [hl], a ; reset intra-animation frame counter
883
inc l
884
ld a, [hl] ; animation frame counter
885
inc a
886
and $3
887
ld [hl], a
888
ldh [hSpriteAnimFrameCounter], a
889
ret
890
891