-
Notifications
You must be signed in to change notification settings - Fork 0
/
scene_loader.c~
1215 lines (987 loc) · 43.6 KB
/
scene_loader.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 <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include<time.h>
#include "scene_loader.h" // Load images to scenes and other data
// x coordinates of player 1 and 2 spawn positions
int PLAYER_1_SPAWN_POSITIONS[4] = {30,60,90,120};
int PLAYER_2_SPAWN_POSITIONS[4] = {440,400,360,320};
/* Define attributes of our projectiles
Each projectile has its own row and attributes (gravity,wind,velocity multiplier)
0- cannon {gravity multiplier, wind multiplier, velocity multiplier, damage, projectile radius, explosion radius}
1- sniper {gravity multiplier, wind multiplier, velocity multiplier, damage, projectile radius, explosion radius}
*/
double PROJECTILE_ATTRIBUTES[PROJECTILE_TYPE_TYPE_TOTAL][PROJECTILE_ATTRIBUTES_TOTAL] = {
{1, 4, 1, 20, 4, 70},
{2, 4, 3, 40, 2, 35}
};
/* This array encodes wind level speeds -> check WIND_LEVEL enum structure in 'scene_loader.h'
If you want to change wind effect change attributes of weapon instead, dont change this array */
double WIND_SPEEDS[WIND_LEVEL_TOTAL_LEVELS] = {-3, -2, -1, 0, 1, 2, 3};
// Color of our array map
#define SAND_COLOR_R 120
#define SAND_COLOR_G 255
#define SAND_COLOR_B 50
// Load all images to SceneData struct and return
SceneData* GetInnitialScenesData(){
// Mallocate array. Data space for every scene
SceneData* InnitScenesData = malloc(SCENE_TOTAL_COUNT * sizeof(SceneData)); // WILL HAVE TO BE DESTOYED ON QUIT
if(InnitScenesData == NULL){ // Check malloc
printf("ERROR: Could not mallocate 'InnitScenesData'\n");
exit(EXIT_FAILURE);
}
// Load background images and others for every scene. Look 'scene_loader.h' for more info
// MAIN MENU
LoadPNG2Canvas(PATH_TO_IMG_SCENE_MENU, &(InnitScenesData[SCENE_MENU].scene_canvas));
LoadPNG2Canvas(PATH_TO_IMG_SCOPE, &(InnitScenesData[SCENE_MENU].scope_canvas));
// GAME MAP
LoadPNG2Canvas(PATH_TO_GAME_MAP, &(InnitScenesData[SCENE_GAME_MAP].scene_canvas));
LoadPNG2Canvas(PATH_TO_PLAYER_1_TANK, &(InnitScenesData[SCENE_GAME_MAP].player_1_tank));
LoadPNG2Canvas(PATH_TO_PLAYER_2_TANK, &(InnitScenesData[SCENE_GAME_MAP].player_2_tank));
// GAME MAP -> explosion
//LoadPNG2Canvas(PATH_TO_EXPLOSION_IMG, &(InnitScenesData[SCENE_GAME_MAP].explosion));
InnitScenesData[1].scene_id = SCENE_MENU;
// Malloc map array
InnitScenesData[SCENE_GAME_MAP].game_map_array = malloc(DISPLAY_WIDTH*sizeof(unsigned int));
// Set initial scene to unknown
CURRENT_SCENE = -1;
return InnitScenesData;
}
/* This function will refresh entire scene, map, players, shot... */
void RefreshScene(SceneData* scene_data, Canvas* game_canvas){
// Load background
RGB transparent = {.R = 0, .G = 0, .B = 0}; // Wont have effect on scene
Canvas2CanvasFull(&(scene_data[CURRENT_SCENE].scene_canvas), game_canvas, 0, 0, false, &transparent);
///////////////////
if(CURRENT_SCENE == SCENE_MENU){ // We are currenty in main menu
//////////// Draw text
RGB color_black = {.R = 0, .G = 0, .B = 0};
DrawText(game_canvas, "PLAY", 350, 70, 5, &color_black);
DrawText(game_canvas, "WIND", 320, 110, 5, &color_black);
if(scene_data[SCENE_MENU].wind_on == true){
DrawText(game_canvas, "ON", 403, 110, 5, &color_black);
}else{
DrawText(game_canvas, "OFF", 390, 110, 5, &color_black);
}
DrawText(game_canvas, "TANKS", 340, 220, 5, &color_black);
DrawText(game_canvas, "PLAYER 1:", 305, 250, 5, &color_black);
DrawText(game_canvas, "PLAYER 2:", 305, 280, 5, &color_black);
// Convert tanks count to string and draw text
char p_1_tanks[4], p_2_tanks[4];
sprintf(p_1_tanks, "%d\n", scene_data[SCENE_MENU].player_1_tanks);
sprintf(p_2_tanks, "%d\n", scene_data[SCENE_MENU].player_2_tanks);
DrawText(game_canvas, p_1_tanks, 440, 250, 5, &color_black);
DrawText(game_canvas, p_2_tanks, 440, 280, 5, &color_black);
/////////////////////////////////////////////////
// Draw scope
// Scope transparent background
transparent.R = 254; transparent.G = 254; transparent.B = 254;
switch(scene_data[CURRENT_SCENE].knob_menu_position){
case (KNOB_MENU_PLAY):
Canvas2CanvasFull(&(scene_data[SCENE_MENU].scope_canvas), game_canvas, 300, 58, true, &transparent);
break;
case (KNOB_MENU_WIND):
Canvas2CanvasFull(&(scene_data[SCENE_MENU].scope_canvas), game_canvas, 270, 98, true, &transparent);
break;
case (KNOB_MENU_PLAYER_1):
Canvas2CanvasFull(&(scene_data[SCENE_MENU].scope_canvas), game_canvas, 250, 238, true, &transparent);
break;
case (KNOB_MENU_PLAYER_2):
Canvas2CanvasFull(&(scene_data[SCENE_MENU].scope_canvas), game_canvas, 250, 268, true, &transparent);
break;
}
}else if(CURRENT_SCENE == SCENE_GAME_MAP){
// Fill map with sand
FillCavnasWithMapArray(scene_data, game_canvas);
// Set position for tanks
SetAllTanksPosition(scene_data);
// Add tank images to canvas 'game_canvas'
FillCanvasWithTanks(scene_data, game_canvas);
// Draw the text with current chosen projectile of active tank
RGB color_black = {.R = 0, .G = 0, .B = 0};
if(scene_data[SCENE_GAME_MAP].active_tank->chosen_projectile == PROJECTILE_TYPE_TYPE_CANNON){
DrawText(game_canvas, "Weapon: Cannon", 160, 20, 3, &color_black);
}else if(scene_data[SCENE_GAME_MAP].active_tank->chosen_projectile == PROJECTILE_TYPE_TYPE_SNIPER){
DrawText(game_canvas, "Weapon: Sniper", 160, 20, 3, &color_black);
}
// Draw parabolic aiming path
DrawAimPath(scene_data, game_canvas, &color_black);
// Draw health bars
DrawAllHealthBars(scene_data, game_canvas);
// If we are animating, display the flying projectile
if(scene_data[SCENE_GAME_MAP].animating){
DrawProjectile(scene_data, game_canvas, &color_black);
}
// If wind is on, draw wind speed information
if(scene_data[SCENE_GAME_MAP].wind_on){
DrawWindSpeeds(scene_data, game_canvas, &color_black);
}
}
return;
}
/* Load scene settings */
void LoadScene(SceneData* scene_data, Canvas* game_canvas, int scene_id){
// Was previous scene 'SCEME_GAME_MAP' ? Free every mallocked memory from that scene
if(CURRENT_SCENE == SCENE_GAME_MAP){
ExitGameMap(scene_data);
}
CURRENT_SCENE = scene_id;
if(scene_id == SCENE_MENU){
scene_data[SCENE_MENU].last_action = ACTION_NO_ACTION; // Refresh actions -> no action
scene_data[SCENE_MENU].knob_menu_position = KNOB_MENU_PLAY; // Set knob pos to default
scene_data[SCENE_MENU].wind_on = false; // Wind is off
scene_data[SCENE_MENU].player_1_tanks = 1;
scene_data[SCENE_MENU].player_2_tanks = 1;
scene_data[SCENE_MENU].player_max_tanks = PLAYER_MAX_TANKS;
scene_data[SCENE_MENU].GameOver = false;
scene_data[SCENE_GAME_MAP].animating = false;
RefreshScene(scene_data, game_canvas);
}else if(scene_id == SCENE_GAME_MAP){
// Grab setting from menu
scene_data[SCENE_GAME_MAP].last_action = scene_data[SCENE_MENU].last_action;
scene_data[SCENE_GAME_MAP].knob_menu_position = KNOB_MENU_PLAY;
scene_data[SCENE_GAME_MAP].wind_on = scene_data[SCENE_MENU].wind_on;
scene_data[SCENE_GAME_MAP].player_1_tanks = scene_data[SCENE_MENU].player_1_tanks;
scene_data[SCENE_GAME_MAP].player_2_tanks = scene_data[SCENE_MENU].player_2_tanks;
scene_data[SCENE_GAME_MAP].player_max_tanks = PLAYER_MAX_TANKS;
scene_data[SCENE_GAME_MAP].player_turn = PLAYER_1_TURN;
scene_data[SCENE_GAME_MAP].Player_1_tank_ptr = 0;
scene_data[SCENE_GAME_MAP].Player_2_tank_ptr = 0;
scene_data[SCENE_GAME_MAP].turn_count = 0;
scene_data[SCENE_GAME_MAP].GameOver = false;
scene_data[SCENE_GAME_MAP].gravity = SCENE_GRAVITY;
scene_data[SCENE_GAME_MAP].animating = false;
scene_data[SCENE_GAME_MAP].player_1_tanks_obj = malloc(scene_data[SCENE_GAME_MAP].player_1_tanks*sizeof(Tank));
scene_data[SCENE_GAME_MAP].player_2_tanks_obj = malloc(scene_data[SCENE_GAME_MAP].player_2_tanks*sizeof(Tank));
// Reset 'confirmed movement' boolean
scene_data[SCENE_GAME_MAP].movement_confirmed = false;
if(scene_data[SCENE_GAME_MAP].player_1_tanks_obj == NULL ||
scene_data[SCENE_GAME_MAP].player_2_tanks_obj == NULL){
printf("ERROR: Could not mallocate 'player_1_tanks_obj'/'player_2_tanks_obj' returning to main menu");
LoadScene(scene_data, game_canvas, SCENE_MENU);
}
// Fill current game map array, with mode 0
FillGameMapArray(scene_data, 0);
// For each tank, reset 'health', 'angle of fire', 'moves', 'projectile attributes' basciall spawn tanks
for(int t = 0; t < scene_data[SCENE_GAME_MAP].player_1_tanks; t++){
scene_data[SCENE_GAME_MAP].player_1_tanks_obj[t].x = PLAYER_1_SPAWN_POSITIONS[t];
scene_data[SCENE_GAME_MAP].player_1_tanks_obj[t].health = TANK_HEALTH;
scene_data[SCENE_GAME_MAP].player_1_tanks_obj[t].angle_of_fire = ANGLE_OF_FIRE_MIN+40; // 40 degrees at the start
scene_data[SCENE_GAME_MAP].player_1_tanks_obj[t].move_limit = TANK_MOVE_LIMIT;
scene_data[SCENE_GAME_MAP].player_1_tanks_obj[t].velocity_of_projectile = PROJECTILE_VELOCITY_MAX/2;
scene_data[SCENE_GAME_MAP].player_1_tanks_obj[t].chosen_projectile = PROJECTILE_TYPE_TYPE_CANNON;
scene_data[SCENE_GAME_MAP].player_1_tanks_obj[t].is_alive = true;
}
for(int t = 0; t < scene_data[SCENE_GAME_MAP].player_2_tanks; t++){
scene_data[SCENE_GAME_MAP].player_2_tanks_obj[t].x = PLAYER_2_SPAWN_POSITIONS[t];
scene_data[SCENE_GAME_MAP].player_2_tanks_obj[t].health = TANK_HEALTH;
scene_data[SCENE_GAME_MAP].player_2_tanks_obj[t].angle_of_fire = ANGLE_OF_FIRE_MAX-40; // 140 degrees at the start
scene_data[SCENE_GAME_MAP].player_2_tanks_obj[t].move_limit = TANK_MOVE_LIMIT;
scene_data[SCENE_GAME_MAP].player_2_tanks_obj[t].velocity_of_projectile = PROJECTILE_VELOCITY_MAX/2;
scene_data[SCENE_GAME_MAP].player_2_tanks_obj[t].chosen_projectile = PROJECTILE_TYPE_TYPE_CANNON;
scene_data[SCENE_GAME_MAP].player_2_tanks_obj[t].is_alive = true;
}
// Set active tank this depends on which turn it is 'player_turn' above in this function
int tank_ptr;
Tank* temp_tank_ptr;
if(scene_data[SCENE_GAME_MAP].player_turn == PLAYER_1_TURN){
tank_ptr = scene_data[SCENE_GAME_MAP].Player_1_tank_ptr;
temp_tank_ptr = &(scene_data[SCENE_GAME_MAP].player_1_tanks_obj[tank_ptr]);
}else if(scene_data[SCENE_GAME_MAP].player_turn == PLAYER_2_TURN){
tank_ptr = scene_data[SCENE_GAME_MAP].Player_2_tank_ptr;
temp_tank_ptr = &(scene_data[SCENE_GAME_MAP].player_2_tanks_obj[tank_ptr]);
}
scene_data[SCENE_GAME_MAP].active_tank = temp_tank_ptr;
// Malloc parabolic aim path_public array as well as private aim path
RefreshAimPath(scene_data, true, true);
RefreshAimPath(scene_data, true, false);
// Refresh aim path array which is already allocated (shown to player)
// Draw parabolic aiming path
RGB color_black = {.R = 0, .G = 0, .B = 0};
DrawAimPath(scene_data, game_canvas, &color_black);
////////////////////////////////////////////////////////////////////////////////////////
RefreshScene(scene_data, game_canvas);
}
return;
}
/* Fill game map array into current scene with different modes
modes - 0 line
- 1 predefined array n.1
*/
void FillGameMapArray(SceneData* scene_data, int mode){
if(mode == 0){
int height = 55;
for(int i = 0; i < DISPLAY_WIDTH; i++){
scene_data[CURRENT_SCENE].game_map_array[i] = (DISPLAY_HEIGHT - height);
}
}else if(mode == 1){
// Predefined array
}
return;
}
/* Set tank y position. Tank will always be on top of our sand. */
void SetAllTanksPosition(SceneData* scene_data){
if(scene_data[CURRENT_SCENE].player_1_tanks_obj == NULL ||
scene_data[CURRENT_SCENE].player_2_tanks_obj == NULL){
printf("ERROR: 'player_1_tanks_obj' or 'player_1_tanks_obj' are NULL\n");
return;
}
for(int t = 0; t < scene_data[CURRENT_SCENE].player_1_tanks; t++){
int x = scene_data[CURRENT_SCENE].player_1_tanks_obj[t].x;
scene_data[CURRENT_SCENE].player_1_tanks_obj[t].y = scene_data[CURRENT_SCENE].game_map_array[x];
}
for(int t = 0; t < scene_data[CURRENT_SCENE].player_2_tanks; t++){
int x = scene_data[CURRENT_SCENE].player_2_tanks_obj[t].x;
scene_data[CURRENT_SCENE].player_2_tanks_obj[t].y = scene_data[CURRENT_SCENE].game_map_array[x];
}
}
/* Fill our game canvas with sand */
void FillCavnasWithMapArray(SceneData* scene_data, Canvas* game_canvas){
RGB sand_color = {
.R = SAND_COLOR_R,
.G = SAND_COLOR_G,
.B = SAND_COLOR_B
};
for(int x = 0; x < DISPLAY_WIDTH; x++){
int sand_y = scene_data[CURRENT_SCENE].game_map_array[x];
for(int y = sand_y; y < DISPLAY_HEIGHT; y++){
ColorCanvasPixel(x, y, &sand_color, game_canvas);
}
}
}
// Add tank images to canvas 'game_canvas'
void FillCanvasWithTanks(SceneData* scene_data, Canvas* game_canvas){
// Get tank count
int player_1_tank_count = scene_data[CURRENT_SCENE].player_1_tanks;
int player_2_tank_count = scene_data[CURRENT_SCENE].player_2_tanks;
// Transparent color for tank is
RGB transparent_color = {.R = 254, .G = 254, .B = 254};
// Tank height is n px
int tank_height = 9;
// Display each tank on canvas, if its alive
for(int t = 0; t < player_1_tank_count; t++){
int x = scene_data[CURRENT_SCENE].player_1_tanks_obj[t].x;
int y = scene_data[CURRENT_SCENE].player_1_tanks_obj[t].y;
if(scene_data[CURRENT_SCENE].player_1_tanks_obj[t].is_alive == true){ // Is the tank alive?
Canvas2CanvasFull(&(scene_data[CURRENT_SCENE].player_1_tank), game_canvas, x, y-tank_height, true, &transparent_color);
}
}
// Display each tank on canvas, if its alive
for(int t = 0; t < player_2_tank_count; t++){
int x = scene_data[CURRENT_SCENE].player_2_tanks_obj[t].x;
int y = scene_data[CURRENT_SCENE].player_2_tanks_obj[t].y;
if(scene_data[CURRENT_SCENE].player_2_tanks_obj[t].is_alive == true){ // Is the tank alive?
Canvas2CanvasFull(&(scene_data[CURRENT_SCENE].player_2_tank), game_canvas, x, y-tank_height, true, &transparent_color);
}
}
return;
}
/* Move active tank to the left if 'left'==true or right otherwise. */
void MoveTank(SceneData* scene_data, bool left){
Tank* tank; // Tank ptr
// Choose tank based on turns at the moment
if(scene_data[CURRENT_SCENE].player_turn == PLAYER_1_TURN){
printf("Moving tank PLAYER 1\n");
// Choose active tank from player 1
int tank_ptr = scene_data[CURRENT_SCENE].Player_1_tank_ptr;
tank = &(scene_data[CURRENT_SCENE].player_1_tanks_obj[tank_ptr]);
}else{
printf("Moving tank PLAYER 2\n");
// Choose active tank from player 2
int tank_ptr = scene_data[CURRENT_SCENE].Player_2_tank_ptr;
tank = &(scene_data[CURRENT_SCENE].player_2_tanks_obj[tank_ptr]);
}
if(tank->move_limit > 0){
if(left){
if(tank->x > 0){
tank->x--;
tank->move_limit--;
}
}else{
if(tank->x <= DISPLAY_WIDTH-20){
tank->x++;
tank->move_limit--;
}
}
printf("New tank.move_limit: %d\n", tank->move_limit);
}else{
printf("You dont have enought moves\n");
}
return;
}
/* This function changes the tank and player in control */
void NextTurn(SceneData* scene_data){
// Change turns
if(scene_data[CURRENT_SCENE].player_turn == PLAYER_1_TURN){
int new_tank_idx = ReturnIndexToLiveTank(scene_data, PLAYER_2_TURN);
if(new_tank_idx == -1){
// Player 1 won
scene_data[CURRENT_SCENE].GameOver = true;
printf("!GAME OVER! - player 1 won\n");
return;
}else{
// Check if We didn't destroy ourselves
int my_tanks_idx = ReturnIndexToLiveTank(scene_data, PLAYER_1_TURN);
if(my_tanks_idx == -1){
// We destroy ourselves
scene_data[CURRENT_SCENE].player_turn = PLAYER_2_TURN;
scene_data[CURRENT_SCENE].GameOver = true;
printf("!GAME OVER! - player 2 won\n");
return;
}
scene_data[CURRENT_SCENE].Player_2_tank_ptr = new_tank_idx;
scene_data[CURRENT_SCENE].player_turn = PLAYER_2_TURN;
// Set the active tank
scene_data[CURRENT_SCENE].active_tank = &(scene_data[CURRENT_SCENE].player_2_tanks_obj[new_tank_idx]);
}
}else{
int new_tank_idx = ReturnIndexToLiveTank(scene_data, PLAYER_1_TURN);
if(new_tank_idx == -1){
// Player 2 won
scene_data[CURRENT_SCENE].GameOver = true;
printf("!GAME OVER! - player 2 won\n");
return;
}else{
// Check if We didn't destroy ourselves
int my_tanks_idx = ReturnIndexToLiveTank(scene_data, PLAYER_2_TURN);
if(my_tanks_idx == -1){
// We destroy ourselves
scene_data[CURRENT_SCENE].player_turn = PLAYER_1_TURN;
scene_data[CURRENT_SCENE].GameOver = true;
printf("!GAME OVER! - player 1 won\n");
return;
}
scene_data[CURRENT_SCENE].Player_1_tank_ptr = new_tank_idx;
scene_data[CURRENT_SCENE].player_turn = PLAYER_1_TURN;
// Set the active tank
scene_data[CURRENT_SCENE].active_tank = &(scene_data[CURRENT_SCENE].player_1_tanks_obj[new_tank_idx]);
}
}
// Set new wind speed if the wind is on
if(scene_data[CURRENT_SCENE].wind_on){
SetWindSpeed(scene_data);
}
scene_data[CURRENT_SCENE].turn_count++;
ResetTanksMoveLimit(scene_data);
// Reset our 'confirm movement' bool var
scene_data[CURRENT_SCENE].movement_confirmed = false;
return;
}
/* This functions returns the index of the next live tank in array of tanks
-1 if there is no live tank. */
int ReturnLiveTankIdx(Tank* tanks, int tanks_count, int old_index){
int index = old_index;
// If tanks_count reaches zero, that means we iterated full circle
while(tanks_count > 0){
// Go to next valid index
if((index+1) >= tanks_count){
index = 0;
}else{
index++;
}
// Check if the tank is dead
if(tanks[index].health <= 0){
// It's dead
tanks_count--;
}else{
// It's live, return index
printf("returning tank index: %d\n", index);
return index;
}
}
return -1;
}
/* Reset move limits on all tanks */
void ResetTanksMoveLimit(SceneData* scene_data){
for(int t = 0; t < scene_data[CURRENT_SCENE].player_1_tanks; t++){
scene_data[CURRENT_SCENE].player_1_tanks_obj[t].move_limit = TANK_MOVE_LIMIT;
}
for(int t = 0; t < scene_data[CURRENT_SCENE].player_2_tanks; t++){
scene_data[CURRENT_SCENE].player_2_tanks_obj[t].move_limit = TANK_MOVE_LIMIT;
}
return;
}
/* This function increases current active tank barrel angle
if add_deg == true move barrel from 0 def to 180 deg
else move barrel from 180 deg to 0 deg by ANGLE_OF_FIRE_STEP degree */
void MoveBarrel(SceneData* scene_data, bool add_deg){
// Active tank
Tank* tank = scene_data[SCENE_GAME_MAP].active_tank;
if(add_deg == true){
if(tank->angle_of_fire >= ANGLE_OF_FIRE_MAX){
printf("Barrel reached its maximum angle!\n");
}else{
// Increase barrel angle by ANGLE_OF_FIRE_STEP deg
tank->angle_of_fire += ANGLE_OF_FIRE_STEP;
}
}else{
if(tank->angle_of_fire <= ANGLE_OF_FIRE_MIN){
printf("Barrel reached its minimum angle!\n");
}else{
// Decrease barrel angle by ANGLE_OF_FIRE_STEP deg
tank->angle_of_fire -= ANGLE_OF_FIRE_STEP;
}
}
printf("Barrel angle: %.2f\n", tank->angle_of_fire);
return;
}
/* This function increases current active tank projectile velocity
if add_velocity == true, increase the velocity
else decreade projectile velocity by PROJECTILE_VELOCITY_STEP degree */
void ChangeProjectileVelocity(SceneData* scene_data, bool add_velocity){
// Active tank
Tank* tank = scene_data[SCENE_GAME_MAP].active_tank;
if(add_velocity == true){
if(tank->velocity_of_projectile >= PROJECTILE_VELOCITY_MAX){
printf("Reached projectile velocity maximum!\n");
}else{
// Increase projectile velocity by PROJECTILE_VELOCITY_STEP
tank->velocity_of_projectile += PROJECTILE_VELOCITY_STEP;
}
}else{
if(tank->velocity_of_projectile <= PROJECTILE_VELOCITY_MIN){
printf("Reached projectile velocity minimum!\n");
}else{
// Decrease projectile velocity by PROJECTILE_VELOCITY_STEP
tank->velocity_of_projectile -= PROJECTILE_VELOCITY_STEP;
}
}
printf("Projectile velocity: %.2f\n", tank->velocity_of_projectile);
return;
}
/* This function changes current weapon for the active tank */
void ChangeWapon(SceneData* scene_data){
if(scene_data[CURRENT_SCENE].active_tank->chosen_projectile < (PROJECTILE_TYPE_TYPE_TOTAL - 1)){
scene_data[CURRENT_SCENE].active_tank->chosen_projectile++;
}else{
scene_data[CURRENT_SCENE].active_tank->chosen_projectile = 0;
}
return;
}
/* This function calculates parabola.
For each time delta of size 'ARRAY_PATH_TIME_CHANGE' seconds, it will calculate positions x,y.
We will specify the number of positions (time stamps) we want, which then also corresponds to the flight
time. If the path gets ouf of left,right, bottom bounds it will end. -> upper boundrary not included
I wanted to make this function more generic so it could be possibly used for other things.
-Option 'should_malloc' will allocate our new path to array public or private according to arguments.
-Option 'private_path' decides which path is calculated public or private one
*/
void CalculateParabolicPath(SceneData* scene_data, int max_positions,
int start_x, int start_y,
double angle, double gravity, double wind_speed, double velocity,
bool wind,
bool should_malloc,
bool private_path
){
/* We will calculate this parabola in conventional cartesian coordinate system
we will have to map those coordinates to our display grid with different values. */
int valid_size = max_positions; // <= max_positions. Size depends on duration of flight
Parabola* parabola;
if(should_malloc){
// Malloc our Parabola datatype
parabola = malloc(sizeof(Parabola));
if(parabola == NULL){
printf("ERROR: Could not malloc 'Parabola' in CalculateParabolicPath!\n");
exit(EXIT_FAILURE);
}
// Malloc our array in which we will store our pixels
parabola->parabolic_path = malloc(max_positions*2*sizeof(short int)); // Each pixel has 2 coordinates
if(parabola->parabolic_path == NULL){
printf("ERROR: Could not malloc 'parabolic_path' in CalculateParabolicPath!\n");
exit(EXIT_FAILURE);
}
}else{
if(private_path){
parabola = scene_data[SCENE_GAME_MAP].aim_path_private;
}else{
parabola = scene_data[SCENE_GAME_MAP].aim_path_public;
// If we have public parabola, player doesnt see effect of the wind
wind = false;
}
}
////////// START CALCULATION ///////////
double converter = PI / 180; // Convert degrees to radians
double v_x = velocity*cos(angle*converter); // horizontal velocity, along x-axis
double v_y = velocity*sin(angle*converter); // vertical velocity, along y-axis
for(int i = 0; i < max_positions; i++){
short int new_x, new_y; // Variables for new positions
double time_delta = (double)i*ARRAY_PATH_TIME_CHANGE;
// Calculate new x coordinate // s = start_x + v*t + wind_v*t // wind has negative speed when it moves to the left
if(wind){ // WIND == ON
new_x = (short int)(start_x + (v_x*time_delta) + (wind_speed*time_delta));
}else{ // WIND == OFF
new_x = (short int)(start_x + (v_x*time_delta));
printf("new_x: %d\n", new_x);
printf("v_x*time_delta: %f\n", v_x*time_delta);
printf("v_x: %f\n", v_x);
printf("time_delta: %f\n", time_delta);
}
// Calculate new y coordinate // s = start_y + v0*t + (1/2)*g*t^2
new_y = (short int)(start_y + (v_y*time_delta) + (0.5)*gravity*time_delta*time_delta);
// Check bounds, break if needed
if((new_y <= 0) || (new_x <= 0) || (new_x >= DISPLAY_WIDTH)){
valid_size = i-1; // Our size of valid pixels will be smaller,
break;
}else{
parabola->parabolic_path[(i*2)] = new_x;
parabola->parabolic_path[(i*2)+1] = new_y;
}
}
////////////////////////////////////////
// Write final valid size to parabola
parabola->number_of_pairs = valid_size;
if(should_malloc){
if(private_path){
scene_data[SCENE_GAME_MAP].aim_path_private = parabola;
}else{
scene_data[SCENE_GAME_MAP].aim_path_public = parabola;
}
}
return;
}
/* This function calls function 'CalculateParabolicPath'
If 'should_malloc' is true the new path will be mallock'd to new array and saved
accordingly to 'aim_path_private' if 'private_path' == true, or save it to
'aim_path_public' if 'private_path' == false */
void RefreshAimPath(SceneData* scene_data, bool should_malloc, bool private_path){
// Calculate aim path for active tank
Tank* tank = scene_data[SCENE_GAME_MAP].active_tank;
// How many time steps to calculate
int max_positions;
if(private_path){
max_positions = AIM_PATH_PRIVATE_ARRAY_MAX_SIZE;
}else{
max_positions = AIM_PATH_PUBLIC_PIXELS_SHOW;
}
// Load physics multipliers based on current chose weapon
int gravity_m = PROJECTILE_ATTRIBUTES[tank->chosen_projectile][PROJECTILE_G_MULTIPLIER];
int wind_m = PROJECTILE_ATTRIBUTES[tank->chosen_projectile][PROJECTILE_W_MULTIPLIER];
int velocity_m = PROJECTILE_ATTRIBUTES[tank->chosen_projectile][PROJECTILE_V_MULTIPLIER];
CalculateParabolicPath(scene_data,
max_positions,
(tank->x) + TANK_BARREL_X_OFFSET,
(DISPLAY_HEIGHT-(tank->y)) + TANK_BARREL_Y_OFFSET,
tank->angle_of_fire,
scene_data[SCENE_GAME_MAP].gravity*gravity_m,
WIND_SPEEDS[scene_data[SCENE_GAME_MAP].wind_speed_index]*wind_m,
(tank->velocity_of_projectile)*velocity_m,
scene_data[SCENE_GAME_MAP].wind_on,
should_malloc, // if should_malloc == false -> don't malloc, save it to scene_data aim_path_public/private
private_path // if true, save the data to private path
);
return;
}
/* This function draws 'aim_path_public' from scene_data onto canvas from the
onto active tank */
void DrawAimPath(SceneData* scene_data, Canvas* game_canvas, RGB* color){
Parabola* parabola = scene_data[SCENE_GAME_MAP].aim_path_public;
for(int pair = 0; pair < parabola->number_of_pairs; pair++){
if(pair >= AIM_PATH_PUBLIC_PIXELS_SHOW){
break; // Show only few pixels
}
short int xx = (short int)parabola->parabolic_path[(pair*2)];
short int yy = DISPLAY_HEIGHT-(short int)parabola->parabolic_path[(pair*2)+1];
CanvasCircle(xx, yy, 2, color, game_canvas);
}
}
/* This function randomly sets wind speed. This game will have 'WIND_LEVEL_TOTAL_LEVELS'
levels of wind speeds. For now: <<<, <<, <, (.) , >, >>, >>> */
void SetWindSpeed(SceneData* scene_data){
// Pick random number bewteen 0 and WIND_LEVEL_TOTAL_LEVELS
srand(time(0));
int random_index = rand()%WIND_LEVEL_TOTAL_LEVELS;
/* Then set wind speed index 'scene_data' to chosen index
of the 'WIND_SPEEDS' array element */
scene_data[CURRENT_SCENE].wind_speed_index = random_index;
return;
}
/* This function encodes wind speed information to display and informs user of
the wind situation */
void DrawWindSpeeds(SceneData* scene_data, Canvas* game_canvas, RGB* color){
// Draw wind speed to display
switch(scene_data[CURRENT_SCENE].wind_speed_index){
case WIND_LEVEL_STRONGEST_LEFT:
DrawText(game_canvas, "Wind: <<<", 30, 20, 5, color);
break;
case WIND_LEVEL_STRONG_LEFT:
DrawText(game_canvas, "Wind: <<", 30, 20, 5, color);
break;
case WIND_LEVEL_WEAKER_LEFT:
DrawText(game_canvas, "Wind: <", 30, 20, 5, color);
break;
case WIND_LEVEL_NONE:
DrawText(game_canvas, "Wind: | ", 30, 20, 5, color);
break;
case WIND_LEVEL_WEAKER_RIGHT:
DrawText(game_canvas, "Wind: >", 30, 20, 5, color);
break;
case WIND_LEVEL_STRONG_RIGHT:
DrawText(game_canvas, "Wind: >>", 30, 20, 5, color);
break;
case WIND_LEVEL_STRONGEST_RIGHT:
DrawText(game_canvas, "Wind: >>>", 30, 20, 5, color);
break;
}
return;
}
/* This function will initialize default state for the projectile and starts animation. */
void StartProjectileAnimation(SceneData* scene_data){
// Load projectile and tank then fill data to projectile in scene_data
Projectile* projectile = &(scene_data[SCENE_GAME_MAP].projectile);
Tank* tank = scene_data[SCENE_GAME_MAP].active_tank;
projectile->type = tank->chosen_projectile; // Copy current weapon data
projectile->to_explode = false; // Dont explode yet
projectile->exploded = false;
projectile->explosion_current_frames = 0; // Animation frame -> 0
// Calculate copy and set animation path data
RefreshAimPath(scene_data, false, true); // Calculate priva path data
projectile->parabola = scene_data[SCENE_GAME_MAP].aim_path_private;
projectile->projectile_path_pairs_index = 0; // Where are we in the animation path
// Start animation
scene_data[SCENE_GAME_MAP].animating = true;
printf("Projectile animation activated!\n");
return;
}
/* This function moves the projectile in animation frame by frame and checks if it
should explode and end animation. */
void ProjectileAnimationTick(SceneData* scene_data){
printf("Projectile animation tick!\n");
// Load projectile
Projectile* projectile = &(scene_data[SCENE_GAME_MAP].projectile);
// Give the projectile new index pair if there is a valid one left
int total_pairs = (projectile->parabola)->number_of_pairs;
int current_index = projectile->projectile_path_pairs_index;
printf("current_index: %d total_pairs: %d\n", current_index, total_pairs);
if(total_pairs > current_index){
projectile->projectile_path_pairs_index++;
}else{
// End of animation
printf("Last animation point reached!\n");
// For now close animation
scene_data[SCENE_GAME_MAP].animating = false;
}
// Refresh the distances between projectile and players
RefreshProjectilePlayersDistance(scene_data);
// Refresh the distance between projectile and the ground
RefreshProjectileGroundDistance(scene_data);
// Check if the projectile should explode
ShouldProjectileExplode(scene_data);
if(projectile->to_explode){
printf("Starting the explosion\n");
}
// Delay the animation a bit
msleep(PROJECTILE_ANIMATION_DELAY);
return;
}
/* This function draws current position of projectile onto canvas.
Draws a circle and arguments are color and radius of the circle */
void DrawProjectile(SceneData* scene_data, Canvas* game_canvas, RGB* color){
// Load projectile
Projectile* projectile = &(scene_data[SCENE_GAME_MAP].projectile);
// Get the data about position and type of projectile
short int x = (projectile->parabola)->parabolic_path[(projectile->projectile_path_pairs_index)*2];
short int y = DISPLAY_HEIGHT-(projectile->parabola)->parabolic_path[(projectile->projectile_path_pairs_index)*2 + 1];
int radius = PROJECTILE_ATTRIBUTES[projectile->type][PROJECTILE_RADIUS];
CanvasCircle(x, y, radius, color, game_canvas);
printf("Drawing projectile!\n");
return;
}
/* This function calculates distance of projectile from players and saves it to
each tank */
void RefreshProjectilePlayersDistance(SceneData* scene_data){
// Load projectile and get the x and y coordinates
Projectile* projectile = &(scene_data[SCENE_GAME_MAP].projectile);
int aim_path_index = projectile->projectile_path_pairs_index;
Parabola* projectile_parabola = projectile->parabola;
int projectile_x = projectile_parabola->parabolic_path[aim_path_index*2];
int projectile_y = projectile_parabola->parabolic_path[aim_path_index*2 + 1];
//////////////////////////////////////////////////
// Check only those tanks which are alive, others will have distance -1
int p1_tanks_total = scene_data[SCENE_GAME_MAP].player_1_tanks;
int p2_tanks_total = scene_data[SCENE_GAME_MAP].player_2_tanks;
for(int t = 0; t < p1_tanks_total; t++){
Tank* temp_tank = &(scene_data[SCENE_GAME_MAP].player_1_tanks_obj[t]);
// If the tank is dead, set distance to -1;
if(temp_tank->is_alive == false){
temp_tank->distance_from_projectile = -1;
}else{
// Calculate distance
int tank_x = temp_tank->x;
int tank_y = temp_tank->y;
temp_tank->distance_from_projectile = GetDistance(tank_x, DISPLAY_HEIGHT-tank_y, projectile_x, projectile_y);
}
printf("Player 1 distances from projectile: %.2f\n",temp_tank->distance_from_projectile);
}
for(int t = 0; t < p2_tanks_total; t++){
Tank* temp_tank = &(scene_data[SCENE_GAME_MAP].player_2_tanks_obj[t]);
// If the tank is dead, set distance to -1;
if(temp_tank->is_alive == false){
temp_tank->distance_from_projectile = -1;
}else{
// Calculate distance
int tank_x = temp_tank->x;
int tank_y = temp_tank->y;
temp_tank->distance_from_projectile = GetDistance(tank_x, DISPLAY_HEIGHT-tank_y, projectile_x, projectile_y);
}
printf("Player 2 distances from projectile: %.2f\n",temp_tank->distance_from_projectile);
}
return;
}
/* This function calculates immediate distance of projectile from the ground */
void RefreshProjectileGroundDistance(SceneData* scene_data){
// Load projectile and get the x and y coordinates
Projectile* projectile = &(scene_data[SCENE_GAME_MAP].projectile);
int aim_path_index = projectile->projectile_path_pairs_index;
Parabola* projectile_parabola = projectile->parabola;
int projectile_x = projectile_parabola->parabolic_path[aim_path_index*2];
int projectile_y = projectile_parabola->parabolic_path[aim_path_index*2 + 1];
//////////////////////////////////////////////////
/* We will use the x index of parabola to get the y information about the
height from array map */
int ground_distance = projectile_y - (DISPLAY_HEIGHT - scene_data[SCENE_GAME_MAP].game_map_array[projectile_x]);
printf("Distance from the ground is: %d\n", ground_distance);
// Save the height info to projectile
projectile->distance_from_ground = ground_distance;
return;
}
/* This function calculates distance between two points {x1,y1,x2,y2} */
double GetDistance(int x1, int y1, int x2, int y2){
int y_dist = abs(y2-y1);
int x_dist = abs(x2-x1);
printf("y_dist = %d x_dist = %d\n", y_dist, x_dist);
double distance = ((double)sqrt(y_dist*y_dist + x_dist*x_dist));
printf("Distance calculated: %f\n", distance);
return distance;
}
/* This function will decide if the projectile should explode.
If it should, 'to_explode' variable in projectile will be set to true*/
void ShouldProjectileExplode(SceneData* scene_data){
// Load projectile
Projectile* projectile = &(scene_data[SCENE_GAME_MAP].projectile);
// Iterate for each living tank and decide if the projectile should explode
int p1_tanks_total = scene_data[SCENE_GAME_MAP].player_1_tanks;
int p2_tanks_total = scene_data[SCENE_GAME_MAP].player_2_tanks;
for(int t = 0; t < p1_tanks_total; t++){
Tank* temp_tank = &(scene_data[SCENE_GAME_MAP].player_1_tanks_obj[t]);
if(temp_tank->is_alive == true){ // Is the tank alive?
if((temp_tank->distance_from_projectile) < PROJECTILE_TANK_DISTANCE_EXPLOSION_TRIGER){
// EXPLODE
printf("Projectile should explode!!!\n");
projectile->to_explode = true;
return;
}
}
}
for(int t = 0; t < p2_tanks_total; t++){
Tank* temp_tank = &(scene_data[SCENE_GAME_MAP].player_2_tanks_obj[t]);
if(temp_tank->is_alive == true){ // Is the tank alive?
if((temp_tank->distance_from_projectile) < PROJECTILE_TANK_DISTANCE_EXPLOSION_TRIGER){
// EXPLODE
printf("Projectile should explode!!!\n");
projectile->to_explode = true;
return;
}
}
}
///////////////////////////////////////////////////////////////////////////
// Check the ground distance and decide if the projectile should explode
if((projectile->distance_from_ground) < PROJECTILE_GROUND_DISTANCE_EXPLOSION_TRIGER){
// EXPLODE
printf("Projectile should explode!!!\n");
projectile->to_explode = true;
return;
}
return;
}
/* This function dealth damage to its surroundings, player tanks and terrain */
void ProjectileDoDamage(SceneData* scene_data){
// Load projectile
Projectile* projectile = &(scene_data[SCENE_GAME_MAP].projectile);
// Distance up to which players and terrain is damaged
int explosion_radius = PROJECTILE_ATTRIBUTES[projectile->type][PROJECTILE_EXPLOSION_RADIUS];
// Damage the projectile will make to players around (distance < explosion_radius)
int projectile_damage = PROJECTILE_ATTRIBUTES[projectile->type][PROJECTILE_DAMAGE];
/* Iterate through all the tanks and if the tank is alive
subtract their health with 'projectile_damage'*/
int p1_tanks_total = scene_data[SCENE_GAME_MAP].player_1_tanks;
int p2_tanks_total = scene_data[SCENE_GAME_MAP].player_2_tanks;
for(int t = 0; t < p1_tanks_total; t++){
Tank* temp_tank = &(scene_data[SCENE_GAME_MAP].player_1_tanks_obj[t]);
if(temp_tank->is_alive == true){ // Is the tank alive?
printf("temp_tank->distance_from_projectile <= explosion_radius ::: %f <= %d\n", temp_tank->distance_from_projectile, explosion_radius);
if((temp_tank->distance_from_projectile) <= explosion_radius){
// Subtract health
printf("Subtracting health! old health: %d ", temp_tank->health);
// Do damage linearly