forked from mitch3b/ChickenOfTheFarm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ChickenOfTheFarm.c
3852 lines (3447 loc) · 112 KB
/
ChickenOfTheFarm.c
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
#include "resources/resources.h"
//
// DEFINES
//
// Controller bits
#define BUTTON_A 0x80
#define BUTTON_B 0x40
#define BUTTON_SELECT 0x20
#define BUTTON_START 0x10
#define BUTTON_UP 0x08
#define BUTTON_DOWN 0x04
#define BUTTON_LEFT 0x02
#define BUTTON_RIGHT 0x01
// Palette addresses
#define BACKGROUND0_0 0x00
#define BACKGROUND0_1 0x01
#define BACKGROUND0_2 0x02
#define BACKGROUND0_3 0x03
#define BACKGROUND1_0 0x04
#define BACKGROUND1_1 0x05
#define BACKGROUND1_2 0x06
#define BACKGROUND1_3 0x07
#define BACKGROUND2_0 0x08
#define BACKGROUND2_1 0x09
#define BACKGROUND2_2 0x0A
#define BACKGROUND2_3 0x0B
#define BACKGROUND3_0 0x0C
#define BACKGROUND3_1 0x0D
#define BACKGROUND3_2 0x0E
#define BACKGROUND3_3 0x0F
#define SPRITE0_0 0x10
#define SPRITE0_1 0x11
#define SPRITE0_2 0x12
#define SPRITE0_3 0x13
#define SPRITE1_0 0x14
#define SPRITE1_1 0x15
#define SPRITE1_2 0x16
#define SPRITE1_3 0x17
#define SPRITE2_0 0x18
#define SPRITE2_1 0x19
#define SPRITE2_2 0x1A
#define SPRITE2_3 0x1B
#define SPRITE3_0 0x1C
#define SPRITE3_1 0x1D
#define SPRITE3_2 0x1E
#define SPRITE3_3 0x1F
// Registers
#define PPU_CTRL *((unsigned char*)0x2000)
#define PPU_MASK *((unsigned char*)0x2001)
#define PPU_ADDRESS *((unsigned char*)0x2006)
#define PPU_DATA *((unsigned char*)0x2007)
// These are used to determine how much screen to show above and below frog while scrolling
#define MAX_BOTTOM_BUFFER 0xAF
#define MAX_TOP_BUFFER 0x3F
#define ARROW_SPEED 3
#define MAX_NUM_SPRITES 21 // need to reserve enough space for max sprites
#define FIRST_ENEMY_SPRITE 84
//#define LAST_ENEMY_SPRITE 124 //Reserve 10 sprites for now
#define INVALID_ID 0
#define FLY_ID 1
#define ARROW_ID 2
#define BIRD_ID 3
#define PORTAL_ID 4
#define KEY_ID 5
#define SNAKE_ID 6
#define HEART_ID 7
#define CHICKEN_ID 8
#define ID_COUNT 9
#define SET_COLOR( index, color ) PPU_ADDRESS = 0x3F; PPU_ADDRESS = index; PPU_DATA = color
#define BIRD_SPEED_CONTROL 2
#define TONGUE_EXTEND_DELAY 0
#define TONGUE_RETRACT_DELAY 4
//
// TYPEDEFS
//
typedef enum _GameState_t
{
TITLE_SCREEN_STATE = 0,
DEMO_STATE,
GAME_RUNNING_STATE,
ENDING_STATE,
} GameState_t;
typedef enum _FrogAnimationState_t
{
FROG_NORMAL = 0,
FROG_HOP,
FROG_JUMP,
FROG_SUSPENDED,
FROG_FALLING,
FROG_LANDING,
} FrogAnimationState_t;
typedef enum _ChickenAnimationState_t
{
CHICKEN_STATE0 = 0,
CHICKEN_STATE1,
CHICKEN_STATE2,
CHICKEN_STATE3,
} ChickenAnimationState_t;
typedef enum _TongueState_t
{
TONGUE_NORMAL = 0,
TONGUE_EXTENDING,
TONGUE_EXTENDING2,
TONGUE_OUT,
TONGUE_RETRACTING,
TONGUE_RETRACTING2,
TONGUE_CLEANUP,
}
TongueState_t;
#define CHICKEN_INVALID 0x00
#define CHICKEN_STANDING 0x01
#define CHICKEN_WALKING 0x02
#define CHICKEN_RUNNING 0x04
#define CHICKEN_SPRINTING 0x08
#define CHICKEN_RETREATING 0x10
#define CHICKEN_PECKING 0x20
#define CHICKEN_FLYING 0x40
typedef struct {
unsigned char state;
unsigned char count;
} chicken_move_t;
#define MOVE_COUNT_0 2
chicken_move_t ChickenMove0[MOVE_COUNT_0] = {{CHICKEN_STANDING, 120},
{CHICKEN_INVALID, 0},};
#define MOVE_COUNT_1 4
chicken_move_t ChickenMove1[MOVE_COUNT_1] = {{CHICKEN_WALKING|CHICKEN_FLYING, 120},
{CHICKEN_PECKING, 30},
{CHICKEN_RETREATING, 130},
{CHICKEN_INVALID, 0},};
#define MOVE_COUNT_2 6
chicken_move_t ChickenMove2[MOVE_COUNT_2] = {{CHICKEN_RUNNING|CHICKEN_FLYING, 40},
{CHICKEN_PECKING, 30},
{CHICKEN_RUNNING|CHICKEN_FLYING, 40},
{CHICKEN_PECKING, 30},
{CHICKEN_RETREATING, 150},
{CHICKEN_INVALID, 0},};
#define MOVE_COUNT_3 7
chicken_move_t ChickenMove3[MOVE_COUNT_3] = {{CHICKEN_SPRINTING|CHICKEN_FLYING, 20},
{CHICKEN_RUNNING, 30},
{CHICKEN_PECKING, 15},
{CHICKEN_RETREATING, 65},
{CHICKEN_PECKING, 15},
{CHICKEN_RETREATING, 65},
{CHICKEN_INVALID, 0},};
#define MOVE_TOTAL 4
chicken_move_t* ChickenMoveList[MOVE_TOTAL] = {
ChickenMove0,
ChickenMove1,
ChickenMove2,
ChickenMove3,
};
//According to nesdev, better to have arrays of attributes than arrays of objects: https://forums.nesdev.com/viewtopic.php?f=2&t=17465&start=0
typedef struct {
unsigned char id[MAX_NUM_SPRITES];
unsigned char startX[MAX_NUM_SPRITES];
unsigned char startY[MAX_NUM_SPRITES];
unsigned char startNametable[MAX_NUM_SPRITES];
unsigned char direction[MAX_NUM_SPRITES]; //Also palette color, bit 7 is direction
} sprites_t;
#define SPRITES_T_MEMBER_COUNT 9
static unsigned char SpriteSize[ID_COUNT] = {
0, //INVALID_ID 0
1, //FLY_ID 1
1, //ARROW_ID 2
2, //BIRD_ID 3
4, //PORTAL_ID 4
1, //KEY_ID 5
2, //SNAKE_ID 6
1, //HEART_ID 7
16, //CHICKEN_ID 8
};
void spawn_1_by_1_sprite(void);
void spawn_2_by_1_sprite(void);
void spawn_portal_sprite(void);
void spawn_chicken_sprite(void);
void despawn_1_sprite(void);
void despawn_2_sprite(void);
void despawn_portal_sprite(void);
void despawn_chicken_sprite(void);
void invalid_ai_handler(void);
void arrow_ai_handler(void);
void bird_ai_handler(void);
void item_ai_handler(void);
void snake_ai_handler(void);
void chicken_ai_handler(void);
void invalid_collision_handler(void);
void enemy_collision_handler(void);
void fly_collision_handler(void);
void heart_collision_handler(void);
void portal_collision_handler(void);
void key_collision_handler(void);
void PlaySoundEffects(void);
void init_physics(void);
typedef struct {
unsigned char pattern;
void (*spawn)(void);
void (*despawn)(void);
void (*ai_handler)(void);
void (*collision_handler)(void);
} sprite_properties_t;
sprite_properties_t spriteProperties[ID_COUNT] = {
{PATTERN_AAA_INVALID_0, &spawn_1_by_1_sprite, &despawn_1_sprite, &invalid_ai_handler, &invalid_collision_handler }, //INVALID_ID
{ PATTERN_FLY_0, &spawn_1_by_1_sprite, &despawn_1_sprite, &item_ai_handler, &fly_collision_handler }, //FLY_ID
{ PATTERN_ARROW_0, &spawn_1_by_1_sprite, &despawn_1_sprite, &arrow_ai_handler, &enemy_collision_handler }, //ARROW_ID
{ PATTERN_BIRD_0, &spawn_2_by_1_sprite, &despawn_2_sprite, &bird_ai_handler, &enemy_collision_handler }, //BIRD_ID
{ PATTERN_PORTAL_0, &spawn_portal_sprite, &despawn_portal_sprite, &item_ai_handler, &portal_collision_handler }, //PORTAL_ID
{ PATTERN_KEY_0, &spawn_1_by_1_sprite, &despawn_1_sprite, &item_ai_handler, &key_collision_handler }, //KEY_ID
{ PATTERN_SNAKE_0, &spawn_2_by_1_sprite, &despawn_2_sprite, &snake_ai_handler, &enemy_collision_handler }, //SNAKE_ID
{ PATTERN_HEART_0, &spawn_1_by_1_sprite, &despawn_1_sprite, &item_ai_handler, &heart_collision_handler }, //HEART_ID
{ PATTERN_CHICKEN_0, &spawn_chicken_sprite, &despawn_chicken_sprite, &chicken_ai_handler, &enemy_collision_handler }, //CHICKEN_ID
};
typedef struct {
const unsigned char* bottom_rle;
const unsigned char* top_rle;
const unsigned char* palette;
const unsigned char* sprites;
unsigned char numSprites;
} level_properties_t;
typedef struct {
unsigned char FrogStartX;
unsigned char FrogStartY;
unsigned char world;
} level_additional_properties_t;
#define NUM_LEVELS 26
level_properties_t LevelTable[NUM_LEVELS] = {
{Nametable_TitleScreen_bottom_rle, 0, TitleScreenPalette, 0, 0, },
{Nametable_Intro_bottom_rle, Nametable_Intro_top_rle, GrassPalette, Sprites_Intro, INTRO_ENEMY_COUNT, },
{Nametable_OneArrow_bottom_rle, Nametable_OneArrow_top_rle, GrassPalette, Sprites_OneArrow, ONEARROW_ENEMY_COUNT, },
{Nametable_ShortClimb_bottom_rle, Nametable_ShortClimb_top_rle, GrassPalette, Sprites_ShortClimb, SHORTCLIMB_ENEMY_COUNT, },
{Nametable_BirdClimb_bottom_rle, Nametable_BirdClimb_top_rle, GrassPalette, Sprites_BirdClimb, BIRDCLIMB_ENEMY_COUNT, },
{Nametable_OpenPit_bottom_rle, Nametable_OpenPit_top_rle, GrassPalette, Sprites_OpenPit, OPENPIT_ENEMY_COUNT, },
{Nametable_SmallPlatforms_bottom_rle, Nametable_SmallPlatforms_top_rle, GrassPalette, Sprites_SmallPlatforms, SMALLPLATFORMS_ENEMY_COUNT, },
{Nametable_ArrowClimb_bottom_rle, Nametable_ArrowClimb_top_rle, GrassPalette, Sprites_ArrowClimb, ARROWCLIMB_ENEMY_COUNT, },
{Nametable_TwoBirdClimb_bottom_rle, Nametable_TwoBirdClimb_top_rle, GrassPalette, Sprites_TwoBirdClimb, TWOBIRDCLIMB_ENEMY_COUNT, },
{Nametable_FirstIce_bottom_rle, Nametable_FirstIce_top_rle, IcePalette, Sprites_FirstIce, FIRSTICE_ENEMY_COUNT, },
{Nametable_IceStairs_bottom_rle, Nametable_IceStairs_top_rle, IcePalette, Sprites_IceStairs, ICESTAIRS_ENEMY_COUNT, },
{Nametable_IceRun_bottom_rle, Nametable_IceRun_top_rle, IcePalette, Sprites_IceRun, ICERUN_ENEMY_COUNT, },
{Nametable_ClimbOver_bottom_rle, Nametable_ClimbOver_top_rle, IcePalette, Sprites_ClimbOver, CLIMBOVER_ENEMY_COUNT, },
{Nametable_KeyRescue_bottom_rle, Nametable_KeyRescue_top_rle, IcePalette, Sprites_KeyRescue, KEYRESCUE_ENEMY_COUNT, },
{Nametable_LevelOutfacingShelves_bottom_rle, Nametable_LevelOutfacingShelves_top_rle, CastlePalette, Sprites_LevelOutfacingShelves, LEVELOUTFACINGSHELVES_ENEMY_COUNT,},
{Nametable_SnakeClimb_bottom_rle, Nametable_SnakeClimb_top_rle, CastlePalette, Sprites_SnakeClimb, SNAKECLIMB_ENEMY_COUNT, },
{Nametable_LevelUpAndDown_bottom_rle, Nametable_LevelUpAndDown_top_rle, CastlePalette, Sprites_LevelUpAndDown, LEVELUPANDDOWN_ENEMY_COUNT, },
{Nametable_LevelBackAndForth_bottom_rle, Nametable_LevelBackAndForth_top_rle, CastlePalette, Sprites_LevelBackAndForth, LEVELBACKANDFORTH_ENEMY_COUNT, },
{Nametable_CastleColumns_bottom_rle, Nametable_CastleColumns_top_rle, CastlePalette, Sprites_CastleColumns, CASTLECOLUMNS_ENEMY_COUNT, },
{Nametable_FirstRave_bottom_rle, Nametable_FirstRave_top_rle, CastlePalette, Sprites_FirstRave, FIRSTRAVE_ENEMY_COUNT, },
{Nametable_RaveSmallGaps_bottom_rle, Nametable_RaveSmallGaps_top_rle, CastlePalette, Sprites_RaveSmallGaps, RAVESMALLGAPS_ENEMY_COUNT, },
{Nametable_RaveSnakeStairs_bottom_rle, Nametable_RaveSnakeStairs_top_rle, CastlePalette, Sprites_RaveSnakeStairs, RAVESNAKESTAIRS_ENEMY_COUNT, },
{Nametable_RaveTwoTowers_bottom_rle, Nametable_RaveTwoTowers_top_rle, CastlePalette, Sprites_RaveTwoTowers, RAVETWOTOWERS_ENEMY_COUNT, },
{Nametable_RavePit_bottom_rle, Nametable_RavePit_top_rle, CastlePalette, Sprites_RavePit, RAVEPIT_ENEMY_COUNT, },
{Nametable_Intro_bottom_rle, Nametable_Intro_top_rle, GrassPalette, Sprites_Intro, INTRO_ENEMY_COUNT, },
{Nametable_EndingScreen_bottom_rle, 0, CastlePalette, 0, 0, },
};
level_additional_properties_t LevelProperties[NUM_LEVELS] = {
{0x10, 0xBF, 0}, // Nametable_TitleScreen_bottom_rle,
{0x10, 0xBF, 1}, // Nametable_Intro_bottom_rle,
{0x10, 0xBF, 1}, // Nametable_OneArrow_bottom_rle,
{0x10, 0xBF, 1}, // Nametable_ShortClimb_bottom_rle,
{0x10, 0xBF, 1}, // Nametable_BirdClimb_bottom_rle,
{0x10, 0xBF, 1}, // Nametable_OpenPit_bottom_rle,
{0x10, 0xBF, 1}, // Nametable_SmallPlatforms_bottom_rle,
{0x78, 0xBF, 1}, // Nametable_ArrowClimb_bottom_rle,
{0x78, 0xBF, 1}, // Nametable_TwoBirdClimb_bottom_rle,
{0x20, 0xBF, 2}, // Nametable_FirstIce_bottom_rle,
{0x10, 0xBF, 2}, // Nametable_IceStairs_bottom_rle,
{0x10, 0xBF, 2}, // Nametable_IceRun_bottom_rle,
{0x10, 0xBF, 2}, // Nametable_ClimbOver_bottom_rle,
{0x80, 0x4F, 2}, // Nametable_KeyRescue_bottom_rle,
{0x10, 0xCF, 3}, // Nametable_LevelOutfacingShelves_bottom_rle,
{0x20, 0xCF, 3}, // Nametable_SnakeClimb_bottom_rle,
{0x10, 0xCF, 3}, // Nametable_LevelUpAndDown_bottom_rle,
{0x10, 0xCF, 3}, // Nametable_LevelBackAndForth_bottom_rle,
{0xE0, 0xCF, 3}, // Nametable_CastleColumns_bottom_rle,
{0x10, 0xBF, 4}, // Nametable_FirstRave_bottom_rle,
{0x10, 0xBF, 4}, // Nametable_RaveSmallGaps_bottom_rle,
{0x70, 0xBF, 4}, // Nametable_RaveSnakeStairs_bottom_rle,
{0x10, 0x9F, 4}, // Nametable_RaveTwoTowers_bottom_rle,
{0x10, 0xBF, 4}, // Nametable_RavePit_bottom_rle,
{0x10, 0xBF, 5}, // Nametable_Intro_bottom_rle,
{0x10, 0xCF, 0}, // Nametable_EndingScreen_bottom_rle,
};
#define TONGUE_SOUND_ID 0
#define TONGUE_SOUND_LENGTH 12
unsigned char tongueSound400C[TONGUE_SOUND_LENGTH] = {0x39, 0x37, 0x35, 0x34, 0x34, 0x35, 0x37, 0x39, 0x30, 0x30, 0x3F, 0x30};
unsigned char tongueSound400E[TONGUE_SOUND_LENGTH] = {0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0A, 0x08, 0x05, 0x00, 0x00, 0x09, 0x00};
#define DAMAGE_SOUND_ID 1
#define DAMAGE_SOUND_LENGTH 8
unsigned char damageSound400C[DAMAGE_SOUND_LENGTH] = {0x3F, 0x30, 0x3C, 0x3A, 0x36, 0x3A, 0x3C, 0x30};
unsigned char damageSound400E[DAMAGE_SOUND_LENGTH] = {0x0F, 0x08, 0x09, 0x0B, 0x0D, 0x0E, 0x0F, 0x0E};
#define HOP_SOUND_ID 2
#define HOP_SOUND_LENGTH 6
unsigned char hopSound4008[HOP_SOUND_LENGTH] = {0x81, 0x81, 0x81, 0x81, 0x81, 0x81};
unsigned char hopSound400A[HOP_SOUND_LENGTH] = {0x80, 0x5C, 0x3A, 0xDF, 0x67, 0x67};
unsigned char hopSound400B[HOP_SOUND_LENGTH] = {0x02, 0x02, 0x02, 0x01, 0x01, 0x01};
#define JUMP_SOUND_ID 3
#define JUMP_SOUND_LENGTH 6
unsigned char jumpSound4008[JUMP_SOUND_LENGTH] = {0x81, 0x81, 0x81, 0x81, 0x81, 0x81};
unsigned char jumpSound400A[JUMP_SOUND_LENGTH] = {0xA8, 0x93, 0x7C, 0x3F, 0xEF, 0xEF};
unsigned char jumpSound400B[JUMP_SOUND_LENGTH] = {0x01, 0x01, 0x01, 0x01, 0x00, 0x00};
#define PAUSE_SOUND_ID 4
#define PAUSE_SOUND_LENGTH 12
unsigned char pauseSound4008[PAUSE_SOUND_LENGTH] = {0x81, 0x81, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x81, 0x81};
unsigned char pauseSound400A[PAUSE_SOUND_LENGTH] = {0x23, 0x31, 0x5E, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0x23, 0x31, 0x5E};
unsigned char pauseSound400B[PAUSE_SOUND_LENGTH] = {0x00, 0x00, 0x00, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x00, 0x00, 0x00};
#define ITEM_SOUND_ID 5
#define ITEM_SOUND_LENGTH 13
unsigned char itemSound4000[ITEM_SOUND_LENGTH] = {0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F};
unsigned char itemSound4001[ITEM_SOUND_LENGTH] = {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
unsigned char itemSound4002[ITEM_SOUND_LENGTH] = {0x6A, 0x6A, 0x6A, 0x59, 0x6A, 0x4B, 0x59, 0x3F, 0x4B, 0x34, 0x34, 0x34, 0x00};
#define PORTAL_SOUND_ID 6
#define PORTAL_SOUND_LENGTH 12
#define PORTAL_NOISE_DELAY 6
unsigned char portalSound4008[PORTAL_SOUND_LENGTH] = {0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81};
unsigned char portalSound400A[PORTAL_SOUND_LENGTH] = {0x80, 0x5C, 0x3A, 0xDF, 0x67, 0x67, 0xC4, 0x80, 0xDF, 0xBF, 0xFB, 0xF3};
unsigned char portalSound400B[PORTAL_SOUND_LENGTH] = {0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x03, 0x01, 0x05};
unsigned char portalSound400C[PORTAL_SOUND_LENGTH] = {0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F};
unsigned char portalSound400E[PORTAL_SOUND_LENGTH] = {0x0C, 0x09, 0x07, 0x07, 0x09, 0x0B, 0x0D, 0x0F, 0x01, 0x01, 0x01, 0x01};
#define MAX_FROG_SPEED 8
#define FROG_COLLISION_X_BUFFER 1
#define FROG_COLLISION_Y_BUFFER 1
//
// GLOBALS
//
// From NESDev:
//+------+----------+--------------------------------------+
//+ Byte | Bits | Description |
//+------+----------+--------------------------------------+
//| 0 | YYYYYYYY | Y Coordinate - 1. Consider the coor- |
//| | | dinate the upper-left corner of the |
//| | | sprite itself. |
//| 1 | IIIIIIII | Tile Index # |
//| 2 | vhp000cc | Attributes |
//| | | v = Vertical Flip (1=Flip) |
//| | | h = Horizontal Flip (1=Flip) |
//| | | p = Background Priority |
//| | | 0 = In front |
//| | | 1 = Behind |
//| | | c = Upper two (2) bits of colour |
//| 3 | XXXXXXXX | X Coordinate (upper-left corner) |
//+------+----------+--------------------------------------+}
#pragma bss-name (push, "OAM")
unsigned char sprites[256];
#pragma bss-name (pop)
#pragma bss-name (push, "ZEROPAGE")
static sprites_t gSpriteTable;
static unsigned char gSpriteState[MAX_NUM_SPRITES];
static unsigned char gSpriteOffset[MAX_NUM_SPRITES];
static unsigned char* gScratchPointer2;
static unsigned char gController1;
static unsigned char gPrevController1;
static unsigned char gPrevController1Change;
static unsigned char gFrameCounter;
static unsigned char gRNG;
static unsigned char gTmpX;
static unsigned char gTmpX2;
static unsigned char gX;
static unsigned char gY;
static unsigned char gYScroll;
static unsigned char gYPrevScroll;
static unsigned char gYNametable;
static unsigned char gYPrevNametable;
static unsigned char devnull;
static unsigned char i;
static unsigned char j;
static unsigned char k;
static unsigned char gTmp;
static unsigned char gTmp2;
static unsigned char gTmp3;
static unsigned char gTmp4;
static unsigned char gTmp5;
static unsigned char gTmp6;
static unsigned char gTmp7;
static unsigned char gTmp8;
static unsigned char x1;
static unsigned char y1;
static unsigned char width1;
static unsigned char height1;
static unsigned char x2;
static unsigned char y2;
static unsigned char width2;
static unsigned char height2;
static unsigned char gVelocity;
static unsigned char gSpeed;
static unsigned char gSpeedDirection;
static unsigned char gSpeedCounter;
static FrogAnimationState_t gFrogAnimationState;
static unsigned char gVblankPrevious;
#pragma bss-name (pop)
#pragma bss-name (push, "BSS")
// 16 x (15 + 15)
// TODO should use a bit per block instead of byte, but this is a lot easier at the moment
unsigned char collision[496];
static unsigned char numKeys;
static unsigned char gCollisionRight;
static unsigned char gJumping; // 0 if not currently in the air from a jump, 1 if yes
static TongueState_t gTongueState;
static unsigned char gTongueCounter;
static unsigned char gBirdMovement;
static unsigned char gBirdSpeedControl; //set to 0-2 and bird doesn't move on 0
static unsigned char gSnakeMovement;
static unsigned char gVelocityDirection;
static unsigned char gVelocityCounter;
// These are probably overkill, but it makes collision detection a lot cleaner
static unsigned char gStage;
static unsigned char gWorld;
static unsigned char gTmpWorld;
static unsigned char gCounter;
static unsigned char gHealth;
static unsigned char gIframes;
static unsigned char gNumSprites;
static GameState_t gGameState;
static unsigned char gFade;
static unsigned char gLives;
static unsigned char gDisplayLives;
static const unsigned char* gScratchPointer;
static unsigned char gPpuCtrlBase;
static unsigned char gTitleScreenColor;
static unsigned char gMusicOn;
static unsigned char gSoundEffectCounter;
static const unsigned char* gSoundEffect0;
static const unsigned char* gSoundEffect1;
static const unsigned char* gSoundEffect2;
static unsigned char gSoundEffectLength;
static unsigned char gCurrentSoundEffect;
static unsigned char gCurrentMusic;
static unsigned char gColorTimer;
static unsigned char gColorTimer2;
static unsigned char gColorTimerLimit;
static unsigned char gTmpDirection;
static unsigned char gContinue;
static unsigned char gFlyCount;
static unsigned char gFlyCountCurrentWorld;
static unsigned char gFlyCollected;
static unsigned char gFlyOnes;
static unsigned char gFlyMax;
static unsigned char gTmpPattern;
static unsigned char gChickenAnimationCounter;
static ChickenAnimationState_t gChickenAnimationState;
static unsigned char gChickenAIState;
static unsigned char gChickenAICounter;
static unsigned char gChickenSpeed;
static unsigned char gChickenVelocity;
static unsigned char gChickenVelocityCounter;
static unsigned char gChickenIframes;
static chicken_move_t* gpChickenMove;
extern unsigned char gVblank;
extern void pMusicInit(unsigned char);
extern void pMusicPlay(void);
void __fastcall__ UnRLE(const unsigned char *data);
void __fastcall__ Title_UnRLE(const unsigned char *data);
#pragma bss-name (pop)
void vblank(void)
{
if( gMusicOn == 1 )
{
pMusicPlay();
PlaySoundEffects();
}
gVblankPrevious = gVblank;
while(gVblank == gVblankPrevious);
}
void vblank_counter(void)
{
for( i = 0; i < gCounter; i++ )
{
vblank();
}
}
void PlaySoundEffects(void)
{
if( gSoundEffectCounter != 0xFF )
{
switch( gCurrentSoundEffect )
{
case TONGUE_SOUND_ID:
gSoundEffect0 = tongueSound400C;
gSoundEffect1 = tongueSound400E;
gSoundEffectLength = TONGUE_SOUND_LENGTH;
break;
case DAMAGE_SOUND_ID:
default:
gSoundEffect0 = damageSound400C;
gSoundEffect1 = damageSound400E;
gSoundEffectLength = DAMAGE_SOUND_LENGTH;
break;
case HOP_SOUND_ID:
gSoundEffect0 = hopSound4008;
gSoundEffect1 = hopSound400A;
gSoundEffect2 = hopSound400B;
gSoundEffectLength = HOP_SOUND_LENGTH;
break;
case JUMP_SOUND_ID:
gSoundEffect0 = jumpSound4008;
gSoundEffect1 = jumpSound400A;
gSoundEffect2 = jumpSound400B;
gSoundEffectLength = JUMP_SOUND_LENGTH;
break;
case PAUSE_SOUND_ID:
gSoundEffect0 = pauseSound4008;
gSoundEffect1 = pauseSound400A;
gSoundEffect2 = pauseSound400B;
gSoundEffectLength = PAUSE_SOUND_LENGTH;
break;
case ITEM_SOUND_ID:
*((unsigned char*)0x4000) = itemSound4000[gSoundEffectCounter];
*((unsigned char*)0x4001) = itemSound4001[gSoundEffectCounter];
*((unsigned char*)0x4002) = itemSound4002[gSoundEffectCounter];
*((unsigned char*)0x4003) = 0;
gSoundEffectLength = ITEM_SOUND_LENGTH;
break;
case PORTAL_SOUND_ID:
*((unsigned char*)0x4008) = portalSound4008[gSoundEffectCounter];
*((unsigned char*)0x400A) = portalSound400A[gSoundEffectCounter];
*((unsigned char*)0x400B) = portalSound400B[gSoundEffectCounter];
if( gSoundEffectCounter >= PORTAL_NOISE_DELAY )
{
*((unsigned char*)0x400C) = portalSound400C[gSoundEffectCounter - PORTAL_NOISE_DELAY];
*((unsigned char*)0x400E) = portalSound400E[gSoundEffectCounter - PORTAL_NOISE_DELAY];
*((unsigned char*)0x400F) = 0;
}
gSoundEffectLength = ITEM_SOUND_LENGTH + PORTAL_NOISE_DELAY;
break;
}
if( gCurrentSoundEffect < 2 )
{
*((unsigned char*)0x400C) = gSoundEffect0[gSoundEffectCounter];
*((unsigned char*)0x400E) = gSoundEffect1[gSoundEffectCounter];
*((unsigned char*)0x400F) = 0;
}
else if( gCurrentSoundEffect < 5 )
{
*((unsigned char*)0x4008) = gSoundEffect0[gSoundEffectCounter];
*((unsigned char*)0x400A) = gSoundEffect1[gSoundEffectCounter];
*((unsigned char*)0x400B) = gSoundEffect2[gSoundEffectCounter];
}
gSoundEffectCounter++;
if(gSoundEffectCounter == gSoundEffectLength)
{
gSoundEffectCounter = 0xFF;
}
}
}
// from NESDev:
// +---------+----------------------------------------------------------+
// | $2000 | PPU Control Register #1 (W) |
// | | |
// | | D7: Execute NMI on VBlank |
// | | 0 = Disabled |
// | | 1 = Enabled |
// | | D6: PPU Master/Slave Selection --+ |
// | | 0 = Master +-- UNUSED |
// | | 1 = Slave --+ |
// | | D5: Sprite Size |
// | | 0 = 8x8 |
// | | 1 = 8x16 |
// | | D4: Background Pattern Table Address |
// | | 0 = $0000 (VRAM) |
// | | 1 = $1000 (VRAM) |
// | | D3: Sprite Pattern Table Address |
// | | 0 = $0000 (VRAM) |
// | | 1 = $1000 (VRAM) |
// | | D2: PPU Address Increment |
// | | 0 = Increment by 1 |
// | | 1 = Increment by 32 |
// | | D1-D0: Name Table Address |
// | | 00 = $2000 (VRAM) |
// | | 01 = $2400 (VRAM) |
// | | 10 = $2800 (VRAM) |
// | | 11 = $2C00 (VRAM) |
// +---------+----------------------------------------------------------+
// +---------+----------------------------------------------------------+
// | $2001 | PPU Control Register #2 (W) |
// | | |
// | | D7-D5: Full Background Colour (when D0 == 1) |
// | | 000 = None +------------+ |
// | | 001 = Green | NOTE: Do not use more |
// | | 010 = Blue | than one type |
// | | 100 = Red +------------+ |
// | | D7-D5: Colour Intensity (when D0 == 0) |
// | | 000 = None +--+ |
// | | 001 = Intensify green | NOTE: Do not use more |
// | | 010 = Intensify blue | than one type |
// | | 100 = Intensify red +--+ |
// | | D4: Sprite Visibility |
// | | 0 = sprites not displayed |
// | | 1 = sprites visible |
// | | D3: Background Visibility |
// | | 0 = Background not displayed |
// | | 1 = Background visible |
// | | D2: Sprite Clipping |
// | | 0 = sprites invisible in left 8-pixel column |
// | | 1 = No clipping |
// | | D1: Background Clipping |
// | | 0 = BG invisible in left 8-pixel column |
// | | 1 = No clipping |
// | | D0: Display Type |
// | | 0 = Colour display |
// | | 1 = Monochrome display |
// +---------+----------------------------------------------------------+
void apuinit(void)
{
// Initialize the APU to a known basic state
*((unsigned char*)0x4015) = 0x0F;
*((unsigned char*)0x4000) = 0x30;
*((unsigned char*)0x4001) = 0x08;
*((unsigned char*)0x4002) = 0x00;
*((unsigned char*)0x4003) = 0x00;
*((unsigned char*)0x4004) = 0x30;
*((unsigned char*)0x4005) = 0x08;
*((unsigned char*)0x4006) = 0x00;
*((unsigned char*)0x4007) = 0x00;
*((unsigned char*)0x4008) = 0x80;
*((unsigned char*)0x4009) = 0x00;
*((unsigned char*)0x400A) = 0x00;
*((unsigned char*)0x400B) = 0x00;
*((unsigned char*)0x400C) = 0x30;
*((unsigned char*)0x400D) = 0x00;
*((unsigned char*)0x400E) = 0x00;
*((unsigned char*)0x400F) = 0x00;
*((unsigned char*)0x4010) = 0x00;
*((unsigned char*)0x4011) = 0x00;
*((unsigned char*)0x4012) = 0x00;
*((unsigned char*)0x4013) = 0x00;
*((unsigned char*)0x4014) = 0x00;
*((unsigned char*)0x4015) = 0x0F;
*((unsigned char*)0x4016) = 0x00;
*((unsigned char*)0x4017) = 0x40;
}
void set_scroll(void)
{
// read 0x2002 to reset the address toggle
devnull = *((unsigned char*)0x2002);
// scroll a number of pixels in the X direction
*((unsigned char*)0x2005) = 0;
// scroll a number of pixels in the Y direction
*((unsigned char*)0x2005) = gYScroll;
//Could put this only when its set, but this is the safest place
}
// TODO not the most efficient but pulling directly from nametables
// This method goes through the top nametable then the bottom. It only needs the top left corner of
// every 2x2 block so grab every other on a row, then skip the next row entirely. Repeat.
void loadCollisionHelper(void)
{
for(i = 0 ; i < 240 ;) {
gTmp4 = PPU_DATA;
collision[i + gTmp6] = (gTmp4 != PATTERN_BLANK_0) ? 0x01 : 0x00;
j = PPU_DATA;
i++;
if((i & 0xF) == 0) {
for(j = 0; j < 32 ; j++) {
gTmp4 = PPU_DATA;
}
}
}
}
void loadCollisionFromNametables(void)
{
PPU_ADDRESS = 0x20; // address of nametable #0
PPU_ADDRESS = 0x00;
//First read is always invalid
i = PPU_DATA;
gTmp6 = 0;
loadCollisionHelper();
PPU_ADDRESS = 0x28; // address of nametable #2
PPU_ADDRESS = 0x00;
//First read is always invalid
i = PPU_DATA;
gTmp6 = 240;
loadCollisionHelper();
for(i = 0; i < 16; i++)
{
collision[480 + i] = 0;
}
}
void ClearSprites(void)
{
// clear sprite ram
for( i = 0; i < 255; i++ )
{
sprites[i] = 0x00;
}
sprites[255] = 0x00;
}
//Copies from sprites array in ram to PPU
void dma_sprites(void)
{
*((unsigned char*)0x4013) = 0x00;
*((unsigned char*)0x4014) = 0x02;
}
void LoadSprites(void)
{
numKeys = 0;
for(gTmp2 = 0; gTmp2 < gNumSprites; gTmp2++)
{
gTmp = *gScratchPointer2++;
gSpriteTable.id[gTmp2] = gTmp;
if(gTmp == KEY_ID) {
numKeys++;
}
gTmp = *gScratchPointer2++;
gSpriteTable.startX[gTmp2] = gTmp;
gTmp = *gScratchPointer2++;
gSpriteTable.startY[gTmp2] = gTmp;
gTmp = *gScratchPointer2++;
gSpriteTable.startNametable[gTmp2] = gTmp;
gTmp = *gScratchPointer2++;
gSpriteTable.direction[gTmp2] = gTmp;
if( gSpriteTable.id[gTmp2] == SNAKE_ID && ((gSpriteTable.direction[gTmp2] & 0x10) == 0x10))
{
gSpriteTable.direction[gTmp2] = gSpriteTable.direction[gTmp2] | 0x20;
}
gSpriteState[gTmp2] = 0;
if( gTmp2 == 0 )
{
gSpriteOffset[gTmp2] = 0;
}
else
{
gSpriteOffset[gTmp2] = gSpriteOffset[gTmp2-1] + (SpriteSize[gSpriteTable.id[gTmp2-1]] << 2);
}
}
}
void palettes(void)
{
// Sprite 0
SET_COLOR(SPRITE0_1, DARK_GREEN);
PPU_DATA = GREEN;
PPU_DATA = WHITE;
// Sprite 1
//SET_COLOR(SPRITE1_1, GRAY_BLUE);
PPU_DATA = BLACK;
PPU_DATA = GRAY_BLUE;
PPU_DATA = DARK_BLUE;
PPU_DATA = LIGHT_ORANGE;
// Sprite 2
//SET_COLOR(SPRITE2_1, DARK_RED);
PPU_DATA = BLACK;
PPU_DATA = DARK_RED;
PPU_DATA = RED;
PPU_DATA = WHITE;
// Sprite 3
//SET_COLOR(SPRITE3_1, RED);
PPU_DATA = BLACK;
PPU_DATA = RED;
PPU_DATA = WHITE;
PPU_DATA = WHITE;
}
void load_palette(void)
{
SET_COLOR(BACKGROUND0_0, BLACK);//gScratchPointer[0]);
if( gDisplayLives == 1 || gContinue == 1 || LevelProperties[gStage].world != 4 )
{
if( gFade == 3 )
{
for( i = 0; i < 16; i++ )
{
PPU_DATA = BLACK;
}
}
if( gFade == 2 )
{
PPU_DATA = BLACK;
PPU_DATA = BLACK;
PPU_DATA = gScratchPointer[1];
PPU_DATA = BLACK;
PPU_DATA = BLACK;
PPU_DATA = BLACK;
PPU_DATA = gScratchPointer[4];
PPU_DATA = BLACK;
PPU_DATA = BLACK;
PPU_DATA = BLACK;
PPU_DATA = gScratchPointer[7];
PPU_DATA = BLACK;
PPU_DATA = BLACK;
PPU_DATA = BLACK;
PPU_DATA = gScratchPointer[10];
}
if( gFade == 1 )
{
PPU_DATA = BLACK;
PPU_DATA = gScratchPointer[1];
PPU_DATA = gScratchPointer[2];
PPU_DATA = BLACK;
PPU_DATA = BLACK;
PPU_DATA = gScratchPointer[4];
PPU_DATA = gScratchPointer[5];
PPU_DATA = BLACK;
PPU_DATA = BLACK;
PPU_DATA = gScratchPointer[7];
PPU_DATA = gScratchPointer[8];
PPU_DATA = BLACK;
PPU_DATA = BLACK;
PPU_DATA = gScratchPointer[10];
PPU_DATA = gScratchPointer[11];
}
if( gFade == 0 )
{
PPU_DATA = gScratchPointer[1];
if( gGameState == ENDING_STATE && gFlyMax == 1 )
{
PPU_DATA = DARK_ORANGE;
PPU_DATA = LIGHT_ORANGE;
}
else
{
PPU_DATA = gScratchPointer[2];
PPU_DATA = gScratchPointer[3];
}
PPU_DATA = BLACK;
PPU_DATA = gScratchPointer[4];
PPU_DATA = gScratchPointer[5];
PPU_DATA = gScratchPointer[6];
PPU_DATA = BLACK;
PPU_DATA = gScratchPointer[7];
PPU_DATA = gScratchPointer[8];
PPU_DATA = gScratchPointer[9];
PPU_DATA = BLACK;
PPU_DATA = gScratchPointer[10];
PPU_DATA = gScratchPointer[11];
PPU_DATA = gScratchPointer[12];
}
SET_COLOR(BACKGROUND0_0, BLACK);//gScratchPointer[0]);
}
else
{
for( gTmp5 = 0; gTmp5 < 15; gTmp5++ )
{
PPU_DATA = BLACK;
}
SET_COLOR(BACKGROUND0_1, DARK_GRAY_BLUE);
}
PPU_CTRL = gPpuCtrlBase + gYNametable;
set_scroll();
}
void fade_out(void)
{
PPU_MASK = 0x0E;
set_scroll();
gCounter = 20;
vblank_counter();
gFade = 1;
load_palette();
gCounter = 20;
vblank_counter();
gFade = 2;
load_palette();
gCounter = 20;
vblank_counter();
gFade = 3;
load_palette();
gCounter = 20;
vblank_counter();
PPU_CTRL = 0x80;
PPU_MASK = 0x00;
set_scroll();
}
void fade_in(void)
{
gFade = 2;
load_palette();
PPU_MASK = 0x0E;
set_scroll();
gCounter = 20;
vblank_counter();
gFade = 1;
load_palette();
gCounter = 20;
vblank_counter();
gFade = 0;
load_palette();
dma_sprites();
PPU_CTRL = gPpuCtrlBase + gYNametable;
if( gStage == 0 || gStage > (NUM_LEVELS-2) || gDisplayLives == 1 || gContinue == 1 )
{
PPU_MASK = 0x0E;
}
else
{
PPU_MASK = 0x1E;
}
set_scroll();