1 | /******************************************************************************************* |
2 | * |
3 | * raylib game - Dr. Turtle & Mr. Gamera |
4 | * |
5 | * Welcome to raylib! |
6 | * |
7 | * To test examples, just press F6 and execute raylib_compile_execute script |
8 | * Note that compiled executable is placed in the same folder as .c file |
9 | * |
10 | * You can find all basic examples on C:\raylib\raylib\examples folder or |
11 | * raylib official webpage: www.raylib.com |
12 | * |
13 | * Enjoy using raylib. :) |
14 | * |
15 | * This game has been created using raylib 1.6 (www.raylib.com) |
16 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) |
17 | * |
18 | * Copyright (c) 2014 Ramon Santamaria (@raysan5) |
19 | * |
20 | ********************************************************************************************/ |
21 | |
22 | #include "raylib.h" |
23 | |
24 | #include <math.h> // Used for sinf() |
25 | |
26 | #define MAX_ENEMIES 10 |
27 | |
28 | typedef enum { TITLE, GAMEPLAY, ENDING } GameScreen; |
29 | |
30 | int main() |
31 | { |
32 | // Initialization |
33 | //-------------------------------------------------------------------------------------- |
34 | const int screenWidth = 1280; |
35 | const int screenHeight = 720; |
36 | |
37 | // Init window |
38 | InitWindow(screenWidth, screenHeight, "Dr. Turtle & Mr. GAMERA" ); |
39 | |
40 | // Initialize audio device |
41 | InitAudioDevice(); |
42 | |
43 | // Load game resources: textures |
44 | Texture2D sky = LoadTexture("resources/sky.png" ); |
45 | Texture2D mountains = LoadTexture("resources/mountains.png" ); |
46 | Texture2D sea = LoadTexture("resources/sea.png" ); |
47 | Texture2D title = LoadTexture("resources/title.png" ); |
48 | Texture2D turtle = LoadTexture("resources/turtle.png" ); |
49 | Texture2D gamera = LoadTexture("resources/gamera.png" ); |
50 | Texture2D shark = LoadTexture("resources/shark.png" ); |
51 | Texture2D orca = LoadTexture("resources/orca.png" ); |
52 | Texture2D swhale = LoadTexture("resources/swhale.png" ); |
53 | Texture2D fish = LoadTexture("resources/fish.png" ); |
54 | Texture2D gframe = LoadTexture("resources/gframe.png" ); |
55 | |
56 | // Load game resources: fonts |
57 | Font font = LoadFont("resources/komika.png" ); |
58 | |
59 | // Load game resources: sounds |
60 | Sound eat = LoadSound("resources/eat.wav" ); |
61 | Sound die = LoadSound("resources/die.wav" ); |
62 | Sound growl = LoadSound("resources/gamera.wav" ); |
63 | |
64 | // Load music stream and start playing music |
65 | Music music = LoadMusicStream("resources/speeding.ogg" ); |
66 | PlayMusicStream(music); |
67 | |
68 | // Define scrolling variables |
69 | int backScrolling = 0; |
70 | int seaScrolling = 0; |
71 | |
72 | // Define current screen |
73 | GameScreen currentScreen = TITLE; |
74 | |
75 | // Define player variables |
76 | int playerRail = 1; |
77 | Rectangle playerBounds = { 30 + 14, playerRail*120 + 90 + 14, 100, 100 }; |
78 | bool gameraMode = false; |
79 | |
80 | // Define enemies variables |
81 | Rectangle enemyBounds[MAX_ENEMIES]; |
82 | int enemyRail[MAX_ENEMIES]; |
83 | int enemyType[MAX_ENEMIES]; |
84 | bool enemyActive[MAX_ENEMIES]; |
85 | float enemySpeed = 10; |
86 | |
87 | // Init enemies variables |
88 | for (int i = 0; i < MAX_ENEMIES; i++) |
89 | { |
90 | // Define enemy type (all same probability) |
91 | //enemyType[i] = GetRandomValue(0, 3); |
92 | |
93 | // Probability system for enemies type |
94 | int enemyProb = GetRandomValue(0, 100); |
95 | |
96 | if (enemyProb < 30) enemyType[i] = 0; |
97 | else if (enemyProb < 60) enemyType[i] = 1; |
98 | else if (enemyProb < 90) enemyType[i] = 2; |
99 | else enemyType[i] = 3; |
100 | |
101 | // define enemy rail |
102 | enemyRail[i] = GetRandomValue(0, 4); |
103 | |
104 | // Make sure not two consecutive enemies in the same row |
105 | if (i > 0) while (enemyRail[i] == enemyRail[i - 1]) enemyRail[i] = GetRandomValue(0, 4); |
106 | |
107 | enemyBounds[i] = (Rectangle){ screenWidth + 14, 120*enemyRail[i] + 90 + 14, 100, 100 }; |
108 | enemyActive[i] = false; |
109 | } |
110 | |
111 | // Define additional game variables |
112 | int score = 0; |
113 | float distance = 0.0f; |
114 | int hiscore = 0; |
115 | float hidistance = 0.0f; |
116 | int foodBar = 0; |
117 | int framesCounter = 0; |
118 | |
119 | unsigned char blue = 200; |
120 | float timeCounter = 0; |
121 | |
122 | SetTargetFPS(60); // Setup game frames per second |
123 | //-------------------------------------------------------------------------------------- |
124 | |
125 | // Main game loop |
126 | while (!WindowShouldClose()) // Detect window close button or ESC key |
127 | { |
128 | // Update |
129 | //---------------------------------------------------------------------------------- |
130 | UpdateMusicStream(music); // Refill music stream buffers (if required) |
131 | |
132 | framesCounter++; |
133 | |
134 | // Sea color tint effect |
135 | blue = 210 + 25 * sinf(timeCounter); |
136 | timeCounter += 0.01; |
137 | |
138 | // Game screens management |
139 | switch (currentScreen) |
140 | { |
141 | case TITLE: |
142 | { |
143 | // Sea scrolling |
144 | seaScrolling -= 2; |
145 | if (seaScrolling <= -screenWidth) seaScrolling = 0; |
146 | |
147 | // Press enter to change to gameplay screen |
148 | if (IsKeyPressed(KEY_ENTER)) |
149 | { |
150 | currentScreen = GAMEPLAY; |
151 | framesCounter = 0; |
152 | } |
153 | |
154 | } break; |
155 | case GAMEPLAY: |
156 | { |
157 | // Background scrolling logic |
158 | backScrolling--; |
159 | if (backScrolling <= -screenWidth) backScrolling = 0; |
160 | |
161 | // Sea scrolling logic |
162 | seaScrolling -= (enemySpeed - 2); |
163 | if (seaScrolling <= -screenWidth) seaScrolling = 0; |
164 | |
165 | // Player movement logic |
166 | if (IsKeyPressed(KEY_DOWN)) playerRail++; |
167 | else if (IsKeyPressed(KEY_UP)) playerRail--; |
168 | |
169 | // Check player not out of rails |
170 | if (playerRail > 4) playerRail = 4; |
171 | else if (playerRail < 0) playerRail = 0; |
172 | |
173 | // Update player bounds |
174 | playerBounds = (Rectangle){ 30 + 14, playerRail*120 + 90 + 14, 100, 100 }; |
175 | |
176 | // Enemies activation logic (every 40 frames) |
177 | if (framesCounter > 40) |
178 | { |
179 | for (int i = 0; i < MAX_ENEMIES; i++) |
180 | { |
181 | if (enemyActive[i] == false) |
182 | { |
183 | enemyActive[i] = true; |
184 | i = MAX_ENEMIES; |
185 | } |
186 | } |
187 | |
188 | framesCounter = 0; |
189 | } |
190 | |
191 | // Enemies logic |
192 | for (int i = 0; i < MAX_ENEMIES; i++) |
193 | { |
194 | if (enemyActive[i]) |
195 | { |
196 | enemyBounds[i].x -= enemySpeed; |
197 | } |
198 | |
199 | // Check enemies out of screen |
200 | if (enemyBounds[i].x <= 0 - 128) |
201 | { |
202 | enemyActive[i] = false; |
203 | enemyType[i] = GetRandomValue(0, 3); |
204 | enemyRail[i] = GetRandomValue(0, 4); |
205 | |
206 | // Make sure not two consecutive enemies in the same row |
207 | if (i > 0) while (enemyRail[i] == enemyRail[i - 1]) enemyRail[i] = GetRandomValue(0, 4); |
208 | |
209 | enemyBounds[i] = (Rectangle){ screenWidth + 14, 120*enemyRail[i] + 90 + 14, 100, 100 }; |
210 | } |
211 | } |
212 | |
213 | if (!gameraMode) enemySpeed += 0.005; |
214 | |
215 | // Check collision player vs enemies |
216 | for (int i = 0; i < MAX_ENEMIES; i++) |
217 | { |
218 | if (enemyActive[i]) |
219 | { |
220 | if (CheckCollisionRecs(playerBounds, enemyBounds[i])) |
221 | { |
222 | if (enemyType[i] < 3) // Bad enemies |
223 | { |
224 | if (gameraMode) |
225 | { |
226 | if (enemyType[i] == 0) score += 50; |
227 | else if (enemyType[i] == 1) score += 150; |
228 | else if (enemyType[i] == 2) score += 300; |
229 | |
230 | foodBar += 15; |
231 | |
232 | enemyActive[i] = false; |
233 | |
234 | // After enemy deactivation, reset enemy parameters to be reused |
235 | enemyType[i] = GetRandomValue(0, 3); |
236 | enemyRail[i] = GetRandomValue(0, 4); |
237 | |
238 | // Make sure not two consecutive enemies in the same row |
239 | if (i > 0) while (enemyRail[i] == enemyRail[i - 1]) enemyRail[i] = GetRandomValue(0, 4); |
240 | |
241 | enemyBounds[i] = (Rectangle){ screenWidth + 14, 120*enemyRail[i] + 90 + 14, 100, 100 }; |
242 | |
243 | PlaySound(eat); |
244 | } |
245 | else |
246 | { |
247 | // Player die logic |
248 | PlaySound(die); |
249 | |
250 | currentScreen = ENDING; |
251 | framesCounter = 0; |
252 | |
253 | // Save hiscore and hidistance for next game |
254 | if (score > hiscore) hiscore = score; |
255 | if (distance > hidistance) hidistance = distance; |
256 | } |
257 | } |
258 | else // Sweet fish |
259 | { |
260 | enemyActive[i] = false; |
261 | enemyType[i] = GetRandomValue(0, 3); |
262 | enemyRail[i] = GetRandomValue(0, 4); |
263 | |
264 | // Make sure not two consecutive enemies in the same row |
265 | if (i > 0) while (enemyRail[i] == enemyRail[i - 1]) enemyRail[i] = GetRandomValue(0, 4); |
266 | |
267 | enemyBounds[i] = (Rectangle){ screenWidth + 14, 120*enemyRail[i] + 90 + 14, 100, 100 }; |
268 | |
269 | if (!gameraMode) foodBar += 80; |
270 | else foodBar += 25; |
271 | |
272 | score += 10; |
273 | |
274 | if (foodBar == 400) |
275 | { |
276 | gameraMode = true; |
277 | |
278 | PlaySound(growl); |
279 | } |
280 | |
281 | PlaySound(eat); |
282 | } |
283 | } |
284 | } |
285 | } |
286 | |
287 | // Gamera mode logic |
288 | if (gameraMode) |
289 | { |
290 | foodBar--; |
291 | |
292 | if (foodBar <= 0) |
293 | { |
294 | gameraMode = false; |
295 | enemySpeed -= 2; |
296 | if (enemySpeed < 10) enemySpeed = 10; |
297 | } |
298 | } |
299 | |
300 | // Update distance counter |
301 | distance += 0.5f; |
302 | |
303 | } break; |
304 | case ENDING: |
305 | { |
306 | // Press enter to play again |
307 | if (IsKeyPressed(KEY_ENTER)) |
308 | { |
309 | currentScreen = GAMEPLAY; |
310 | |
311 | // Reset player |
312 | playerRail = 1; |
313 | playerBounds = (Rectangle){ 30 + 14, playerRail*120 + 90 + 14, 100, 100 }; |
314 | gameraMode = false; |
315 | |
316 | // Reset enemies data |
317 | for (int i = 0; i < MAX_ENEMIES; i++) |
318 | { |
319 | int enemyProb = GetRandomValue(0, 100); |
320 | |
321 | if (enemyProb < 30) enemyType[i] = 0; |
322 | else if (enemyProb < 60) enemyType[i] = 1; |
323 | else if (enemyProb < 90) enemyType[i] = 2; |
324 | else enemyType[i] = 3; |
325 | |
326 | //enemyType[i] = GetRandomValue(0, 3); |
327 | enemyRail[i] = GetRandomValue(0, 4); |
328 | |
329 | // Make sure not two consecutive enemies in the same row |
330 | if (i > 0) while (enemyRail[i] == enemyRail[i - 1]) enemyRail[i] = GetRandomValue(0, 4); |
331 | |
332 | enemyBounds[i] = (Rectangle){ screenWidth + 14, 120*enemyRail[i] + 90 + 14, 100, 100 }; |
333 | enemyActive[i] = false; |
334 | } |
335 | |
336 | enemySpeed = 10; |
337 | |
338 | // Reset game variables |
339 | score = 0; |
340 | distance = 0.0; |
341 | foodBar = 0; |
342 | framesCounter = 0; |
343 | } |
344 | |
345 | } break; |
346 | default: break; |
347 | } |
348 | //---------------------------------------------------------------------------------- |
349 | |
350 | // Draw |
351 | //---------------------------------------------------------------------------------- |
352 | BeginDrawing(); |
353 | |
354 | ClearBackground(RAYWHITE); |
355 | |
356 | // Draw background (common to all screens) |
357 | DrawTexture(sky, 0, 0, WHITE); |
358 | |
359 | DrawTexture(mountains, backScrolling, 0, WHITE); |
360 | DrawTexture(mountains, screenWidth + backScrolling, 0, WHITE); |
361 | |
362 | if (!gameraMode) |
363 | { |
364 | DrawTexture(sea, seaScrolling, 0, (Color){ 16, 189, blue, 255}); |
365 | DrawTexture(sea, screenWidth + seaScrolling, 0, (Color){ 16, 189, blue, 255}); |
366 | } |
367 | else |
368 | { |
369 | DrawTexture(sea, seaScrolling, 0, (Color){ 255, 113, 66, 255}); |
370 | DrawTexture(sea, screenWidth + seaScrolling, 0, (Color){ 255, 113, 66, 255}); |
371 | } |
372 | |
373 | switch (currentScreen) |
374 | { |
375 | case TITLE: |
376 | { |
377 | // Draw title |
378 | DrawTexture(title, screenWidth/2 - title.width/2, screenHeight/2 - title.height/2 - 80, WHITE); |
379 | |
380 | // Draw blinking text |
381 | if ((framesCounter/30) % 2) DrawTextEx(font, "PRESS ENTER" , (Vector2){ screenWidth/2 - 150, 480 }, font.baseSize, 0, WHITE); |
382 | |
383 | } break; |
384 | case GAMEPLAY: |
385 | { |
386 | // Draw water lines |
387 | for (int i = 0; i < 5; i++) DrawRectangle(0, i*120 + 120, screenWidth, 110, Fade(SKYBLUE, 0.1f)); |
388 | |
389 | // Draw player |
390 | if (!gameraMode) DrawTexture(turtle, playerBounds.x - 14, playerBounds.y - 14, WHITE); |
391 | else DrawTexture(gamera, playerBounds.x - 64, playerBounds.y - 64, WHITE); |
392 | |
393 | // Draw player bounding box |
394 | //if (!gameraMode) DrawRectangleRec(playerBounds, Fade(GREEN, 0.4f)); |
395 | //else DrawRectangleRec(playerBounds, Fade(ORANGE, 0.4f)); |
396 | |
397 | // Draw enemies |
398 | for (int i = 0; i < MAX_ENEMIES; i++) |
399 | { |
400 | if (enemyActive[i]) |
401 | { |
402 | // Draw enemies |
403 | switch(enemyType[i]) |
404 | { |
405 | case 0: DrawTexture(shark, enemyBounds[i].x - 14, enemyBounds[i].y - 14, WHITE); break; |
406 | case 1: DrawTexture(orca, enemyBounds[i].x - 14, enemyBounds[i].y - 14, WHITE); break; |
407 | case 2: DrawTexture(swhale, enemyBounds[i].x - 14, enemyBounds[i].y - 14, WHITE); break; |
408 | case 3: DrawTexture(fish, enemyBounds[i].x - 14, enemyBounds[i].y - 14, WHITE); break; |
409 | default: break; |
410 | } |
411 | |
412 | // Draw enemies bounding boxes |
413 | /* |
414 | switch(enemyType[i]) |
415 | { |
416 | case 0: DrawRectangleRec(enemyBounds[i], Fade(RED, 0.5f)); break; |
417 | case 1: DrawRectangleRec(enemyBounds[i], Fade(RED, 0.5f)); break; |
418 | case 2: DrawRectangleRec(enemyBounds[i], Fade(RED, 0.5f)); break; |
419 | case 3: DrawRectangleRec(enemyBounds[i], Fade(GREEN, 0.5f)); break; |
420 | default: break; |
421 | } |
422 | */ |
423 | } |
424 | } |
425 | |
426 | // Draw gameplay interface |
427 | DrawRectangle(20, 20, 400, 40, Fade(GRAY, 0.4f)); |
428 | DrawRectangle(20, 20, foodBar, 40, ORANGE); |
429 | DrawRectangleLines(20, 20, 400, 40, BLACK); |
430 | |
431 | DrawTextEx(font, FormatText("SCORE: %04i" , score), (Vector2){ screenWidth - 300, 20 }, font.baseSize, -2, ORANGE); |
432 | DrawTextEx(font, FormatText("DISTANCE: %04i" , (int)distance), (Vector2){ 550, 20 }, font.baseSize, -2, ORANGE); |
433 | |
434 | if (gameraMode) |
435 | { |
436 | DrawText("GAMERA MODE" , 60, 22, 40, GRAY); |
437 | DrawTexture(gframe, 0, 0, Fade(WHITE, 0.5f)); |
438 | } |
439 | |
440 | } break; |
441 | case ENDING: |
442 | { |
443 | // Draw a transparent black rectangle that covers all screen |
444 | DrawRectangle(0, 0, screenWidth, screenHeight, Fade(BLACK, 0.4f)); |
445 | |
446 | DrawTextEx(font, "GAME OVER" , (Vector2){ 300, 160 }, font.baseSize*3, -2, MAROON); |
447 | |
448 | DrawTextEx(font, FormatText("SCORE: %04i" , score), (Vector2){ 680, 350 }, font.baseSize, -2, GOLD); |
449 | DrawTextEx(font, FormatText("DISTANCE: %04i" , (int)distance), (Vector2){ 290, 350 }, font.baseSize, -2, GOLD); |
450 | DrawTextEx(font, FormatText("HISCORE: %04i" , hiscore), (Vector2){ 665, 400 }, font.baseSize, -2, ORANGE); |
451 | DrawTextEx(font, FormatText("HIDISTANCE: %04i" , (int)hidistance), (Vector2){ 270, 400 }, font.baseSize, -2, ORANGE); |
452 | |
453 | // Draw blinking text |
454 | if ((framesCounter/30) % 2) DrawTextEx(font, "PRESS ENTER to REPLAY" , (Vector2){ screenWidth/2 - 250, 520 }, font.baseSize, -2, LIGHTGRAY); |
455 | |
456 | } break; |
457 | default: break; |
458 | } |
459 | |
460 | EndDrawing(); |
461 | //---------------------------------------------------------------------------------- |
462 | } |
463 | |
464 | // De-Initialization |
465 | //-------------------------------------------------------------------------------------- |
466 | |
467 | // Unload textures |
468 | UnloadTexture(sky); |
469 | UnloadTexture(mountains); |
470 | UnloadTexture(sea); |
471 | UnloadTexture(gframe); |
472 | UnloadTexture(title); |
473 | UnloadTexture(turtle); |
474 | UnloadTexture(shark); |
475 | UnloadTexture(orca); |
476 | UnloadTexture(swhale); |
477 | UnloadTexture(fish); |
478 | UnloadTexture(gamera); |
479 | |
480 | // Unload font texture |
481 | UnloadFont(font); |
482 | |
483 | // Unload sounds |
484 | UnloadSound(eat); |
485 | UnloadSound(die); |
486 | UnloadSound(growl); |
487 | |
488 | UnloadMusicStream(music); // Unload music |
489 | CloseAudioDevice(); // Close audio device |
490 | |
491 | CloseWindow(); // Close window and OpenGL context |
492 | //-------------------------------------------------------------------------------------- |
493 | |
494 | return 0; |
495 | } |
496 | |