-
Notifications
You must be signed in to change notification settings - Fork 0
/
game.s
1576 lines (1357 loc) · 41.8 KB
/
game.s
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
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
@@@
@@@ balloon spite
@@@ core game logic
@@@
.include "game.inc"
.include "arenas.inc"
.include "archetype.inc"
@@ Physics numbers are mostly 12.4 fixed point
.equ PHYS_FIXED_POINT, 4
@@ Accounts for both the division by 2 required for correct
@@ integration, and the fixed point fraction of mass
.equ IMPULSE_SCALING, 10
@@ Terminal velocity
.equ TERMINAL_VELOCITY, 16
@@ Collision radii squared
.equ COLLIDE_TYPE_0_RADIUS, 15
.equ COLLIDE_TYPE_1_RADIUS, 150
.equ BALLOON_TILE, 0
.equ BALLOON_POP_TILE, 1
.equ ACTOR_TILE_OFFSET, 16
.equ FRAME_FLY, 0
.equ FRAME_BUMP, 1
.equ FRAME_DIE, 2
.equ FRAME_WIN, 3
.equ BODY_LEN, 16
.equ BALLOONIST_LEN, BODY_LEN+16
.equ N_BALLOONISTS,2
.equ MAX_BALLOONS,8
.equ BALLOON_MASS, 0xff
.equ BALLOON_LIFT, -5
.equ BALLOON_DISTANCE, 12
.equ BALLOON_POP_TIMING, 20
.equ BLINK_TIME, 60
.equ EXERTION_BASE_SHIFT, 1
.equ EXERTION_MAX_SHIFT, 2
.equ COST_PER_FLAP, 16
.equ HORIZONTAL_TRAVEL, 2
.equ BODY_T_X, 0
.equ BODY_T_Y, 2
.equ BODY_T_VX, 4
.equ BODY_T_VY, 6
.equ BODY_T_IMPULSE_X, 8
.equ BODY_T_IMPULSE_Y, 10
.equ BODY_T_COLLIDE_TYPE, 12
.equ BODY_T_MASS, 13
.equ BALLOON_T_POPPING_CTR, 14
.equ ACTOR_T_FLAP_CTR, 14
.equ ACTOR_T_TARGET_CTR, 15
.equ ACTOR_T_ARCHETYPE_PTR, 16
.equ ACTOR_T_EXERTION, 20
.equ ACTOR_T_TILE_OFFSET, 22
.equ ACTOR_T_FRAME, 24
.equ ACTOR_T_FRAME_DELAY, 25
.equ ACTOR_T_ANIMATION, 26
.equ ACTOR_T_BALLOONS, 27
.equ ACTOR_T_INVULNERABILITY, 28
.equ ACTOR_T_IDENTITY, 29
.equ ACTOR_T_STATE, 30
.equ ACTOR_T_TARGET, 31
.section .iwram
@@ See the file HACKING.org for details of these structures
.align 2
.lcomm balloonists, BALLOONIST_LEN*N_BALLOONISTS
.align 2
.lcomm balloons, BODY_LEN*MAX_BALLOONS*N_BALLOONISTS
.align 2
.lcomm arena, 4
.lcomm popped_balloon, 8
.section .text
.arm
.align
.include "gba.inc"
@@@ play_game(r0 = us, r1 = our color, r2 = them, r3 = their color, r4 = arena)
.global play_game
play_game:
stmfd sp!, {r5-r12,lr}
stmfd sp!, {r0-r3}
bl random_word
tst r0, #1
ldreq r0, =other_song_data
ldrne r0, =in_game_song_data
bl music_play_song
ldmfd sp!, {r0-r3}
@@ set things up based on arguments passed in
ldr r5, =arena_table
ldr r5, [r5, r4, lsl #2]
ldr r4, =arena
str r5, [r4]
@@ put the balloonist spawns on the stack for later
ldrh r6, [r5, #ARENA_T_SPAWN_1_X]
ldrh r7, [r5, #ARENA_T_SPAWN_2_X]
stmfd sp!, {r2,r3,r7}
stmfd sp!, {r0,r1,r6}
bl setup_arena
bl setup_balloons
ldr r8, =balloonists
ldmfd sp!, {r6,r7,r10}
mov r9, #1
bl setup_balloonist
ldr r8, =balloonists+BALLOONIST_LEN
ldmfd sp!, {r6,r7,r10}
mov r9, #2
bl setup_balloonist
@@ Run the core game loop
.Lcoreloop:
0:
ldr r5, =arena
ldr r5, [r5]
@@ PROCESS ACTORS
@@ If we're in demo mode, we'd want to call an alternate
@@ routine for the player.
ldr r4, =balloonists
add r6, r4, #BALLOONIST_LEN
bl update_exertion
bl apply_gravity
bl human_input
bl process_invulnerability
mov r0, r4
mov r4, r6
mov r6, r0
bl update_exertion
bl apply_gravity
bl enemy_action
bl process_invulnerability
@@ CHECK COLLISIONS
ldr r4, =balloonists
add r6, r4, #BALLOONIST_LEN
bl check_balloonist_balloonist_collision
bl check_balloon_collisions
mov r0, r4
mov r4, r6
mov r6, r0
bl check_balloon_collisions
@@ UPDATE MOTION
ldr r4, =balloonists
bl update_balloonist_motion
add r4, r4, #BALLOONIST_LEN
bl update_balloonist_motion
ldr r4, =balloonists
bl apply_arena_wrapping
add r4, r4, #BALLOONIST_LEN
bl apply_arena_wrapping
@@ check if any terminating condition occurred
ldr r4, =balloonists
add r6, r4, #BALLOONIST_LEN
ldrb r2, [r4, #ACTOR_T_BALLOONS] @ number of balloons
cmp r2, #0
beq .Lmatch_finished
mov r0, r4
mov r4, r6
mov r6, r0
ldrb r2, [r4, #ACTOR_T_BALLOONS] @ number of balloons
cmp r2, #0
beq .Lmatch_finished
@@ RENDER GRAPHICS
bl gfx_wait_vblank
mov r0, #oam_base
ldr r4, =balloonists
bl render_balloonist
add r4, r4, #BALLOONIST_LEN
bl render_balloonist
bl render_popped_balloon
bl disable_remaining_sprites
@@ loop forever
b .Lcoreloop
.Lmatch_finished:
ldrb r5, [r4, #ACTOR_T_IDENTITY]
mov r1, #FRAME_DIE<<1
strb r1, [r4, #ACTOR_T_FRAME]
mov r1, #FRAME_WIN<<1
strb r1, [r6, #ACTOR_T_FRAME]
@@ ensure we're not flashing
mov r0, #0
strb r1, [r4, #ACTOR_T_INVULNERABILITY]
strb r1, [r6, #ACTOR_T_INVULNERABILITY]
@@ play falling sound
stmfd sp!, {r0-r3}
mov r0, #0
mov r1, #0x10
mov r2, #0xd000
bl music_play_sfx
mov r0, #0
mov r1, #0x130
mov r2, #0x0800
bl music_play_sfx
ldmfd sp!, {r0-r3}
@@ crude hack to show the loser plumetting
0: stmfd sp!, {r5}
bl gfx_wait_vblank
mov r0, #oam_base
ldr r4, =balloonists
bl render_balloonist
add r4, r4, #BALLOONIST_LEN
bl render_balloonist
bl render_popped_balloon
bl disable_remaining_sprites
ldmfd sp!, {r5}
ldr r4, =balloonists
cmp r5, #2
addeq r4, r4, #BALLOONIST_LEN
ldrsh r0, [r4, #BODY_T_Y]
add r0, r0, #1<<4
strh r0, [r4, #BODY_T_Y]
cmp r0, #160<<4
blt 0b
mov r0, #0
mov r1, #0
mov r2, #0x0000
bl music_play_sfx
mov r0, #3
mov r1, #0
ldr r2, =0b010000011000
bl music_play_sfx
cmp r5, #1
moveq r0, #OUTCOME_LOSE
movne r0, #OUTCOME_WIN
ldmfd sp!, {r5-r12,pc}
@@ r5 = arena ptr
setup_arena:
stmfd sp!, {lr}
@@ setup the background
bl gfx_wait_vblank
mov r0, #REG_DISPCNT
mov r1, #0x40
orr r1, r1, #0b10111<<8 @ display sprites.
strh r1, [r0]
mov r1, #0b00000100
strh r1, [r0, #8] @ REG_BG0
mov r1, #0
strh r1, [r0, #0x10] @ REG_BG0SCX
strh r1, [r0, #0x12] @ REG_BG0SCY
mov r1, #0b00001001
orr r1, r1, #0x0100
strh r1, [r0, #0xA] @ REG_BG1
mov r1, #0
strh r1, [r0, #0x14] @ REG_BG1SCX
strh r1, [r0, #0x16] @ REG_BG1SCY
mov r1, #0b00001110
orr r1, r1, #0x6200
strh r1, [r0, #0xC] @ REG_BG2
mov r0, #vram_base
mov r1, #0x4000
bl dma_zero32
mov r0, #1
ldr r1, [r5, #ARENA_T_MIDGROUND_PTR]
bl copy_tilemap_to_vram_bg
mov r0, #2
ldr r1, [r5, #ARENA_T_MIDGROUND_PTR+4]
bl copy_tilemap_to_vram_bg
ldr r0, [r5, #ARENA_T_PALETTE_PTR]
bl gfx_load_bg_palette
ldmfd sp!, {pc}
@@ Expects r0 to point to our current offset into OAM
disable_remaining_sprites:
mov r8, #oam_base
add r8, r8, #1024
1: mov r1, #0x200
strh r1, [r0], #2
mov r1, #0
strh r1, [r0], #2
strh r1, [r0], #2
strh r1, [r0], #2
cmp r0, r8
blt 1b
mov pc, lr
setup_balloons:
stmfd sp!, {lr}
@@ copy in sprite tiles
@@ we start with the two balloon frames
mov r5, #vram_base
add r5, r5, #0x10000
ldr r1, =balloon_sprites
ldr r2, =balloon_sprites_end
sub r2, r2, r1
mov r0, r5
bl dma_copy32
@@ Drop in the invariant palette at 0 for any overlays or
@@ anything we might use.
mov r0, #palram_base
add r0, r0, #0x200
ldr r1, =invariant_palette
mov r2, #32
bl dma_copy32
@@ Make balloons transparent
ldr r1, =REG_BLDCNT
mov r3, #0x0f40
strh r3, [r1], #2
mov r3, #0x0800
orr r3, r3, #0x10
strh r3, [r1]
ldr r0, =balloons
mov r1, #BODY_LEN*MAX_BALLOONS*N_BALLOONISTS
bl dma_zero32
ldr r0, =popped_balloon
mov r1, #0
str r1, [r0], #4
str r1, [r0], #4
ldmfd sp!, {pc}
@@ copy in frames, setup actor structure
@@ r5 = vram offset (the value passed in isn't use; we use r9
@@ instead and copy to a fixed location)
@@ r6 = archetype index
@@ r7 = palettes
@@ r8 = actors ptr
@@ r9 = palette index / which player
@@ r10 = spawn point (y<<8 | x)
setup_balloonist:
stmfd sp!, {lr}
ldr r4, =archetype_table
add r4, r4, r6, lsl #5
str r4, [r8, #ACTOR_T_ARCHETYPE_PTR]
@@ Fill in body structure
and r1, r10, #0xff
lsl r1, r1, #PHYS_FIXED_POINT
strh r1, [r8, #BODY_T_X] @ X
lsr r1, r10, #8
lsl r1, r1, #PHYS_FIXED_POINT
strh r1, [r8, #BODY_T_Y] @ Y
mov r1, #0
str r1, [r8, #BODY_T_VX] @ x and y velocity
str r1, [r8, #BODY_T_IMPULSE_X] @ x and y impulse
mov r1, #1
strb r1, [r8, #BODY_T_COLLIDE_TYPE] @ 16x16 collisions
ldrb r1, [r4, #ARCHETYPE_T_MASS]
strb r1, [r8, #BODY_T_MASS]
@@ Copy frames into VRAM
mov r5, #vram_base
add r5, #0x10000
add r5, r5, r9, lsl #9 @ 32 bytes per tile, 16 tiles per group
add r4, r4, #ARCHETYPE_T_FRAME_PTRS
.rept 4 @ fly, bump, die, win
ldr r1, [r4], #4
mov r2, #16*16/2
mov r0, r5
add r5, r5, r2
bl dma_copy32
.endr
@@ Setup palette
mov r0, #palram_base
add r0, #0x200
add r0, r0, r9, lsl #5
ldr r2, =invariant_palette
ldmia r2, {r1-r4}
stmia r0!, {r1-r4}
and r1, r7, #0xff
lsr r3, r7, #8
ldr r2, =palette_table
add r3, r2, r3, lsl #3
add r2, r2, r1, lsl #3
ldmia r2, {r1,r2}
stmia r0!, {r1,r2}
ldmia r3, {r1,r2}
stmia r0!, {r1,r2}
@@ Fill in balloonist structure
strb r9, [r8, #ACTOR_T_IDENTITY]
mov r1, r9, lsl #4
strh r1, [r8, #ACTOR_T_TILE_OFFSET]
mov r1, #0 @ frame and facing
strb r1, [r8, #ACTOR_T_FRAME]
strb r1, [r8, #ACTOR_T_FRAME_DELAY]
strb r1, [r8, #ACTOR_T_ANIMATION]
strb r1, [r8, #ACTOR_T_INVULNERABILITY]
strh r1, [r8, #ACTOR_T_EXERTION]
strb r1, [r8, #ACTOR_T_STATE]
strb r1, [r8, #ACTOR_T_TARGET]
mov r1, #1
strb r1, [r8, #ACTOR_T_FLAP_CTR]
strb r1, [r8, #ACTOR_T_TARGET_CTR]
@@ start with 4 balloons
mov r5, #0b1111
strb r5, [r8, #ACTOR_T_BALLOONS]
ldr r0, =balloons
sub r2, r9, #1
add r0, r0, r2, lsl #7 @ log2(BODY_LEN*MAX_BALLOONS)
@@ place balloons initially in fixed positions in an arc around the player
ldrsh r2, [r8, #BODY_T_X]
ldrsh r3, [r8, #BODY_T_Y]
ldr r4, =initial_balloon_offs
1: ldrsb r1, [r4], #1
add r1, r2, r1, lsl #PHYS_FIXED_POINT
strh r1, [r0, #BODY_T_X]
ldrsb r1, [r4], #1
add r1, r3, r1, lsl #PHYS_FIXED_POINT
strh r1, [r0, #BODY_T_Y]
mov r1, #0
strb r1, [r0, #BODY_T_COLLIDE_TYPE]
mov r1, #BALLOON_MASS
strb r1, [r0, #BODY_T_MASS]
add r0, r0, #BODY_LEN
2: lsrs r5, r5, #1
beq 1f
tst r5, #1
bne 1b
b 2b
1:
ldmfd sp!, {pc}
@@ r0 = OAM pointer
render_popped_balloon:
stmfd sp!, {lr}
ldr r2, =popped_balloon
ldrb r1, [r2, #5] @ count
subs r1, r1, #1
bmi 9f
strb r1, [r2, #5]
ldrsh r1, [r2, #2]
asr r1, r1, #PHYS_FIXED_POINT
sub r1, r1, #4
and r1, r1, #255
orr r1, r1, #0x400
strh r1, [r0], #2
ldrsh r1, [r2, #0]
asr r1, r1, #PHYS_FIXED_POINT
sub r1, r1, #4
mov r3, #512
sub r3, r3, #1
and r1, r1, r3
strh r1, [r0], #2
mov r1, #BALLOON_POP_TILE @ tile idx
ldrb r3, [r2, #4] @ color
orr r1, r1, r3, lsl #12
strh r1, [r0], #2
mov r1, #0 @ rotation bits
strh r1, [r0], #2
9: ldmfd sp!, {pc}
@@ render actor and balloons to oam
@@ r4 = us
@@ r0 = OAMptr
render_balloonist:
stmfd sp!, {lr}
ldrb r9, [r4, #ACTOR_T_IDENTITY]
ldrb r5, [r4, #ACTOR_T_BALLOONS] @ number of balloons
ldr r3, =balloons
sub r2, r9, #1
add r3, r3, r2, lsl #7 @ log2(BODY_LEN*MAX_BALLOONS)
.Lnext_balloon:
cmp r5, #0
beq .Ldone
tst r5, #1
lsreq r5, r5, #1
addeq r3, r3, #BODY_LEN
beq .Lnext_balloon
ldrsh r1, [r3, #BODY_T_Y]
asr r1, r1, #PHYS_FIXED_POINT
sub r1, r1, #4
and r1, r1, #255
orr r1, r1, #0x400
strh r1, [r0], #2
ldrsh r1, [r3, #BODY_T_X]
asr r1, r1, #PHYS_FIXED_POINT
sub r1, r1, #4
mov r2, #512
sub r2, r2, #1
and r1, r1, r2
strh r1, [r0], #2
@@ would be nice to make priority random
mov r1, #0 @ tile idx
orr r1, r1, r9, lsl #12 @ player's palette
strh r1, [r0], #2
mov r1, #0 @ rotation bits
strh r1, [r0], #2
add r3, r3, #BODY_LEN
lsrs r5, r5, #1
bne .Lnext_balloon
.Ldone:
ldrb r1, [r4, #ACTOR_T_INVULNERABILITY]
tst r1, #0b1
bne 8f
ldrsh r1, [r4, #BODY_T_Y] @ y coordinate
asr r1, r1, #PHYS_FIXED_POINT
sub r1, r1, #8
and r1, r1, #0xff
strh r1, [r0], #2
ldrb r3, [r4, #ACTOR_T_FRAME] @ mode
ldrsh r1, [r4, #BODY_T_X] @ x coordinate
asr r1, r1, #PHYS_FIXED_POINT
sub r1, r1, #8
mov r2, #0x200
sub r2, r2, #1
and r1, r1, r2
orr r1, r1, #0x4000 @ size = 16x16
and r2, r3, #1 @ take the facing bit
orr r1, r1, r2, lsl #12
strh r1, [r0], #2
bic r3, r3, #1
lsl r3, r3, #1
ldrh r2, [r4, #ACTOR_T_TILE_OFFSET]
add r2, r2, r3
orr r2, r9, lsl #12
orr r2, r2, #0x0800 @ priority 2 (behind balloons)
strh r2, [r0], #2
@ rotation bits
mov r1, #0
strh r1, [r0], #2
8: @@ drop sweatdrop if overexerted
ldrh r1, [r4, #ACTOR_T_EXERTION]
ldr r2, [r4, #ACTOR_T_ARCHETYPE_PTR]
ldrb r2, [r2, #ARCHETYPE_T_STAMINA]
lsl r2, #EXERTION_BASE_SHIFT
cmp r1, r2
blt 9f
ldrsh r1, [r4, #BODY_T_Y]
asr r1, #PHYS_FIXED_POINT
sub r1, r1, #8
and r1, r1, #0xff
orr r1, r1, #0x400
strh r1, [r0], #2
ldrsh r1, [r4, #BODY_T_X]
asr r1, #PHYS_FIXED_POINT
and r1, r1, #0xff
strh r1, [r0], #2
mov r2, #2
strh r2, [r0], #2
mov r1, #0
strh r1, [r0], #2
9: ldmfd sp!, {pc}
.pool
@ FIXME replace raw compares with table lookups of player speeds
@@ r4 = us
human_input:
stmfd sp!, {lr}
ldr r2, =key_input
ldrh r1, [r2], #2
ldrh r2, [r2] @ debounce
.Lregular_input:
@@ B button (flap)
tst r2, #0b10 @ B button
bne .Lnot_flapping
@@ bottom two bits are right and left, which is our direction
mvn r0, r1, lsr #4
bl flap
.Lnot_flapping:
9:
ldmfd sp!, {pc}
@@ r0 = direction (lowest bits: LR)
@@ r4 = actor
@@ XXX maybe give us a little extra impulse on the frame after debounce?
flap:
tst r0, #0b11
beq 1f
tst r0, #0b01
ldrb r3, [r4, #ACTOR_T_FRAME]
bicne r3, r3, #1 @ face right
orreq r3, r3, #1 @ face left
strb r3, [r4, #ACTOR_T_FRAME]
1: ldr r3, [r4, #ACTOR_T_ARCHETYPE_PTR]
ldrh r1, [r4, #ACTOR_T_EXERTION]
ldrb r2, [r3, #ARCHETYPE_T_STAMINA]
lsl r2, #EXERTION_BASE_SHIFT
cmp r1, r2
bgt 0f
add r1, r1, #COST_PER_FLAP
cmp r1, r2
addge r1, r1, r2
strh r1, [r4, #ACTOR_T_EXERTION]
ldrb r2, [r3, #ARCHETYPE_T_STRENGTH]
ldrsh r1, [r4, #BODY_T_IMPULSE_Y]
sub r1, r1, r2, lsl #4
strh r1, [r4, #BODY_T_IMPULSE_Y]
tst r0, #0b11
beq 0f
tst r0, #0b01
ldrsh r3, [r4, #BODY_T_IMPULSE_X]
addne r3, r3, #HORIZONTAL_TRAVEL<<(IMPULSE_SCALING-2)
subeq r3, r3, #HORIZONTAL_TRAVEL<<(IMPULSE_SCALING-2)
strh r3, [r4, #BODY_T_IMPULSE_X]
0:
bx lr
@@ r4 = us
@@ r5 = arena
@@ r6 = them
enemy_action:
ldrb r0, [r4, #ACTOR_T_STATE]
ldr r1, =dispatch_table
ldr pc, [r1, r0, lsl #2]
initial_state:
mov r0, #ST_APPROACH_PLAYER
strb r0, [r4, #ACTOR_T_STATE]
bx lr
approach_player:
stmfd sp!, {lr}
@@ XXX needs to become a separate state variable
ldrb r0, [r4, #ACTOR_T_FLAP_CTR]
subs r0, r0, #1
movne r3, #0
bne 0f
ldrsh r2, [r4, #BODY_T_X]
ldrsh r3, [r6, #BODY_T_X]
asr r2, #2
subs r7, r2, r3, asr #2
movlt r0, #0b01
movgt r0, #0b10
moveq r0, #0
bl flap
bl calculate_flap_equilibrium
mov r9, r0
ldrsh r2, [r4, #BODY_T_Y]
ldrsh r3, [r6, #BODY_T_Y]
subs r8, r2, r3
@@ XXX everything here is basically bullshit, since we
@@ actually need to find solutions to the differential equations
@@ that describe the motion of this object to do this properly.
@@ r7 = delta x (12.4), r8 = delta y (12.4), r9 = flap equilibrium
@@ n = dx / htravel
rsb r0, r7, #0
mov r1, #HORIZONTAL_TRAVEL+3 @ XXX needs fudging
swi #6<<16 @ slow division
movs r7, r0, asr #4
rsbmi r7, r7, #0 @ abs
@@ r7 = n, number of flaps to travel dx
bl calculate_gravity_plus_lift
movs r1, r0
moveq r1, #1
mov r0, r8
swi #6<<16
@@ r0 = dy / (gravity+lift)
@@ this is the number of frames in total we'd need to add/drop to travel dy
movs r1, r7
moveq r1, #1
swi #6<<16
@@ r0 = r0 / n
@@ this is the number of extra frames on this flap, if we
@@ expect to make n flaps in total
@@ we add this value to the flap equilibrium to figure out our
@@ timing. really, we should be incorporating our character's
@@ stamina, though.
@@ well, we would, except that's bullshit; we take some of
@@ that information and drastically modify our flap rate based on
@@ it.
@@ adds r0, r0, r9
@@ movmi r0, r9
asr r0, #2
cmp r0, #0
lsrpl r0, r9, #1
lslmi r0, r9, #1
and r0, r0, #0xff
0: strb r0, [r4, #ACTOR_T_FLAP_CTR]
ldmfd sp!, {pc}
@@ wish we had vcnt
@@ this is per Hacker's Delight
@@ uses r0-r2
popcnt:
ldr r1, =0x55555555
and r1, r1, r0, lsr #1
sub r0, r0, r1
@@ There must be a way to avoid this intermediary register
ldr r2, =0x33333333
and r1, r2, r0, lsr #2
and r0, r0, r2
add r0, r0, r1
add r0, r0, r0, lsr #4
ldr r1, =0x0f0f0f0f
and r0, r0, r1
add r0, r0, r0, lsr #8
add r0, r0, r0, lsr #16
and r0, r0, #0x3f
mov pc, lr
maintain_equilibrium:
stmfd sp!, {lr}
ldrb r0, [r4, #ACTOR_T_FLAP_CTR]
subs r0, r0, #1
movne r3, #0
bne 0f
mov r0, #0
bl flap
bl calculate_flap_equilibrium
0: strb r0, [r4, #ACTOR_T_FLAP_CTR]
ldmfd sp!, {pc}
@@ r4 = balloonist
@@ r5 = arena
@@ XXX It occurs to me, suddenly, that if the game loop were
@@ structured in a specific order, we would have already
@@ calculated this value and applied it as the only impulses on
@@ the actor. So instead of recomputing it all the time, we
@@ could just use ACTOR_T_IMPULSE_Y. Maybe later.
calculate_gravity_plus_lift:
stmfd sp!, {lr}
ldrb r0, [r4, #ACTOR_T_BALLOONS]
bl popcnt
mov r1, #BALLOON_LIFT
ldrb r2, [r5, #ARENA_T_GRAVITY]
mla r0, r1, r0, r2
@@ r0 = number of balloons * lift + gravity
ldmfd sp!, {pc}
@@ flap count to maintain current altitude:
@@ (strength<<4) / (gravity + lift) * cost
calculate_flap_equilibrium:
stmfd sp!, {lr}
bl calculate_gravity_plus_lift
mov r1, #COST_PER_FLAP
mul r1, r0, r1
ldr r0, [r4, #ACTOR_T_ARCHETYPE_PTR]
ldrb r0, [r0, #ARCHETYPE_T_STRENGTH]
lsl r0, #12
swi #6<<16 @ slow division
asr r0, #4
ldmfd sp!, {pc}
.align 2
dispatch_table:
.equ ST_INITIAL, 0
.equ ST_MAINTAIN_EQUILIBRIUM, 1
.equ ST_APPROACH_PLAYER, 2
.word initial_state, maintain_equilibrium, approach_player
update_exertion:
ldrh r1, [r4, #ACTOR_T_EXERTION]
ldr r2, [r4, #ACTOR_T_ARCHETYPE_PTR]
ldrb r2, [r2, #ARCHETYPE_T_STAMINA]
lsl r2, #EXERTION_BASE_SHIFT
cmp r1, r2
moveq r1, #1
subs r1, r1, #1
strgeh r1, [r4, #ACTOR_T_EXERTION]
mov pc, lr
@@ r4 = balloonist
@@ r5 = arena
apply_gravity:
ldrsh r1, [r4, #BODY_T_IMPULSE_Y]
ldrsb r0, [r5, #ARENA_T_GRAVITY]
add r1, r1, r0
strh r1, [r4, #BODY_T_IMPULSE_Y]
mov pc,lr
@@ r4 = balloonist
process_invulnerability:
ldrb r0, [r4, #ACTOR_T_INVULNERABILITY]
subs r0, r0, #1
movle r0, #0
strb r0, [r4, #ACTOR_T_INVULNERABILITY]
mov pc, lr
@@ r4 = this balloonist
@@ r5 = arena
update_balloonist_motion:
stmfd sp!, {lr}
@@ update balloons
ldrb r9, [r4, #ACTOR_T_BALLOONS] @ number of balloons
ldrsh r7, [r4, #BODY_T_X]
ldrsh r8, [r4, #BODY_T_Y]
mov r6, r4
ldrb r0, [r4, #ACTOR_T_IDENTITY]
sub r0, r0, #1
stmfd sp!, {r4}
ldr r4, =balloons
add r4, r4, r0, lsl #7 @ log2(BODY_LEN*MAX_BALLOONS)
0: tst r9, #1
beq 1f
ldrsh r0, [r4, #BODY_T_IMPULSE_Y]
add r0, r0, #BALLOON_LIFT
strh r0, [r4, #BODY_T_IMPULSE_Y]
bl update_body_motion
@@ constrain to be within a given radius of the ballonist
ldrsh r0, [r4, #BODY_T_X]
ldrsh r1, [r4, #BODY_T_Y]
sub r0, r0, r7
sub r1, r1, r8
mul r2, r0, r0
mla r2, r1, r1, r2
asr r2, #PHYS_FIXED_POINT*2
subs r3, r2, #BALLOON_DISTANCE*BALLOON_DISTANCE
ble 1f
@@ bring balloons closer and apply lift to balloonist
@@ r4 = body A
@@ r6 = body B
@@ r7 = distance squared (in pixels)
@@ r8 = penetration squared (in pixels)
stmfd sp!, {r0-r12}
mov r7, r2
mov r8, r3
bl compute_contact_normal_opp
bl resolve_contact
ldmfd sp!, {r0-r12}
1: add r4, r4, #BODY_LEN
lsrs r9, r9, #1
bne 0b
ldmfd sp!, {r4}
@@ update balloonist
bl update_body_motion
ldmfd sp!, {pc}
@@ r4 = body
@@ r5 = arena
update_body_motion:
@@ we should be able to avoid saving a few of these registers
@@ if we're a little more careful.
stmfd sp!, {r6-r10,lr}
ldrb r3, [r4, #BODY_T_MASS]
@@ delta = mass * impulse
mov r0, #0
ldrsh r1, [r4, #BODY_T_IMPULSE_X]
strh r0, [r4, #BODY_T_IMPULSE_X]
mul r0, r1, r3
asr r0, r0, #IMPULSE_SCALING
@@ velocity += delta / 2
ldrsh r1, [r4, #BODY_T_VX]
add r1, r1, r0, asr #1
@@ position += velocity
ldrsh r2, [r4, #BODY_T_X]
add r2, r2, r1
@@ delta = mass * impulse
mov r6, #0
ldrsh r7, [r4, #BODY_T_IMPULSE_Y]
strh r6, [r4, #BODY_T_IMPULSE_Y]
mul r6, r7, r3
asr r6, r6, #IMPULSE_SCALING
@@ velocity += delta / 2
ldrsh r7, [r4, #BODY_T_VY]
add r7, r7, r6, asr #1
@@ position += velocity
ldrsh r8, [r4, #BODY_T_Y]
add r8, r8, r7
@@ at this point
@@ r0 = accel delta X
@@ r1 = vx
@@ r2 = x
@@ r3 = mass (unused from here on)
@@ r4 = actor
@@ r5 = arena
@@ r6 = accel delta y
@@ r7 = vy
@@ r8 = y
ldrb r3, [r4, #BODY_T_COLLIDE_TYPE]
stmfd sp!, {r0,r4,r6}
@@ free: r0,r4,r6,r9,r10
@@ we check the minkowski sum of an 8x8 tile and either an 8x8 or 16x16 sprite
@@ collide with playfield
@@ we check the n tiles that compose the balloonist and set
@@ a flag for each, then decide how to respond based on those
@@ flags and current velocity
@@ XXX subtract 8+r3<<2 from positions here for leftmost corner
stmfd sp!, {r1,r2,r8}
ldr r6, [r5, #ARENA_T_MIDGROUND_PTR]
ldrb r9, [r6], #1
ldrb r10, [r6], #1
@@ scale to pixels
asr r2, #PHYS_FIXED_POINT
asr r8, #PHYS_FIXED_POINT
sub r2, r2, #4
sub r8, r8, #4
subs r2, r2, r3, lsl #2
movmi r2, #0
subs r8, r8, r3, lsl #2
movmi r8, #0
@@ scale to tiles
lsr r2, #3
lsr r8, #3
mla r0, r8, r9, r2
lsl r0, r0, #1
@@ replace r10 with maximum offset of the map
mul r2, r9, r10
mov r10, r2, lsl #1