1 /*
2 Copyright (C) 2009-2021 Parallel Realities
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 */
19
20 #include "headers.h"
21
22 #include "audio/audio.h"
23 #include "audio/music.h"
24 #include "entity.h"
25 #include "event/map_trigger.h"
26 #include "game.h"
27 #include "graphics/graphics.h"
28 #include "hud.h"
29 #include "init.h"
30 #include "inventory.h"
31 #include "map.h"
32 #include "medal.h"
33 #include "menu/io_menu.h"
34 #include "menu/main_menu.h"
35 #include "menu/menu.h"
36 #include "player.h"
37 #include "system/error.h"
38 #include "system/load_save.h"
39 #include "system/random.h"
40 #include "system/resources.h"
41
42 extern Game game;
43 extern Entity *self;
44
45 static ContinueData continueData;
46
47 static void shake(void);
48 static void wipeOutRightToLeft(void);
49 static void wipeOutLeftToRight(void);
50 static void wipeInRightToLeft(void);
51 static void wipeInLeftToRight(void);
52 static void wipeInCircleToLarge(void);
53 static void wipeInCircleToSmall(void);
54 static void wipeOutCircleToLarge(void);
55 static void wipeOutCircleToSmall(void);
56 static void creditsWipeOut(void);
57 static void fadeToNormal(void);
58 static void initEndCredits(void);
59
initGame()60 void initGame()
61 {
62 game.drawScreen = TRUE;
63
64 if (game.paused == TRUE)
65 {
66 pauseGame();
67 }
68
69 game.status = IN_GAME;
70
71 game.menu = initMainMenu();
72
73 game.drawMenu = &drawMainMenu;
74
75 game.shakeThinkTime = 0;
76
77 game.kills = 0;
78
79 game.batsDrowned = 0;
80
81 game.timesEaten = 0;
82
83 game.arrowsFired = 0;
84
85 game.playTime = 0;
86
87 game.distanceTravelled = 0;
88
89 game.timeSpentAsSlime = 0;
90
91 game.attacksBlocked = 0;
92
93 game.thinkTime = 0;
94
95 game.continues = 0;
96
97 game.secretsFound = 0;
98
99 game.cheating = FALSE;
100
101 game.infiniteArrows = FALSE;
102
103 game.infiniteEnergy = FALSE;
104
105 game.lavaIsFatal = TRUE;
106
107 game.mapExitable = 0;
108
109 game.canContinue = FALSE;
110
111 game.overrideMusic = FALSE;
112
113 game.showHUD = TRUE;
114
115 game.offsetX = game.offsetY = 0;
116
117 if (game.pauseSurface != NULL)
118 {
119 destroyTexture(game.pauseSurface);
120
121 game.pauseSurface = NULL;
122 }
123
124 if (game.gameOverSurface != NULL)
125 {
126 destroyTexture(game.gameOverSurface);
127
128 game.gameOverSurface = NULL;
129 }
130 }
131
doGame()132 void doGame()
133 {
134 /* Execute the action if there is one */
135
136 doGameOver();
137
138 fadeToNormal();
139
140 if (game.shakeThinkTime > 0 || game.shakeThinkTime == -1)
141 {
142 shake();
143
144 if (game.shakeThinkTime > 0)
145 {
146 game.shakeThinkTime--;
147
148 if (game.shakeThinkTime <= 0)
149 {
150 game.offsetX = game.offsetY = 0;
151 }
152 }
153 }
154
155 if (game.weatherType != NO_WEATHER)
156 {
157 game.weatherAction();
158 }
159
160 game.playTime++;
161 }
162
freeGame()163 void freeGame()
164 {
165 if (game.pauseSurface != NULL)
166 {
167 destroyTexture(game.pauseSurface);
168
169 game.pauseSurface = NULL;
170 }
171
172 if (game.gameOverSurface != NULL)
173 {
174 destroyTexture(game.gameOverSurface);
175
176 game.gameOverSurface = NULL;
177 }
178 }
179
drawWeather()180 void drawWeather()
181 {
182 if (game.weatherType != NO_WEATHER)
183 {
184 game.weatherDraw();
185 }
186 }
187
drawGame()188 void drawGame()
189 {
190 float alpha;
191
192 if (game.alphaSurface.w != 0 && game.alphaSurface.h != 0)
193 {
194 alpha = 255.0f / game.alphaTime;
195
196 alpha *= game.thinkTime;
197
198 drawBox(game.alphaSurface.x, game.alphaSurface.y, game.alphaSurface.w, game.alphaSurface.h, game.alphaSurface.r, game.alphaSurface.g, game.alphaSurface.b, alpha);
199 }
200
201 drawGameOver();
202
203 if (game.transition != NULL)
204 {
205 game.transition();
206 }
207 }
208
setTransition(int type,void (* func)(void))209 void setTransition(int type, void (*func)(void))
210 {
211 if (type == TRANSITION_OUT)
212 {
213 switch (prand() % MAX_OUT_TRANSITIONS)
214 {
215 case WIPE_OUT_RIGHT_TO_LEFT:
216 game.transitionX = SCREEN_WIDTH;
217 game.transitionY = SCREEN_HEIGHT;
218 game.transition = &wipeOutRightToLeft;
219 break;
220
221 case WIPE_OUT_CIRCLE_TO_LARGE:
222 game.transitionX = 0;
223 game.transition = &wipeOutCircleToLarge;
224 break;
225
226 case WIPE_OUT_CIRCLE_TO_SMALL:
227 game.transitionX = SCREEN_WIDTH;
228 game.transition = &wipeOutCircleToSmall;
229 break;
230
231 default:
232 game.transitionX = 0;
233 game.transitionY = SCREEN_HEIGHT;
234 game.transition = &wipeOutLeftToRight;
235 break;
236 }
237 }
238
239 else
240 {
241 switch (prand() % MAX_IN_TRANSITIONS)
242 {
243 case WIPE_IN_RIGHT_TO_LEFT:
244 game.transitionX = SCREEN_WIDTH;
245 game.transitionY = SCREEN_HEIGHT;
246 game.transition = &wipeInRightToLeft;
247 break;
248
249 case WIPE_IN_CIRCLE_TO_LARGE:
250 game.transitionX = 0;
251 game.transition = &wipeInCircleToLarge;
252 break;
253
254 case WIPE_IN_CIRCLE_TO_SMALL:
255 game.transitionX = SCREEN_WIDTH;
256 game.transition = &wipeInCircleToSmall;
257 break;
258
259 default:
260 game.transitionX = 0;
261 game.transitionY = SCREEN_HEIGHT;
262 game.transition = &wipeInLeftToRight;
263 break;
264 }
265 }
266
267 game.transitionCallback = func;
268 }
269
shakeScreen(int shakeStrength,int time)270 void shakeScreen(int shakeStrength, int time)
271 {
272 game.offsetX = game.offsetY = 0;
273
274 game.shakeThinkTime = time;
275 game.shakeStrength = shakeStrength;
276 }
277
shake()278 static void shake()
279 {
280 switch (game.shakeStrength)
281 {
282 case LIGHT:
283 game.offsetX = prand() % 2 * (prand() % 2 == 0 ? -1 : 1);
284 game.offsetY = prand() % 2 * (prand() % 2 == 0 ? -1 : 1);
285 break;
286
287 case MEDIUM:
288 game.offsetX = prand() % 4 * (prand() % 2 == 0 ? -1 : 1);
289 game.offsetY = prand() % 4 * (prand() % 2 == 0 ? -1 : 1);
290 break;
291
292 case STRONG:
293 game.offsetX = prand() % 6 * (prand() % 2 == 0 ? -1 : 1);
294 game.offsetY = prand() % 6 * (prand() % 2 == 0 ? -1 : 1);
295 break;
296 }
297 }
298
wipeOutRightToLeft()299 static void wipeOutRightToLeft()
300 {
301 if (game.transitionX <= -15)
302 {
303 game.transition = NULL;
304
305 if (game.transitionCallback != NULL)
306 {
307 clearScreen(0, 0, 0);
308
309 game.transitionCallback();
310 }
311
312 return;
313 }
314
315 drawBox(game.transitionX < 0 ? 0 : game.transitionX, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0, 255);
316
317 game.transitionX -= 15;
318 }
319
wipeOutLeftToRight()320 static void wipeOutLeftToRight()
321 {
322 if (game.transitionX > SCREEN_WIDTH + 15)
323 {
324 game.transition = NULL;
325
326 if (game.transitionCallback != NULL)
327 {
328 clearScreen(0, 0, 0);
329
330 game.transitionCallback();
331 }
332
333 return;
334 }
335
336 drawBox(0, 0, game.transitionX, SCREEN_HEIGHT, 0, 0, 0, 255);
337
338 game.transitionX += 15;
339 }
340
wipeInLeftToRight()341 static void wipeInLeftToRight()
342 {
343 if (game.transitionX >= SCREEN_WIDTH)
344 {
345 game.transition = NULL;
346
347 if (game.transitionCallback != NULL)
348 {
349 clearScreen(0, 0, 0);
350
351 game.transitionCallback();
352 }
353
354 return;
355 }
356
357 drawBox(game.transitionX, 0, SCREEN_WIDTH - game.transitionX, SCREEN_HEIGHT, 0, 0, 0, 255);
358
359 game.transitionX += 15;
360 }
361
wipeInRightToLeft()362 static void wipeInRightToLeft()
363 {
364 if (game.transitionX < 0)
365 {
366 game.transition = NULL;
367
368 if (game.transitionCallback != NULL)
369 {
370 clearScreen(0, 0, 0);
371
372 game.transitionCallback();
373 }
374
375 return;
376 }
377
378 drawBox(0, 0, game.transitionX, SCREEN_HEIGHT, 0, 0, 0, 255);
379
380 game.transitionX -= 15;
381 }
382
wipeInCircleToLarge()383 static void wipeInCircleToLarge()
384 {
385 if (game.transitionX > SCREEN_WIDTH)
386 {
387 game.transition = NULL;
388
389 if (game.transitionCallback != NULL)
390 {
391 clearScreen(0, 0, 0);
392
393 game.transitionCallback();
394 }
395
396 return;
397 }
398
399 drawCircleFromSurface(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, game.transitionX);
400
401 game.transitionX += 10;
402 }
403
wipeInCircleToSmall()404 static void wipeInCircleToSmall()
405 {
406 if (game.transitionX < 0)
407 {
408 game.transition = NULL;
409
410 if (game.transitionCallback != NULL)
411 {
412 clearScreen(0, 0, 0);
413
414 game.transitionCallback();
415 }
416
417 return;
418 }
419
420 drawCircle(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, game.transitionX, 0, 0, 0);
421
422 game.transitionX -= 10;
423 }
424
wipeOutCircleToLarge()425 static void wipeOutCircleToLarge()
426 {
427 if (game.transitionX > SCREEN_WIDTH)
428 {
429 game.transition = NULL;
430
431 if (game.transitionCallback != NULL)
432 {
433 clearScreen(0, 0, 0);
434
435 game.transitionCallback();
436 }
437
438 return;
439 }
440
441 drawCircle(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, game.transitionX, 0, 0, 0);
442
443 game.transitionX += 10;
444 }
445
wipeOutCircleToSmall()446 static void wipeOutCircleToSmall()
447 {
448 if (game.transitionX <= -10)
449 {
450 game.transition = NULL;
451
452 if (game.transitionCallback != NULL)
453 {
454 clearScreen(0, 0, 0);
455
456 game.transitionCallback();
457 }
458
459 return;
460 }
461
462 drawCircleFromSurface(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, game.transitionX < 0 ? 0 : game.transitionX);
463
464 game.transitionX -= 10;
465 }
466
creditsWipeOut()467 static void creditsWipeOut()
468 {
469 if (game.transitionX <= -5)
470 {
471 game.transition = NULL;
472
473 if (game.transitionCallback != NULL)
474 {
475 clearScreen(0, 0, 0);
476
477 game.transitionCallback();
478 }
479
480 return;
481 }
482
483 drawCircleFromSurface(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, game.transitionX < 0 ? 0 : game.transitionX);
484
485 game.transitionX -= 5;
486
487 if (game.transitionX <= spotlightSize() / 2)
488 {
489 if (game.transitionY > 0)
490 {
491 game.transitionX = spotlightSize() / 2;
492
493 game.transitionY--;
494 }
495 }
496 }
497
setNextLevel(char * name,char * playerStart)498 void setNextLevel(char *name, char *playerStart)
499 {
500 STRNCPY(game.nextMap, name, sizeof(game.nextMap));
501 STRNCPY(game.playerStart, playerStart, sizeof(game.playerStart));
502 }
503
setNextLevelFromScript(char * token)504 void setNextLevelFromScript(char *token)
505 {
506 char *name, *playerStart, *savePtr;
507
508 name = strtok_r(token, " ", &savePtr);
509 playerStart = strtok_r(NULL, "\0", &savePtr);
510
511 setNextLevel(name, playerStart);
512
513 setTransition(TRANSITION_OUT, &goToNextMap);
514
515 if (game.overrideMusic == FALSE)
516 {
517 fadeOutMusic(1000);
518 }
519 }
520
showEndCredits()521 void showEndCredits()
522 {
523 game.overrideMusic = FALSE;
524
525 game.transitionX = SCREEN_WIDTH;
526 game.transitionY = 120;
527 game.transition = &creditsWipeOut;
528
529 game.transitionCallback = &initEndCredits;
530 }
531
initEndCredits()532 static void initEndCredits()
533 {
534 game.status = IN_CREDITS;
535 }
536
goToNextMap()537 void goToNextMap()
538 {
539 Entity *start;
540
541 saveTemporaryData();
542
543 freeLevelResources();
544
545 if (hasPersistance(game.nextMap) == FALSE)
546 {
547 loadMap(game.nextMap, TRUE);
548 }
549
550 else
551 {
552 loadMap(game.nextMap, FALSE);
553
554 loadPersitanceData(game.nextMap);
555 }
556
557 if (strcmpignorecase(game.playerStart, "PLAYER_START") != 0)
558 {
559 start = getEntityByObjectiveName(game.playerStart);
560
561 if (start == NULL)
562 {
563 showErrorAndExit("Could not find player start %s", game.playerStart);
564 }
565
566 loadPlayer(start->startX, start->startY, NULL);
567 }
568
569 fireMapTrigger(game.nextMap);
570
571 game.nextMap[0] = '\0';
572 game.playerStart[0] = '\0';
573
574 game.offsetX = game.offsetY = 0;
575 game.shakeThinkTime = 0;
576
577 clearScreen(0, 0, 0);
578 }
579
setCheckpoint(float x,float y)580 void setCheckpoint(float x, float y)
581 {
582 game.checkpointX = x;
583 game.checkpointY = y;
584 }
585
getCheckpoint(float * x,float * y)586 void getCheckpoint(float *x, float *y)
587 {
588 *x = game.checkpointX;
589 *y = game.checkpointY;
590 }
591
pauseGame()592 void pauseGame()
593 {
594 switch (game.status)
595 {
596 case IN_GAME:
597 case IN_TITLE:
598 game.paused = TRUE;
599
600 game.previousStatus = game.status;
601
602 game.status = IN_MENU;
603
604 game.menu = initMainMenu();
605
606 if (game.pauseSurface == NULL)
607 {
608 game.pauseSurface = copyScreen();
609 }
610
611 if (game.previousStatus == IN_GAME)
612 {
613 pauseMusic(TRUE);
614
615 pauseSound(TRUE);
616 }
617 break;
618
619 case IN_EDITOR:
620 cleanup(0);
621 break;
622
623 case IN_INVENTORY:
624 case IN_CREDITS:
625 /* Do nothing */
626 break;
627
628 default:
629 if (game.menu->returnAction == NULL)
630 {
631 if (game.pauseSurface != NULL)
632 {
633 destroyTexture(game.pauseSurface);
634
635 game.pauseSurface = NULL;
636 }
637
638 game.paused = FALSE;
639
640 game.status = game.previousStatus;
641
642 game.menu = initMainMenu();
643
644 game.drawMenu = &drawMainMenu;
645
646 pauseMusic(FALSE);
647
648 pauseSound(FALSE);
649 }
650 break;
651 }
652 }
653
showSaveDialog()654 void showSaveDialog()
655 {
656 pauseGame();
657
658 pauseMusic(FALSE);
659
660 pauseSound(FALSE);
661
662 game.status = IN_MENU;
663
664 game.paused = TRUE;
665
666 game.menu = initIOMenu(TRUE);
667
668 game.drawMenu = &drawIOMenu;
669 }
670
pauseGameInventory()671 void pauseGameInventory()
672 {
673 switch (game.status)
674 {
675 case IN_GAME:
676 game.status = IN_INVENTORY;
677
678 clearInventoryDescription();
679
680 if (game.pauseSurface == NULL)
681 {
682 game.pauseSurface = copyScreen();
683 }
684 break;
685
686 case IN_INVENTORY:
687 if (game.pauseSurface != NULL)
688 {
689 destroyTexture(game.pauseSurface);
690
691 game.pauseSurface = NULL;
692 }
693
694 game.status = IN_GAME;
695 break;
696 }
697 }
698
focusLost()699 void focusLost()
700 {
701 #if DEV == 0
702 if (game.paused == FALSE && game.status == IN_GAME)
703 {
704 pauseGame();
705 }
706 #endif
707 }
708
showPauseDialog()709 void showPauseDialog()
710 {
711 drawImage(game.pauseSurface, 0, 0, FALSE, 255);
712 }
713
resetGameSettings()714 void resetGameSettings()
715 {
716 game.audio = TRUE;
717 game.sfxDefaultVolume = 10;
718 game.musicDefaultVolume = 8;
719 game.showHints = TRUE;
720 game.fullscreen = FALSE;
721 game.audioQuality = 22050;
722 }
723
writeGameSettingsToFile(FILE * fp)724 void writeGameSettingsToFile(FILE *fp)
725 {
726 fprintf(fp, "GAME_SETTINGS\n");
727 fprintf(fp, "AUDIO %d\n", game.audio == TRUE ? TRUE : game.audioDisabled);
728 fprintf(fp, "SFX_VOLUME %d\n", game.sfxDefaultVolume);
729 fprintf(fp, "MUSIC_VOLUME %d\n", game.musicDefaultVolume);
730 fprintf(fp, "HINTS %d\n", game.showHints);
731 fprintf(fp, "FULLSCREEN %d\n", game.fullscreen);
732 fprintf(fp, "AUDIO_QUALITY %d\n", game.audioQuality);
733 fprintf(fp, "FONT %s\n", game.customFont);
734 fprintf(fp, "SMALL_FONT_SIZE %d\n", game.fontSizeSmall == 0 ? NORMAL_FONT_SIZE : game.fontSizeSmall);
735 fprintf(fp, "LARGE_FONT_SIZE %d\n", game.fontSizeLarge == 0 ? LARGE_FONT_SIZE : game.fontSizeLarge);
736 }
737
readGameSettingsFromFile(char * buffer)738 void readGameSettingsFromFile(char *buffer)
739 {
740 char *line, *token, *savePtr;
741
742 savePtr = NULL;
743
744 line = strtok_r(buffer, "\n", &savePtr);
745
746 while (line != NULL)
747 {
748 token = strtok(line, " ");
749
750 if (strcmpignorecase(token, "AUDIO") == 0)
751 {
752 token = strtok(NULL, "\0");
753
754 if (token != NULL)
755 {
756 game.audio = atoi(token);
757 }
758 }
759
760 else if (strcmpignorecase(token, "SFX_VOLUME") == 0)
761 {
762 token = strtok(NULL, "\0");
763
764 if (token != NULL)
765 {
766 game.sfxDefaultVolume = atoi(token);
767 }
768 }
769
770 else if (strcmpignorecase(token, "MUSIC_VOLUME") == 0)
771 {
772 token = strtok(NULL, "\0");
773
774 if (token != NULL)
775 {
776 game.musicDefaultVolume = atoi(token);
777 }
778 }
779
780 else if (strcmpignorecase(token, "HINTS") == 0)
781 {
782 token = strtok(NULL, "\0");
783
784 if (token != NULL)
785 {
786 game.showHints = atoi(token);
787 }
788 }
789
790 else if (strcmpignorecase(token, "FULLSCREEN") == 0)
791 {
792 token = strtok(NULL, "\0");
793
794 if (token != NULL)
795 {
796 game.fullscreen = atoi(token);
797 }
798 }
799
800 else if (strcmpignorecase(token, "AUDIO_QUALITY") == 0)
801 {
802 token = strtok(NULL, "\0");
803
804 if (token != NULL)
805 {
806 game.audioQuality = atoi(token);
807 }
808
809 if (game.audioQuality == 0)
810 {
811 game.audioQuality = 22050;
812 }
813 }
814
815 else if (strcmpignorecase(token, "FONT") == 0)
816 {
817 token = strtok(NULL, "\0");
818
819 if (token != NULL)
820 {
821 STRNCPY(game.customFont, token, MAX_FILE_LENGTH);
822 }
823 }
824
825 else if (strcmpignorecase(token, "SMALL_FONT_SIZE") == 0)
826 {
827 token = strtok(NULL, "\0");
828
829 if (token != NULL)
830 {
831 game.fontSizeSmall = atoi(token);
832 }
833 }
834
835 else if (strcmpignorecase(token, "LARGE_FONT_SIZE") == 0)
836 {
837 token = strtok(NULL, "\0");
838
839 if (token != NULL)
840 {
841 game.fontSizeLarge = atoi(token);
842 }
843 }
844
845 line = strtok_r(NULL, "\n", &savePtr);
846 }
847
848 if (strlen(game.customFont) != 0)
849 {
850 if (game.fontSizeSmall <= 0 || game.fontSizeLarge <= 0)
851 {
852 showErrorAndExit(_("SMALL_FONT_SIZE and LARGE_FONT_SIZE must be specified when using a custom font"));
853 }
854 }
855 }
856
initGameOver()857 void initGameOver()
858 {
859 if (game.gameOverSurface == NULL)
860 {
861 game.gameOverSurface = loadImage("gfx/common/gameover.png");
862 }
863
864 game.gameOverX = 0;
865 }
866
doGameOver()867 void doGameOver()
868 {
869 if (game.gameOverSurface != NULL)
870 {
871 game.gameOverX += 3;
872 }
873 }
874
drawGameOver()875 void drawGameOver()
876 {
877 if (game.gameOverSurface != NULL)
878 {
879 if (game.gameOverX >= game.gameOverSurface->w)
880 {
881 game.gameOverX = game.gameOverSurface->w;
882 }
883
884 drawClippedImage(game.gameOverSurface, 0, 0, (SCREEN_WIDTH - game.gameOverSurface->w) / 2, (SCREEN_HEIGHT - game.gameOverSurface->h) / 2, game.gameOverX, game.gameOverSurface->h);
885 }
886 }
887
getPlayTimeAsString()888 char *getPlayTimeAsString()
889 {
890 /* 1 second is 60 frames */
891
892 int hours, minutes;
893 long tempTime;
894 char *timeString;
895
896 timeString = malloc(15 * sizeof(char));
897
898 if (timeString == NULL)
899 {
900 showErrorAndExit("Failed to allocate a whole %d bytes for Play Time string...", 15 * (int)sizeof(char));
901 }
902
903 tempTime = game.playTime;
904
905 hours = tempTime / (60 * 60 * 60);
906
907 tempTime -= hours * 60 * 60 * 60;
908
909 minutes = tempTime / (60 * 60);
910
911 SNPRINTF(timeString, 15, "%dH %dM", hours, minutes);
912
913 return timeString;
914 }
915
getPlayTimeHours()916 char *getPlayTimeHours()
917 {
918 /* 1 second is 60 frames */
919
920 int hours;
921 long tempTime;
922 char *timeString;
923
924 timeString = malloc(5 * sizeof(char));
925
926 if (timeString == NULL)
927 {
928 showErrorAndExit("Failed to allocate a whole %d bytes for Play Time Hours string...", 5 * (int)sizeof(char));
929 }
930
931 tempTime = game.playTime;
932
933 hours = tempTime / (60 * 60 * 60);
934
935 SNPRINTF(timeString, 5, "%d", hours);
936
937 return timeString;
938 }
939
getSlimeTimeAsString()940 char *getSlimeTimeAsString()
941 {
942 /* 1 second is 60 frames */
943
944 int minutes;
945 long tempTime;
946 char *timeString;
947
948 timeString = malloc(15 * sizeof(char));
949
950 if (timeString == NULL)
951 {
952 showErrorAndExit("Failed to allocate a whole %d bytes for Slime Time string...", 15 * (int)sizeof(char));
953 }
954
955 tempTime = game.timeSpentAsSlime;
956
957 minutes = tempTime / (60 * 60);
958
959 tempTime -= minutes * 60 * 60;
960
961 tempTime /= 60;
962
963 SNPRINTF(timeString, 15, "%dM %ldS", minutes, tempTime);
964
965 return timeString;
966 }
967
fadeFromColour(int r,int g,int b,int fadeTime)968 void fadeFromColour(int r, int g, int b, int fadeTime)
969 {
970 game.alphaTime = game.thinkTime = fadeTime;
971
972 game.alphaSurface.x = 0;
973 game.alphaSurface.y = 0;
974 game.alphaSurface.w = SCREEN_WIDTH;
975 game.alphaSurface.h = SCREEN_HEIGHT;
976
977 game.alphaSurface.r = r;
978 game.alphaSurface.g = g;
979 game.alphaSurface.b = b;
980
981 drawBox(0, 0, game.transitionX, SCREEN_HEIGHT, 0, 0, 0, 255);
982 }
983
fadeToNormal()984 static void fadeToNormal()
985 {
986 if (game.alphaSurface.w != 0 && game.alphaSurface.h != 0)
987 {
988 game.thinkTime--;
989
990 if (game.thinkTime <= 0)
991 {
992 game.alphaSurface.w = 0;
993 game.alphaSurface.h = 0;
994 }
995 }
996 }
997
increaseKillCount()998 void increaseKillCount()
999 {
1000 game.kills++;
1001
1002 if (game.kills == 100)
1003 {
1004 addMedal("kill_100");
1005 }
1006
1007 else if (game.kills == 200)
1008 {
1009 addMedal("kill_200");
1010 }
1011
1012 else if (game.kills == 500)
1013 {
1014 addMedal("kill_500");
1015 }
1016
1017 else if (game.kills == 1000)
1018 {
1019 addMedal("kill_1000");
1020 }
1021
1022 else if (game.kills == 2000)
1023 {
1024 addMedal("kill_2000");
1025 }
1026 }
1027
increaseSecretsFound()1028 void increaseSecretsFound()
1029 {
1030 game.secretsFound++;
1031
1032 setInfoBoxMessage(90, 0, 255, 0, _("You found a secret!"));
1033
1034 playSound("sound/common/secret");
1035
1036 if (game.secretsFound == TOTAL_SECRETS)
1037 {
1038 addMedal("all_secrets");
1039 }
1040 }
1041
setContinuePoint(int cameraFollow,char * boss,void (* resumeAction)(void))1042 void setContinuePoint(int cameraFollow, char *boss, void (*resumeAction)(void))
1043 {
1044 Entity *temp;
1045
1046 temp = self;
1047
1048 game.canContinue = TRUE;
1049
1050 continueData.cameraMinX = getCameraMinX();
1051 continueData.cameraMinY = getCameraMinY();
1052 continueData.cameraMaxX = getCameraMaxX();
1053 continueData.cameraMaxY = getCameraMaxY();
1054
1055 continueData.cameraFollow = cameraFollow;
1056
1057 continueData.resumeAction = resumeAction;
1058
1059 STRNCPY(continueData.boss, boss, sizeof(continueData.boss));
1060
1061 saveContinueData();
1062
1063 self = temp;
1064 }
1065
getContinuePoint()1066 void getContinuePoint()
1067 {
1068 Entity *e;
1069
1070 loadContinueData();
1071
1072 limitCamera(continueData.cameraMinX, continueData.cameraMinY, continueData.cameraMaxX, continueData.cameraMaxY);
1073
1074 setMapStartX(continueData.cameraMinX);
1075 setMapStartY(continueData.cameraMinY);
1076
1077 setCameraPosition(continueData.cameraMinX, continueData.cameraMinY);
1078
1079 limitPlayerToCameraLimits();
1080
1081 if (continueData.cameraFollow == FALSE)
1082 {
1083 centerMapOnEntity(NULL);
1084 }
1085
1086 else
1087 {
1088 cameraSnapToTargetEntity();
1089 }
1090
1091 if (continueData.resumeAction != NULL)
1092 {
1093 e = getEntityByName(continueData.boss);
1094
1095 if (e == NULL)
1096 {
1097 showErrorAndExit("Continue data could not find %s", continueData.boss);
1098 }
1099
1100 e->action = continueData.resumeAction;
1101 }
1102
1103 game.continues++;
1104
1105 if (game.continues == 20)
1106 {
1107 addMedal("continue_20");
1108 }
1109 }
1110
clearContinuePoint()1111 void clearContinuePoint()
1112 {
1113 game.canContinue = FALSE;
1114
1115 continueData.resumeAction = NULL;
1116
1117 continueData.boss[0] = '\0';
1118 }
1119
updateExitCount(int exitCount)1120 void updateExitCount(int exitCount)
1121 {
1122 game.mapExitable += exitCount;
1123
1124 if (game.mapExitable < 0)
1125 {
1126 game.mapExitable = 0;
1127 }
1128 }
1129