2016-01-29 18:26:06 +03:00
/*******************************************************************************************
*
* raylib - sample game : gorilas
*
* Sample game Marc Palau 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"
# include <stdio.h>
# include <stdlib.h>
# include <time.h>
# include <math.h>
# if defined(PLATFORM_WEB)
# include <emscripten/emscripten.h>
# endif
//----------------------------------------------------------------------------------
// Some Defines
//----------------------------------------------------------------------------------
# define MAX_BUILDINGS 15
# define MAX_EXPLOSIONS 200
# define MAX_PLAYERS 2
# define BUILDING_RELATIVE_ERROR 30 // Building size random range %
# define BUILDING_MIN_RELATIVE_HEIGHT 20 // Minimum height in % of the screenHeight
# define BUILDING_MAX_RELATIVE_HEIGHT 60 // Maximum height in % of the screenHeight
# define BUILDING_MIN_GRAYSCALE_COLOR 120 // Minimum gray color for the buildings
# define BUILDING_MAX_GRAYSCALE_COLOR 200 // Maximum gray color for the buildings
# define MIN_PLAYER_POSITION 5 // Minimum x position %
# define MAX_PLAYER_POSITION 20 // Maximum x position %
# define GRAVITY 9.81f
# define DELTA_FPS 60
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
typedef struct Player {
Vector2 position ;
Vector2 size ;
Vector2 aimingPoint ;
int aimingAngle ;
int aimingPower ;
Vector2 previousPoint ;
int previousAngle ;
int previousPower ;
Vector2 impactPoint ;
bool isLeftTeam ; // This player belongs to the left or to the right team
bool isPlayer ; // If is a player or an AI
bool isAlive ;
} Player ;
typedef struct Building {
Rectangle rectangle ;
Color color ;
} Building ;
typedef struct Explosion {
Vector2 position ;
int radius ;
bool active ;
} Explosion ;
typedef struct Ball {
Vector2 position ;
Vector2 speed ;
int radius ;
bool active ;
} Ball ;
//------------------------------------------------------------------------------------
// Global Variables Declaration
//------------------------------------------------------------------------------------
2019-05-21 18:47:06 +03:00
static const int screenWidth = 800 ;
static const int screenHeight = 450 ;
2016-01-29 18:26:06 +03:00
static bool gameOver = false ;
static bool pause = false ;
2019-05-21 18:47:06 +03:00
static Player player [ MAX_PLAYERS ] = { 0 } ;
static Building building [ MAX_BUILDINGS ] = { 0 } ;
static Explosion explosion [ MAX_EXPLOSIONS ] = { 0 } ;
static Ball ball = { 0 } ;
2016-01-29 18:26:06 +03:00
static int playerTurn = 0 ;
static bool ballOnAir = false ;
//------------------------------------------------------------------------------------
// 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)
// Additional module functions
static void InitBuildings ( void ) ;
static void InitPlayers ( void ) ;
static bool UpdatePlayer ( int playerTurn ) ;
static bool UpdateBall ( int playerTurn ) ;
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
2017-10-22 12:12:10 +03:00
int main ( void )
2016-01-29 18:26:06 +03:00
{
2018-03-16 23:31:10 +03:00
// Initialization (Note windowTitle is unused on Android)
2017-10-22 12:12:10 +03:00
//---------------------------------------------------------
2016-01-29 18:26:06 +03:00
InitWindow ( screenWidth , screenHeight , " sample game: gorilas " ) ;
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
{
2017-10-22 12:12:10 +03:00
// Update and Draw
2016-01-29 18:26:06 +03:00
//----------------------------------------------------------------------------------
2017-10-22 12:12:10 +03:00
UpdateDrawFrame ( ) ;
2016-01-29 18:26:06 +03:00
//----------------------------------------------------------------------------------
}
# endif
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadGame ( ) ; // Unload loaded data (textures, sounds, models...)
CloseWindow ( ) ; // Close window and OpenGL context
//--------------------------------------------------------------------------------------
2018-03-16 23:31:10 +03:00
2016-01-29 18:26:06 +03:00
return 0 ;
}
//------------------------------------------------------------------------------------
// Module Functions Definitions (local)
//------------------------------------------------------------------------------------
// Initialize game variables
void InitGame ( void )
{
// Init shoot
ball . radius = 10 ;
ballOnAir = false ;
ball . active = false ;
InitBuildings ( ) ;
InitPlayers ( ) ;
// Init explosions
for ( int i = 0 ; i < MAX_EXPLOSIONS ; i + + )
{
explosion [ i ] . position = ( Vector2 ) { 0.0f , 0.0f } ;
explosion [ i ] . radius = 30 ;
explosion [ i ] . active = false ;
}
}
// Update game (one frame)
void UpdateGame ( void )
{
if ( ! gameOver )
{
if ( IsKeyPressed ( ' P ' ) ) pause = ! pause ;
if ( ! pause )
{
if ( ! ballOnAir ) ballOnAir = UpdatePlayer ( playerTurn ) ; // If we are aiming
else
{
if ( UpdateBall ( playerTurn ) ) // If collision
{
// Game over logic
bool leftTeamAlive = false ;
bool rightTeamAlive = false ;
for ( int i = 0 ; i < MAX_PLAYERS ; i + + )
{
if ( player [ i ] . isAlive )
{
if ( player [ i ] . isLeftTeam ) leftTeamAlive = true ;
if ( ! player [ i ] . isLeftTeam ) rightTeamAlive = true ;
}
}
if ( leftTeamAlive & & rightTeamAlive )
{
ballOnAir = false ;
ball . active = false ;
playerTurn + + ;
if ( playerTurn = = MAX_PLAYERS ) playerTurn = 0 ;
}
else
{
gameOver = true ;
// if (leftTeamAlive) left team wins
// if (rightTeamAlive) right team wins
}
}
}
}
}
else
{
if ( IsKeyPressed ( KEY_ENTER ) )
{
InitGame ( ) ;
gameOver = false ;
}
}
}
// Draw game (one frame)
void DrawGame ( void )
{
BeginDrawing ( ) ;
ClearBackground ( RAYWHITE ) ;
if ( ! gameOver )
{
// Draw buildings
for ( int i = 0 ; i < MAX_BUILDINGS ; i + + ) DrawRectangleRec ( building [ i ] . rectangle , building [ i ] . color ) ;
// Draw explosions
for ( int i = 0 ; i < MAX_EXPLOSIONS ; i + + )
{
if ( explosion [ i ] . active ) DrawCircle ( explosion [ i ] . position . x , explosion [ i ] . position . y , explosion [ i ] . radius , RAYWHITE ) ;
}
// Draw players
for ( int i = 0 ; i < MAX_PLAYERS ; i + + )
{
if ( player [ i ] . isAlive )
{
if ( player [ i ] . isLeftTeam ) DrawRectangle ( player [ i ] . position . x - player [ i ] . size . x / 2 , player [ i ] . position . y - player [ i ] . size . y / 2 ,
player [ i ] . size . x , player [ i ] . size . y , BLUE ) ;
else DrawRectangle ( player [ i ] . position . x - player [ i ] . size . x / 2 , player [ i ] . position . y - player [ i ] . size . y / 2 ,
player [ i ] . size . x , player [ i ] . size . y , RED ) ;
}
}
// Draw ball
if ( ball . active ) DrawCircle ( ball . position . x , ball . position . y , ball . radius , MAROON ) ;
// Draw the angle and the power of the aim, and the previous ones
if ( ! ballOnAir )
{
// Draw shot information
/*
if ( player [ playerTurn ] . isLeftTeam )
{
2019-05-21 18:47:06 +03:00
DrawText ( TextFormat ( " Previous Point %i, %i " , ( int ) player [ playerTurn ] . previousPoint . x , ( int ) player [ playerTurn ] . previousPoint . y ) , 20 , 20 , 20 , DARKBLUE ) ;
DrawText ( TextFormat ( " Previous Angle %i " , player [ playerTurn ] . previousAngle ) , 20 , 50 , 20 , DARKBLUE ) ;
DrawText ( TextFormat ( " Previous Power %i " , player [ playerTurn ] . previousPower ) , 20 , 80 , 20 , DARKBLUE ) ;
DrawText ( TextFormat ( " Aiming Point %i, %i " , ( int ) player [ playerTurn ] . aimingPoint . x , ( int ) player [ playerTurn ] . aimingPoint . y ) , 20 , 110 , 20 , DARKBLUE ) ;
DrawText ( TextFormat ( " Aiming Angle %i " , player [ playerTurn ] . aimingAngle ) , 20 , 140 , 20 , DARKBLUE ) ;
DrawText ( TextFormat ( " Aiming Power %i " , player [ playerTurn ] . aimingPower ) , 20 , 170 , 20 , DARKBLUE ) ;
2016-01-29 18:26:06 +03:00
}
else
{
2019-05-21 18:47:06 +03:00
DrawText ( TextFormat ( " Previous Point %i, %i " , ( int ) player [ playerTurn ] . previousPoint . x , ( int ) player [ playerTurn ] . previousPoint . y ) , screenWidth * 3 / 4 , 20 , 20 , DARKBLUE ) ;
DrawText ( TextFormat ( " Previous Angle %i " , player [ playerTurn ] . previousAngle ) , screenWidth * 3 / 4 , 50 , 20 , DARKBLUE ) ;
DrawText ( TextFormat ( " Previous Power %i " , player [ playerTurn ] . previousPower ) , screenWidth * 3 / 4 , 80 , 20 , DARKBLUE ) ;
DrawText ( TextFormat ( " Aiming Point %i, %i " , ( int ) player [ playerTurn ] . aimingPoint . x , ( int ) player [ playerTurn ] . aimingPoint . y ) , screenWidth * 3 / 4 , 110 , 20 , DARKBLUE ) ;
DrawText ( TextFormat ( " Aiming Angle %i " , player [ playerTurn ] . aimingAngle ) , screenWidth * 3 / 4 , 140 , 20 , DARKBLUE ) ;
DrawText ( TextFormat ( " Aiming Power %i " , player [ playerTurn ] . aimingPower ) , screenWidth * 3 / 4 , 170 , 20 , DARKBLUE ) ;
2016-01-29 18:26:06 +03:00
}
*/
// Draw aim
if ( player [ playerTurn ] . isLeftTeam )
{
// Previous aiming
DrawTriangle ( ( Vector2 ) { player [ playerTurn ] . position . x - player [ playerTurn ] . size . x / 4 , player [ playerTurn ] . position . y - player [ playerTurn ] . size . y / 4 } ,
( Vector2 ) { player [ playerTurn ] . position . x + player [ playerTurn ] . size . x / 4 , player [ playerTurn ] . position . y + player [ playerTurn ] . size . y / 4 } ,
player [ playerTurn ] . previousPoint , GRAY ) ;
// Actual aiming
DrawTriangle ( ( Vector2 ) { player [ playerTurn ] . position . x - player [ playerTurn ] . size . x / 4 , player [ playerTurn ] . position . y - player [ playerTurn ] . size . y / 4 } ,
( Vector2 ) { player [ playerTurn ] . position . x + player [ playerTurn ] . size . x / 4 , player [ playerTurn ] . position . y + player [ playerTurn ] . size . y / 4 } ,
player [ playerTurn ] . aimingPoint , DARKBLUE ) ;
}
else
{
// Previous aiming
DrawTriangle ( ( Vector2 ) { player [ playerTurn ] . position . x - player [ playerTurn ] . size . x / 4 , player [ playerTurn ] . position . y + player [ playerTurn ] . size . y / 4 } ,
( Vector2 ) { player [ playerTurn ] . position . x + player [ playerTurn ] . size . x / 4 , player [ playerTurn ] . position . y - player [ playerTurn ] . size . y / 4 } ,
player [ playerTurn ] . previousPoint , GRAY ) ;
// Actual aiming
DrawTriangle ( ( Vector2 ) { player [ playerTurn ] . position . x - player [ playerTurn ] . size . x / 4 , player [ playerTurn ] . position . y + player [ playerTurn ] . size . y / 4 } ,
( Vector2 ) { player [ playerTurn ] . position . x + player [ playerTurn ] . size . x / 4 , player [ playerTurn ] . position . y - player [ playerTurn ] . size . y / 4 } ,
player [ playerTurn ] . aimingPoint , MAROON ) ;
}
}
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 ( ) ;
}
//--------------------------------------------------------------------------------------
// Additional module functions
//--------------------------------------------------------------------------------------
static void InitBuildings ( void )
{
// Horizontal generation
int currentWidth = 0 ;
// We make sure the absolute error randomly generated for each building, has as a minimum value the screenWidth.
// This way all the screen will be filled with buildings. Each building will have a different, random width.
float relativeWidth = 100 / ( 100 - BUILDING_RELATIVE_ERROR ) ;
float buildingWidthMean = ( screenWidth * relativeWidth / MAX_BUILDINGS ) + 1 ; // We add one to make sure we will cover the whole screen.
// Vertical generation
int currentHeighth = 0 ;
int grayLevel ;
// Creation
for ( int i = 0 ; i < MAX_BUILDINGS ; i + + )
{
// Horizontal
building [ i ] . rectangle . x = currentWidth ;
building [ i ] . rectangle . width = GetRandomValue ( buildingWidthMean * ( 100 - BUILDING_RELATIVE_ERROR / 2 ) / 100 + 1 , buildingWidthMean * ( 100 + BUILDING_RELATIVE_ERROR ) / 100 ) ;
currentWidth + = building [ i ] . rectangle . width ;
// Vertical
currentHeighth = GetRandomValue ( BUILDING_MIN_RELATIVE_HEIGHT , BUILDING_MAX_RELATIVE_HEIGHT ) ;
building [ i ] . rectangle . y = screenHeight - ( screenHeight * currentHeighth / 100 ) ;
building [ i ] . rectangle . height = screenHeight * currentHeighth / 100 + 1 ;
// Color
grayLevel = GetRandomValue ( BUILDING_MIN_GRAYSCALE_COLOR , BUILDING_MAX_GRAYSCALE_COLOR ) ;
building [ i ] . color = ( Color ) { grayLevel , grayLevel , grayLevel , 255 } ;
}
}
static void InitPlayers ( void )
{
for ( int i = 0 ; i < MAX_PLAYERS ; i + + )
{
player [ i ] . isAlive = true ;
// Decide the team of this player
if ( i % 2 = = 0 ) player [ i ] . isLeftTeam = true ;
else player [ i ] . isLeftTeam = false ;
// Now there is no AI
player [ i ] . isPlayer = true ;
// Set size, by default by now
player [ i ] . size = ( Vector2 ) { 40 , 40 } ;
// Set position
if ( player [ i ] . isLeftTeam ) player [ i ] . position . x = GetRandomValue ( screenWidth * MIN_PLAYER_POSITION / 100 , screenWidth * MAX_PLAYER_POSITION / 100 ) ;
else player [ i ] . position . x = screenWidth - GetRandomValue ( screenWidth * MIN_PLAYER_POSITION / 100 , screenWidth * MAX_PLAYER_POSITION / 100 ) ;
for ( int j = 0 ; j < MAX_BUILDINGS ; j + + )
{
if ( building [ j ] . rectangle . x > player [ i ] . position . x )
{
// Set the player in the center of the building
player [ i ] . position . x = building [ j - 1 ] . rectangle . x + building [ j - 1 ] . rectangle . width / 2 ;
// Set the player at the top of the building
player [ i ] . position . y = building [ j - 1 ] . rectangle . y - player [ i ] . size . y / 2 ;
break ;
}
}
// Set statistics to 0
player [ i ] . aimingPoint = player [ i ] . position ;
player [ i ] . previousAngle = 0 ;
player [ i ] . previousPower = 0 ;
player [ i ] . previousPoint = player [ i ] . position ;
player [ i ] . aimingAngle = 0 ;
player [ i ] . aimingPower = 0 ;
player [ i ] . impactPoint = ( Vector2 ) { - 100 , - 100 } ;
}
}
static bool UpdatePlayer ( int playerTurn )
{
// If we are aiming at the firing quadrant, we calculate the angle
if ( GetMousePosition ( ) . y < = player [ playerTurn ] . position . y )
{
// Left team
if ( player [ playerTurn ] . isLeftTeam & & GetMousePosition ( ) . x > = player [ playerTurn ] . position . x )
{
// Distance (calculating the fire power)
player [ playerTurn ] . aimingPower = sqrt ( pow ( player [ playerTurn ] . position . x - GetMousePosition ( ) . x , 2 ) + pow ( player [ playerTurn ] . position . y - GetMousePosition ( ) . y , 2 ) ) ;
// Calculates the angle via arcsin
player [ playerTurn ] . aimingAngle = asin ( ( player [ playerTurn ] . position . y - GetMousePosition ( ) . y ) / player [ playerTurn ] . aimingPower ) * RAD2DEG ;
// Point of the screen we are aiming at
player [ playerTurn ] . aimingPoint = GetMousePosition ( ) ;
// Ball fired
if ( IsMouseButtonPressed ( MOUSE_LEFT_BUTTON ) )
{
player [ playerTurn ] . previousPoint = player [ playerTurn ] . aimingPoint ;
player [ playerTurn ] . previousPower = player [ playerTurn ] . aimingPower ;
player [ playerTurn ] . previousAngle = player [ playerTurn ] . aimingAngle ;
ball . position = player [ playerTurn ] . position ;
return true ;
}
}
// Right team
else if ( ! player [ playerTurn ] . isLeftTeam & & GetMousePosition ( ) . x < = player [ playerTurn ] . position . x )
{
// Distance (calculating the fire power)
player [ playerTurn ] . aimingPower = sqrt ( pow ( player [ playerTurn ] . position . x - GetMousePosition ( ) . x , 2 ) + pow ( player [ playerTurn ] . position . y - GetMousePosition ( ) . y , 2 ) ) ;
// Calculates the angle via arcsin
player [ playerTurn ] . aimingAngle = asin ( ( player [ playerTurn ] . position . y - GetMousePosition ( ) . y ) / player [ playerTurn ] . aimingPower ) * RAD2DEG ;
// Point of the screen we are aiming at
player [ playerTurn ] . aimingPoint = GetMousePosition ( ) ;
// Ball fired
if ( IsMouseButtonPressed ( MOUSE_LEFT_BUTTON ) )
{
player [ playerTurn ] . previousPoint = player [ playerTurn ] . aimingPoint ;
player [ playerTurn ] . previousPower = player [ playerTurn ] . aimingPower ;
player [ playerTurn ] . previousAngle = player [ playerTurn ] . aimingAngle ;
ball . position = player [ playerTurn ] . position ;
return true ;
}
}
else
{
player [ playerTurn ] . aimingPoint = player [ playerTurn ] . position ;
player [ playerTurn ] . aimingPower = 0 ;
player [ playerTurn ] . aimingAngle = 0 ;
}
}
else
{
player [ playerTurn ] . aimingPoint = player [ playerTurn ] . position ;
player [ playerTurn ] . aimingPower = 0 ;
player [ playerTurn ] . aimingAngle = 0 ;
}
return false ;
}
static bool UpdateBall ( int playerTurn )
{
static int explosionNumber = 0 ;
// Activate ball
if ( ! ball . active )
{
if ( player [ playerTurn ] . isLeftTeam )
{
ball . speed . x = cos ( player [ playerTurn ] . previousAngle * DEG2RAD ) * player [ playerTurn ] . previousPower * 3 / DELTA_FPS ;
ball . speed . y = - sin ( player [ playerTurn ] . previousAngle * DEG2RAD ) * player [ playerTurn ] . previousPower * 3 / DELTA_FPS ;
ball . active = true ;
}
else
{
ball . speed . x = - cos ( player [ playerTurn ] . previousAngle * DEG2RAD ) * player [ playerTurn ] . previousPower * 3 / DELTA_FPS ;
ball . speed . y = - sin ( player [ playerTurn ] . previousAngle * DEG2RAD ) * player [ playerTurn ] . previousPower * 3 / DELTA_FPS ;
ball . active = true ;
}
}
ball . position . x + = ball . speed . x ;
ball . position . y + = ball . speed . y ;
ball . speed . y + = GRAVITY / DELTA_FPS ;
// Collision
if ( ball . position . x + ball . radius < 0 ) return true ;
else if ( ball . position . x - ball . radius > screenWidth ) return true ;
else
{
// Player collision
for ( int i = 0 ; i < MAX_PLAYERS ; i + + )
{
if ( CheckCollisionCircleRec ( ball . position , ball . radius , ( Rectangle ) { player [ i ] . position . x - player [ i ] . size . x / 2 , player [ i ] . position . y - player [ i ] . size . y / 2 ,
player [ i ] . size . x , player [ i ] . size . y } ) )
{
// We can't hit ourselves
if ( i = = playerTurn ) return false ;
else
{
// We set the impact point
player [ playerTurn ] . impactPoint . x = ball . position . x ;
player [ playerTurn ] . impactPoint . y = ball . position . y + ball . radius ;
// We destroy the player
player [ i ] . isAlive = false ;
return true ;
}
}
}
// Building collision
// NOTE: We only check building collision if we are not inside an explosion
for ( int i = 0 ; i < MAX_BUILDINGS ; i + + )
{
if ( CheckCollisionCircles ( ball . position , ball . radius , explosion [ i ] . position , explosion [ i ] . radius - ball . radius ) )
{
return false ;
}
}
for ( int i = 0 ; i < MAX_BUILDINGS ; i + + )
{
if ( CheckCollisionCircleRec ( ball . position , ball . radius , building [ i ] . rectangle ) )
{
// We set the impact point
player [ playerTurn ] . impactPoint . x = ball . position . x ;
player [ playerTurn ] . impactPoint . y = ball . position . y + ball . radius ;
// We create an explosion
explosion [ explosionNumber ] . position = player [ playerTurn ] . impactPoint ;
explosion [ explosionNumber ] . active = true ;
explosionNumber + + ;
return true ;
}
}
}
return false ;
2018-03-16 23:31:10 +03:00
}