raylib/games/space_invaders.c
Ahmad Fatoum 2c219fb814
Allow use of main instead of android_main
Inspired by #504.
Instead of requiring the user to do PLATFORM_ANDROID #ifdefery,
have the android_main entry point exported by raylib and call
the user-defined main. This way many games could (in theory)
run unmodified on Android and elsewhere.

This is untested!
2018-03-16 21:37:22 +01:00

401 lines
13 KiB
C

/*******************************************************************************************
*
* raylib - sample game: space invaders
*
* Sample game developed by Ian Eito, Albert Martos and Ramon Santamaria
*
* This game has been created using raylib v1.3 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
#include "raylib.h"
#if defined(PLATFORM_WEB)
#include <emscripten/emscripten.h>
#endif
//----------------------------------------------------------------------------------
// Some Defines
//----------------------------------------------------------------------------------
#define NUM_SHOOTS 50
#define NUM_MAX_ENEMIES 50
#define FIRST_WAVE 10
#define SECOND_WAVE 20
#define THIRD_WAVE 50
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
typedef enum { FIRST = 0, SECOND, THIRD } EnemyWave;
typedef struct Player{
Rectangle rec;
Vector2 speed;
Color color;
} Player;
typedef struct Enemy{
Rectangle rec;
Vector2 speed;
bool active;
Color color;
} Enemy;
typedef struct Shoot{
Rectangle rec;
Vector2 speed;
bool active;
Color color;
} Shoot;
//------------------------------------------------------------------------------------
// Global Variables Declaration
//------------------------------------------------------------------------------------
static int screenWidth = 800;
static int screenHeight = 450;
static bool gameOver;
static bool pause;
static int score;
static bool victory;
static Player player;
static Enemy enemy[NUM_MAX_ENEMIES];
static Shoot shoot[NUM_SHOOTS];
static EnemyWave wave;
static int shootRate;
static float alpha;
static int activeEnemies;
static int enemiesKill;
static bool smooth;
//------------------------------------------------------------------------------------
// Module Functions Declaration (local)
//------------------------------------------------------------------------------------
static void InitGame(void); // Initialize game
static void UpdateGame(void); // Update game (one frame)
static void DrawGame(void); // Draw game (one frame)
static void UnloadGame(void); // Unload game
static void UpdateDrawFrame(void); // Update and Draw (one frame)
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization (Note windowTitle is unused on Android)
//---------------------------------------------------------
InitWindow(screenWidth, screenHeight, "sample game: space invaders");
InitGame();
#if defined(PLATFORM_WEB)
emscripten_set_main_loop(UpdateDrawFrame, 0, 1);
#else
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update and Draw
//----------------------------------------------------------------------------------
UpdateDrawFrame();
//----------------------------------------------------------------------------------
}
#endif
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadGame(); // Unload loaded data (textures, sounds, models...)
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
//------------------------------------------------------------------------------------
// Module Functions Definitions (local)
//------------------------------------------------------------------------------------
// Initialize game variables
void InitGame(void)
{
// Initialize game variables
shootRate = 0;
pause = false;
gameOver = false;
victory = false;
smooth = false;
wave = FIRST;
activeEnemies = FIRST_WAVE;
enemiesKill = 0;
score = 0;
alpha = 0;
// Initialize player
player.rec.x = 20;
player.rec.y = 50;
player.rec.width = 20;
player.rec.height = 20;
player.speed.x = 5;
player.speed.y = 5;
player.color = BLACK;
// Initialize enemies
for (int i = 0; i < NUM_MAX_ENEMIES; i++)
{
enemy[i].rec.width = 10;
enemy[i].rec.height = 10;
enemy[i].rec.x = GetRandomValue(screenWidth, screenWidth + 1000);
enemy[i].rec.y = GetRandomValue(0, screenHeight - enemy[i].rec.height);
enemy[i].speed.x = 5;
enemy[i].speed.y = 5;
enemy[i].active = true;
enemy[i].color = GRAY;
}
// Initialize shoots
for (int i = 0; i < NUM_SHOOTS; i++)
{
shoot[i].rec.x = player.rec.x;
shoot[i].rec.y = player.rec.y + player.rec.height/4;
shoot[i].rec.width = 10;
shoot[i].rec.height = 5;
shoot[i].speed.x = 7;
shoot[i].speed.y = 0;
shoot[i].active = false;
shoot[i].color = MAROON;
}
}
// Update game (one frame)
void UpdateGame(void)
{
if (!gameOver)
{
if (IsKeyPressed('P')) pause = !pause;
if (!pause)
{
switch (wave)
{
case FIRST:
{
if (!smooth)
{
alpha += 0.02f;
if (alpha >= 1.0f) smooth = true;
}
if (smooth) alpha -= 0.02f;
if (enemiesKill == activeEnemies)
{
enemiesKill = 0;
for (int i = 0; i < activeEnemies; i++)
{
if (!enemy[i].active) enemy[i].active = true;
}
activeEnemies = SECOND_WAVE;
wave = SECOND;
smooth = false;
alpha = 0.0f;
}
} break;
case SECOND:
{
if (!smooth)
{
alpha += 0.02f;
if (alpha >= 1.0f) smooth = true;
}
if (smooth) alpha -= 0.02f;
if (enemiesKill == activeEnemies)
{
enemiesKill = 0;
for (int i = 0; i < activeEnemies; i++)
{
if (!enemy[i].active) enemy[i].active = true;
}
activeEnemies = THIRD_WAVE;
wave = THIRD;
smooth = false;
alpha = 0.0f;
}
} break;
case THIRD:
{
if (!smooth)
{
alpha += 0.02f;
if (alpha >= 1.0f) smooth = true;
}
if (smooth) alpha -= 0.02f;
if (enemiesKill == activeEnemies) victory = true;
} break;
default: break;
}
// Player movement
if (IsKeyDown(KEY_RIGHT)) player.rec.x += player.speed.x;
if (IsKeyDown(KEY_LEFT)) player.rec.x -= player.speed.x;
if (IsKeyDown(KEY_UP)) player.rec.y -= player.speed.y;
if (IsKeyDown(KEY_DOWN)) player.rec.y += player.speed.y;
// Player collision with enemy
for (int i = 0; i < activeEnemies; i++)
{
if (CheckCollisionRecs(player.rec, enemy[i].rec)) gameOver = true;
}
// Enemy behaviour
for (int i = 0; i < activeEnemies; i++)
{
if (enemy[i].active)
{
enemy[i].rec.x -= enemy[i].speed.x;
if (enemy[i].rec.x < 0)
{
enemy[i].rec.x = GetRandomValue(screenWidth, screenWidth + 1000);
enemy[i].rec.y = GetRandomValue(0, screenHeight - enemy[i].rec.height);
}
}
}
// Wall behaviour
if (player.rec.x <= 0) player.rec.x = 0;
if (player.rec.x + player.rec.width >= screenWidth) player.rec.x = screenWidth - player.rec.width;
if (player.rec.y <= 0) player.rec.y = 0;
if (player.rec.y + player.rec.height >= screenHeight) player.rec.y = screenHeight - player.rec.height;
//Shoot initialization
if (IsKeyDown(KEY_SPACE))
{
shootRate += 5;
for (int i = 0; i < NUM_SHOOTS; i++)
{
if (!shoot[i].active && shootRate%20 == 0)
{
shoot[i].rec.x = player.rec.x;
shoot[i].rec.y = player.rec.y + player.rec.height/4;
shoot[i].active = true;
break;
}
}
}
// Shoot logic
for (int i = 0; i < NUM_SHOOTS; i++)
{
if (shoot[i].active)
{
// Movement
shoot[i].rec.x += shoot[i].speed.x;
// Collision with enemy
for (int j = 0; j < activeEnemies; j++)
{
if (enemy[j].active)
{
if (CheckCollisionRecs(shoot[i].rec, enemy[j].rec))
{
shoot[i].active = false;
enemy[j].rec.x = GetRandomValue(screenWidth, screenWidth + 1000);
enemy[j].rec.y = GetRandomValue(0, screenHeight - enemy[j].rec.height);
shootRate = 0;
enemiesKill++;
score += 100;
}
if (shoot[i].rec.x + shoot[i].rec.width >= screenWidth)
{
shoot[i].active = false;
shootRate = 0;
}
}
}
}
}
}
}
else
{
if (IsKeyPressed(KEY_ENTER))
{
InitGame();
gameOver = false;
}
}
}
// Draw game (one frame)
void DrawGame(void)
{
BeginDrawing();
ClearBackground(RAYWHITE);
if (!gameOver)
{
DrawRectangleRec(player.rec, player.color);
if (wave == FIRST) DrawText("FIRST WAVE", screenWidth/2 - MeasureText("FIRST WAVE", 40)/2, screenHeight/2 - 40, 40, Fade(BLACK, alpha));
else if (wave == SECOND) DrawText("SECOND WAVE", screenWidth/2 - MeasureText("SECOND WAVE", 40)/2, screenHeight/2 - 40, 40, Fade(BLACK, alpha));
else if (wave == THIRD) DrawText("THIRD WAVE", screenWidth/2 - MeasureText("THIRD WAVE", 40)/2, screenHeight/2 - 40, 40, Fade(BLACK, alpha));
for (int i = 0; i < activeEnemies; i++)
{
if (enemy[i].active) DrawRectangleRec(enemy[i].rec, enemy[i].color);
}
for (int i = 0; i < NUM_SHOOTS; i++)
{
if (shoot[i].active) DrawRectangleRec(shoot[i].rec, shoot[i].color);
}
DrawText(FormatText("%04i", score), 20, 20, 40, GRAY);
if (victory) DrawText("YOU WIN", screenWidth/2 - MeasureText("YOU WIN", 40)/2, screenHeight/2 - 40, 40, BLACK);
if (pause) DrawText("GAME PAUSED", screenWidth/2 - MeasureText("GAME PAUSED", 40)/2, screenHeight/2 - 40, 40, GRAY);
}
else DrawText("PRESS [ENTER] TO PLAY AGAIN", GetScreenWidth()/2 - MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, GetScreenHeight()/2 - 50, 20, GRAY);
EndDrawing();
}
// Unload game variables
void UnloadGame(void)
{
// TODO: Unload all dynamic loaded data (textures, sounds, models...)
}
// Update and Draw (one frame)
void UpdateDrawFrame(void)
{
UpdateGame();
DrawGame();
}