567 lines
13 KiB
C++
Executable File
567 lines
13 KiB
C++
Executable File
#include "main_code.h"
|
|
|
|
#ifdef TARGET_ANDROID
|
|
#include "android_api.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <time.h>
|
|
|
|
#include "include/Engine.h"
|
|
|
|
#include "main_code.h"
|
|
|
|
//Better move those to external config or something...
|
|
const cardinal CONST_LASER_TIMER = 200;
|
|
const int CONST_HIT_SCORE_POINTS = 100;
|
|
const float CONST_TIME_SCALE = 0.01f;
|
|
|
|
const float CONST_PLAYER_ACCELERATION = 3.f;
|
|
const float CONST_VELOCITY_FADE = 0.95f;
|
|
const float CONST_LASER_VELOCITY = 100.f;
|
|
|
|
const vec2 CONST_PLAYER_HALF_SIZE(30.f, 30.f);
|
|
const vec2 CONST_LASER_HALF_SIZE(10.f, 10.f);
|
|
|
|
|
|
const vec2 CONST_AST_POS1(40, 40);
|
|
const vec2 CONST_AST_POS2(40, 280);
|
|
const vec2 CONST_AST_POS3(440, 160);
|
|
|
|
|
|
|
|
float HealthToScale(int health)
|
|
{
|
|
return 0.5f + health * 0.3f;
|
|
}
|
|
|
|
float HealthToHitDistance(int health)
|
|
{
|
|
const float CONST_HIT_DISTANCE = 15.f;
|
|
|
|
return max(CONST_HIT_DISTANCE * HealthToScale(health), 10.f);
|
|
}
|
|
|
|
void TMyApplication::InnerInit()
|
|
{
|
|
|
|
*Console<<"Inner init go!\n";
|
|
|
|
#ifdef TARGET_ANDROID
|
|
ST::PathToResources = "";
|
|
#endif
|
|
#ifdef TARGET_WIN32
|
|
#ifdef NDEBUG
|
|
ST::PathToResources = "assets/";
|
|
#else
|
|
ST::PathToResources = "../../../assets/";
|
|
#endif
|
|
#endif
|
|
#ifdef TARGET_IOS
|
|
ST::PathToResources = "assets/";
|
|
#endif
|
|
|
|
RandomGenerator.seed(static_cast<unsigned int>(std::time(0)));
|
|
|
|
TapIsDown = false;
|
|
|
|
ResourceManager->TexList.AddTexture(CONST_CONSOLE_TEX_NAME);
|
|
|
|
ResourceManager->ShaderManager.AddShader("DefaultShader", "gui_transparent.vertex", "gui_transparent.fragment");
|
|
|
|
Renderer->PushShader("DefaultShader");
|
|
|
|
ResourceManager->FontManager.AddFont("droid_sans14", "droid_sans14_font_bitmap.bmp32", "droid_sans14_font_charmap.txt");
|
|
ResourceManager->FontManager.PushFont("droid_sans14");
|
|
|
|
ResourceManager->TexList.Serialize(*FileToPropertyTree("textures.xml"));
|
|
|
|
Renderer->PushProjectionMatrix(Renderer->GetScreenWidth(), Renderer->GetScreenHeight());
|
|
|
|
Level = 1;
|
|
Score = 0;
|
|
|
|
InitLevel();
|
|
|
|
Player.RenderPair.first.SamplerMap[CONST_STRING_TEXTURE_UNIFORM] = "shipTexture";
|
|
Player.RenderPair.second.Data = MakeDataTriangleList(Player.Pos - CONST_PLAYER_HALF_SIZE, Player.Pos + CONST_PLAYER_HALF_SIZE);
|
|
Player.RenderPair.second.RefreshBuffer();
|
|
|
|
BackgroundRenderPair.first.SamplerMap[CONST_STRING_TEXTURE_UNIFORM] = "backgroundTexture";
|
|
BackgroundRenderPair.second.Data = MakeDataTriangleList(vec2(0,0), vec2(Renderer->GetScreenWidth(), Renderer->GetScreenHeight()));
|
|
BackgroundRenderPair.second.RefreshBuffer();
|
|
|
|
*Console<<"Inner init end!\n";
|
|
|
|
glDepthFunc(GL_LEQUAL);
|
|
|
|
}
|
|
|
|
void TMyApplication::InnerDeinit()
|
|
{
|
|
}
|
|
|
|
|
|
void TMyApplication::InnerDraw()
|
|
{
|
|
|
|
glClearColor(0,0,0,1);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
//Draw background
|
|
TRenderParamsSetter renderParamSetter(BackgroundRenderPair.first);
|
|
|
|
Renderer->DrawTriangleList(BackgroundRenderPair.second);
|
|
|
|
|
|
//Draw asteroids
|
|
BOOST_FOREACH(CAsteroidStruct& asteroid, Asteroids)
|
|
{
|
|
TRenderParamsSetter renderParamSetter(asteroid.RenderPair.first);
|
|
|
|
Renderer->DrawTriangleList(asteroid.RenderPair.second);
|
|
}
|
|
|
|
|
|
//Draw player
|
|
if (GameState != GS_LOST)
|
|
{
|
|
TRenderParamsSetter renderParamSetter(Player.RenderPair.first);
|
|
Renderer->DrawTriangleList(Player.RenderPair.second);
|
|
}
|
|
|
|
//Draw lasers
|
|
BOOST_FOREACH(CLaserStruct& laser, Lasers)
|
|
{
|
|
TRenderParamsSetter renderParamSetter(laser.RenderPair.first);
|
|
|
|
Renderer->DrawTriangleList(laser.RenderPair.second);
|
|
}
|
|
|
|
|
|
//Draw texts
|
|
glBindTexture(GL_TEXTURE_2D, ResourceManager->TexList[ResourceManager->FontManager.GetCurrentFontTextureName()]);
|
|
|
|
Renderer->DrawTriangleList(LevelScoreText);
|
|
Renderer->DrawTriangleList(LevelMessageText);
|
|
|
|
}
|
|
|
|
|
|
void TMyApplication::InnerUpdate(cardinal dt)
|
|
{
|
|
float scaledTime = CONST_TIME_SCALE * dt;
|
|
|
|
//Update Asteroids
|
|
BOOST_FOREACH(CAsteroidStruct& asteroid, Asteroids)
|
|
{
|
|
|
|
float difAngle = asteroid.AngularVelocity * scaledTime;
|
|
|
|
asteroid.Angle += difAngle;
|
|
|
|
mat3 rotationMatrix = CreateZRotationMatrix(difAngle);
|
|
|
|
MoveDataTriangleList(asteroid.RenderPair.second.Data, -vec3(asteroid.CenterPos, 0));
|
|
RotateDataTriangleList(asteroid.RenderPair.second.Data, rotationMatrix);
|
|
MoveDataTriangleList(asteroid.RenderPair.second.Data, vec3(asteroid.CenterPos, 0));
|
|
|
|
|
|
vec2 dif = asteroid.Velocity * scaledTime;
|
|
|
|
vec2 newCenterPos = NormalizePos(asteroid.CenterPos + dif);
|
|
|
|
dif = newCenterPos - asteroid.CenterPos;
|
|
|
|
asteroid.CenterPos = newCenterPos;
|
|
|
|
MoveDataTriangleList(asteroid.RenderPair.second.Data, vec3(dif, 0));
|
|
|
|
asteroid.RenderPair.second.RefreshBuffer();
|
|
}
|
|
|
|
|
|
|
|
UpdatePlayerPosition(scaledTime);
|
|
|
|
|
|
|
|
//Shoot laser
|
|
LaserTimer.Update(dt);
|
|
|
|
if (LaserTimer.IsOver())
|
|
{
|
|
LaserTimer.SetTimer(CONST_LASER_TIMER);
|
|
|
|
if (GameState == GS_PLAY)
|
|
{
|
|
AddNewLaser();
|
|
}
|
|
}
|
|
|
|
//Update lasers
|
|
BOOST_FOREACH(CLaserStruct& laser, Lasers)
|
|
{
|
|
vec2 dif = CONST_LASER_VELOCITY *laser.Dir * scaledTime;
|
|
MoveDataTriangleList(laser.RenderPair.second.Data, vec3(dif, 0));
|
|
laser.Pos += dif;
|
|
|
|
laser.RenderPair.second.RefreshBuffer();
|
|
}
|
|
|
|
//Do all checks...
|
|
ProcessLaserHit();
|
|
|
|
ProcessPlayerCollisions();
|
|
|
|
ClearUnusedLasers();
|
|
|
|
if (Asteroids.size() == 0 && GameState == GS_PLAY)
|
|
{
|
|
GameState = GS_WON;
|
|
RefreshScoreText();
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
void TMyApplication::InnerOnTapDown(vec2 p)
|
|
{
|
|
TapIsDown = true;
|
|
LastTap = p;
|
|
}
|
|
|
|
void TMyApplication::InnerOnTapUp(vec2 p)
|
|
{
|
|
TapIsDown = false;
|
|
|
|
|
|
if (GameState == GS_LOST)
|
|
{
|
|
Level = 1;
|
|
Score = 0;
|
|
InitLevel();
|
|
}
|
|
else if (GameState == GS_WON)
|
|
{
|
|
Level++;
|
|
InitLevel();
|
|
}
|
|
}
|
|
|
|
void TMyApplication::InnerOnTapUpAfterMove(vec2 p)
|
|
{
|
|
TapIsDown = false;
|
|
}
|
|
|
|
void TMyApplication::InnerOnMove(vec2 shift)
|
|
{
|
|
LastTap -= shift;
|
|
}
|
|
|
|
void TMyApplication::InitLevel()
|
|
{
|
|
GameState = GS_PLAY;
|
|
|
|
LaserTimer.SetTimer(CONST_LASER_TIMER);
|
|
|
|
Asteroids.clear();
|
|
Lasers.clear();
|
|
|
|
CreateRandomAsteroid(CONST_AST_POS1, Level);
|
|
CreateRandomAsteroid(CONST_AST_POS2, Level);
|
|
CreateRandomAsteroid(CONST_AST_POS3, Level);
|
|
|
|
Player.Angle = 0;
|
|
Player.Pos = vec2(Renderer->GetScreenWidth()/2, Renderer->GetScreenHeight()/2);
|
|
|
|
RefreshScoreText();
|
|
|
|
}
|
|
|
|
CAsteroidStruct TMyApplication::CreateAsteroid(vec2 pos, vec2 dir, int health)
|
|
{
|
|
static const float CONST_TEX_COORD_SCALE = 0.01f;
|
|
static const float CONST_ANGULAR_VELOCITY_SCALE = 0.001f;
|
|
|
|
static const boost::random::uniform_int_distribution<> velocityDistribution(5, 10);
|
|
|
|
static const boost::random::uniform_int_distribution<> angularVelocityDistribution(0, 10);
|
|
|
|
static const boost::random::uniform_int_distribution<> vertexNumberDistribution(5, 10);
|
|
static const boost::random::uniform_int_distribution<> vertexShiftDistribution(20, 30);
|
|
|
|
CAsteroidStruct result;
|
|
|
|
result.Health = health;
|
|
|
|
result.CenterPos = pos;
|
|
|
|
result.Angle = 0;
|
|
|
|
result.AngularVelocity = angularVelocityDistribution(RandomGenerator) * CONST_ANGULAR_VELOCITY_SCALE;
|
|
|
|
result.Velocity = velocityDistribution(RandomGenerator) * dir;
|
|
|
|
int numberOfVertices = vertexNumberDistribution(RandomGenerator);
|
|
|
|
std::vector<float> distArr(numberOfVertices);
|
|
|
|
for (int i=0; i<numberOfVertices; i++)
|
|
{
|
|
distArr[i] = vertexShiftDistribution(RandomGenerator) * HealthToScale(health);
|
|
}
|
|
|
|
//First iteration
|
|
vec2 prevShift = distArr[0] * vec2(1,0);
|
|
|
|
vec2 prevTexCoordShift = prevShift * CONST_TEX_COORD_SCALE;
|
|
|
|
//Other iterations
|
|
for (int i = 1; i <= numberOfVertices; i++)
|
|
{
|
|
float angle = i * 2 * pi / static_cast<float>(numberOfVertices);
|
|
|
|
vec2 shift = distArr[i % numberOfVertices] * vec2(cosf(angle), sinf(angle));
|
|
|
|
vec2 texCoordShift = shift * CONST_TEX_COORD_SCALE;
|
|
|
|
result.RenderPair.second.Data.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].push_back(vec3(pos, 0));
|
|
result.RenderPair.second.Data.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].push_back(vec3(pos + prevShift, 0));
|
|
result.RenderPair.second.Data.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].push_back(vec3(pos + shift, 0));
|
|
|
|
result.RenderPair.second.Data.Vec2CoordArr[CONST_STRING_TEXCOORD_ATTRIB].push_back(vec2(0.5f, 0.5f));
|
|
result.RenderPair.second.Data.Vec2CoordArr[CONST_STRING_TEXCOORD_ATTRIB].push_back(vec2(0.5f, 0.5f) + prevTexCoordShift);
|
|
result.RenderPair.second.Data.Vec2CoordArr[CONST_STRING_TEXCOORD_ATTRIB].push_back(vec2(0.5f, 0.5f) + texCoordShift);
|
|
|
|
prevShift = shift;
|
|
prevTexCoordShift = texCoordShift;
|
|
}
|
|
|
|
result.RenderPair.first.SamplerMap[CONST_STRING_TEXTURE_UNIFORM] = "asteroidTexture";
|
|
|
|
result.RenderPair.second.RefreshBuffer();
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
void TMyApplication::CreateRandomAsteroid(vec2 pos, int health)
|
|
{
|
|
|
|
static const boost::random::uniform_int_distribution<> angleDistribution(0, 36);
|
|
|
|
float randomAngle = angleDistribution(RandomGenerator) * pi / 18.f;
|
|
|
|
Asteroids.push_back(CreateAsteroid(pos, vec2(cosf(randomAngle), sinf(randomAngle)), health));
|
|
}
|
|
|
|
void TMyApplication::CreateAsteroidParts(vec2 pos, int newHealth)
|
|
{
|
|
static const boost::random::uniform_int_distribution<> CreateAsteroidParts(2, 5);
|
|
|
|
static const boost::random::uniform_int_distribution<> angleDistribution(0, 36);
|
|
|
|
float randomAngle = angleDistribution(RandomGenerator) * pi / 18.f;
|
|
|
|
int number = CreateAsteroidParts(RandomGenerator);
|
|
|
|
for (int i = 0; i < number; i++)
|
|
{
|
|
float partAngle = randomAngle + i * 2 * pi / static_cast<float>(number);
|
|
Asteroids.push_back(CreateAsteroid(pos, vec2(cosf(partAngle), sinf(partAngle)), newHealth));
|
|
}
|
|
|
|
}
|
|
|
|
vec2 TMyApplication::NormalizePos(vec2 pos)
|
|
{
|
|
vec2 result = pos;
|
|
|
|
while (result.v[0] < 0)
|
|
{
|
|
result.v[0] += Renderer->GetScreenWidth();
|
|
}
|
|
|
|
while (result.v[1] < 0)
|
|
{
|
|
result.v[1] += Renderer->GetScreenHeight();
|
|
}
|
|
|
|
while (result.v[0] >= Renderer->GetScreenWidth())
|
|
{
|
|
result.v[0] -= Renderer->GetScreenWidth();
|
|
}
|
|
|
|
while (result.v[1] >= Renderer->GetScreenHeight())
|
|
{
|
|
result.v[1] -= Renderer->GetScreenHeight();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void TMyApplication::RefreshPlayerPos()
|
|
{
|
|
Replace6PointsInTriangleList(Player.RenderPair.second.Data, 0, (-1) * CONST_PLAYER_HALF_SIZE, CONST_PLAYER_HALF_SIZE);
|
|
|
|
mat3 rotationMatrix = CreateZRotationMatrix(Player.Angle);
|
|
|
|
RotateDataTriangleList(Player.RenderPair.second.Data, rotationMatrix);
|
|
|
|
MoveDataTriangleList(Player.RenderPair.second.Data, vec3(Player.Pos, 0));
|
|
|
|
Player.RenderPair.second.RefreshBuffer();
|
|
|
|
}
|
|
|
|
void TMyApplication::AddNewLaser()
|
|
{
|
|
CLaserStruct laser;
|
|
|
|
float angle = Player.Angle;
|
|
|
|
laser.Dir = vec2(cosf(angle), sinf(angle));
|
|
|
|
laser.Pos = Player.Pos;
|
|
|
|
laser.RenderPair.second.Data = MakeDataTriangleList((-1) * CONST_LASER_HALF_SIZE, CONST_LASER_HALF_SIZE);
|
|
|
|
mat3 rotationMatrix = CreateZRotationMatrix(angle);
|
|
|
|
RotateDataTriangleList(laser.RenderPair.second.Data, rotationMatrix);
|
|
|
|
MoveDataTriangleList(laser.RenderPair.second.Data, vec3(laser.Pos, 0));
|
|
|
|
laser.RenderPair.first.SamplerMap[CONST_STRING_TEXTURE_UNIFORM] = "fireTexture";
|
|
|
|
laser.RenderPair.second.RefreshBuffer();
|
|
|
|
Lasers.push_back(laser);
|
|
}
|
|
|
|
void TMyApplication::UpdatePlayerPosition(float scaledTime)
|
|
{
|
|
|
|
if (TapIsDown)
|
|
{
|
|
|
|
vec2 dir = Normalize(LastTap - Player.Pos);
|
|
|
|
Player.Velocity += CONST_PLAYER_ACCELERATION * scaledTime * dir;
|
|
|
|
Player.Angle = acosf(DotProduct(dir, vec2(1, 0)));
|
|
|
|
if (dir.v[1] < 0)
|
|
{
|
|
Player.Angle = -Player.Angle;
|
|
}
|
|
}
|
|
|
|
Player.Pos += Player.Velocity * scaledTime;
|
|
|
|
|
|
Player.Velocity *= powf(CONST_VELOCITY_FADE, clamp(scaledTime, 0.f, 1.5f));
|
|
|
|
Player.Pos = NormalizePos(Player.Pos);
|
|
|
|
RefreshPlayerPos();
|
|
|
|
}
|
|
|
|
void TMyApplication::ProcessLaserHit()
|
|
{
|
|
|
|
for (auto laserItr = Lasers.begin(); laserItr != Lasers.end(); )
|
|
{
|
|
for (auto astItr = Asteroids.begin(); astItr != Asteroids.end(); )
|
|
{
|
|
|
|
float realDistance = HealthToHitDistance(astItr->Health);
|
|
|
|
if (fabs(laserItr->Pos.v[0] - astItr->CenterPos.v[0]) < realDistance &&
|
|
fabs(laserItr->Pos.v[1] - astItr->CenterPos.v[1]) < realDistance)
|
|
{
|
|
laserItr = Lasers.erase(laserItr);
|
|
|
|
if (astItr->Health > 0)
|
|
{
|
|
CreateAsteroidParts(astItr->CenterPos, astItr->Health - 1);
|
|
}
|
|
|
|
astItr = Asteroids.erase(astItr);
|
|
|
|
Score += CONST_HIT_SCORE_POINTS;
|
|
RefreshScoreText();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
if (astItr != Asteroids.end())
|
|
{
|
|
astItr++;
|
|
}
|
|
}
|
|
|
|
if (laserItr != Lasers.end())
|
|
{
|
|
laserItr++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void TMyApplication::ProcessPlayerCollisions()
|
|
{
|
|
|
|
for (auto astItr = Asteroids.begin(); astItr != Asteroids.end(); astItr++)
|
|
{
|
|
float realDistance = HealthToHitDistance(astItr->Health);
|
|
|
|
if (fabs(astItr->CenterPos.v[0] - Player.Pos.v[0]) < realDistance && fabs(astItr->CenterPos.v[1] - Player.Pos.v[1]) < realDistance)
|
|
{
|
|
GameState = GS_LOST;
|
|
RefreshScoreText();
|
|
}
|
|
}
|
|
}
|
|
|
|
void TMyApplication::ClearUnusedLasers()
|
|
{
|
|
for (auto itr = Lasers.begin(); itr != Lasers.end(); )
|
|
{
|
|
if (itr->Pos.v[0] < 0 || itr->Pos.v[1] < 0 || itr->Pos.v[0] > Renderer->GetScreenWidth() || itr->Pos.v[1] > Renderer->GetScreenHeight())
|
|
{
|
|
itr = Lasers.erase(itr);
|
|
}
|
|
|
|
if (itr != Lasers.end())
|
|
{
|
|
itr++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void TMyApplication::RefreshScoreText()
|
|
{
|
|
LevelScoreText = ResourceManager->FontManager.DrawStringToVBO(vec2(10, 20), TTextBasicAreaParams(), "Score: "+ tostr(Score));
|
|
|
|
if (GameState == GS_PLAY)
|
|
{
|
|
LevelMessageText = ResourceManager->FontManager.DrawStringToVBO(vec2(10, 300), TTextBasicAreaParams(), "Now playing "+ tostr(Level) + " level");
|
|
}
|
|
else if (GameState == GS_WON)
|
|
{
|
|
LevelMessageText = ResourceManager->FontManager.DrawStringToVBO(vec2(10, 300), TTextBasicAreaParams(), "You won! Tap on screen to start next level");
|
|
}
|
|
else if (GameState == GS_LOST)
|
|
{
|
|
LevelMessageText = ResourceManager->FontManager.DrawStringToVBO(vec2(10, 300), TTextBasicAreaParams(), "You failed! Tap on screen to restart");
|
|
}
|
|
} |