added my simple simulation implementation with trivial graphics
This commit is contained in:
parent
ea0aa01b2d
commit
183a26412a
28 changed files with 950 additions and 36 deletions
|
@ -8,9 +8,19 @@ set(GAME_SRC
|
|||
main.cpp
|
||||
opengl.cpp
|
||||
glclasses.cpp
|
||||
config.cpp
|
||||
simulation.cpp
|
||||
renderer.cpp
|
||||
game_window.cpp
|
||||
triangle_window.cpp
|
||||
|
||||
util.cpp
|
||||
game.cpp
|
||||
state/object.cpp
|
||||
state/state.cpp
|
||||
state/player.cpp
|
||||
state/planet.cpp
|
||||
state/missile.cpp
|
||||
state/commands.cpp
|
||||
state/state.cpp
|
||||
)
|
||||
|
||||
set(GAME_HEADERS
|
||||
|
|
67
game/game.cpp
Normal file
67
game/game.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
#include "game.hpp"
|
||||
|
||||
#include "state/commands.hpp"
|
||||
|
||||
#include "util.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
Game::Game()
|
||||
{
|
||||
// advance simulation with 100 Hz
|
||||
m_time_step = 1.0 / 100.0;
|
||||
m_time_for_next_step = 0.0;
|
||||
|
||||
m_state = new game::State();
|
||||
m_state->init();
|
||||
|
||||
m_state->addPlayer(0);
|
||||
m_state->addPlayer(1);
|
||||
m_state->addPlayer(2);
|
||||
m_state->addPlayer(3);
|
||||
//m_state->addPlayer(2);
|
||||
}
|
||||
|
||||
bool Game::cycle(float dt)
|
||||
{
|
||||
static float acc = 0.0;
|
||||
acc += dt;
|
||||
|
||||
float spawnInterval = 0.1;
|
||||
while(acc > spawnInterval) {
|
||||
acc -= spawnInterval;
|
||||
|
||||
float a = 2.0 * M_PI * util::randf_0_1();
|
||||
float speed = 0.002;
|
||||
m_state->players[0]->addCommand(new game::ShootCommand(a, speed));
|
||||
}
|
||||
|
||||
#if 1
|
||||
if (dt >= 10.0) {
|
||||
//std::cout<<"time to big: " << dt << std::endl;
|
||||
dt = m_time_step;
|
||||
}
|
||||
|
||||
//std::cout<<"adding dt: " << dt << std::endl;
|
||||
m_time_for_next_step += dt;
|
||||
|
||||
int steps = 0;
|
||||
while(m_time_for_next_step >= m_time_step) {
|
||||
//std::cout<<"time now: " << m_time_for_next_step << std::endl;
|
||||
m_time_for_next_step -= m_time_step;
|
||||
|
||||
m_state->advance(m_time_step);
|
||||
steps++;
|
||||
}
|
||||
|
||||
//std::cout << m_time_for_next_step << " s remaining time, " << steps << " steps taken." << std::endl;
|
||||
|
||||
return true;
|
||||
#else
|
||||
(void) dt;
|
||||
|
||||
m_state->advance(dt);
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
21
game/game.hpp
Normal file
21
game/game.hpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include "state/state.hpp"
|
||||
|
||||
class Game {
|
||||
public:
|
||||
Game();
|
||||
|
||||
// main method of the game. run this regulary
|
||||
// return false if want to exit.
|
||||
bool cycle(float dt);
|
||||
|
||||
// for rendering
|
||||
const game::State *state() const { return m_state; }
|
||||
|
||||
private:
|
||||
game::State *m_state;
|
||||
|
||||
float m_time_for_next_step;
|
||||
float m_time_step;
|
||||
};
|
0
game/game_window.cpp
Normal file
0
game/game_window.cpp
Normal file
89
game/game_window.h
Normal file
89
game/game_window.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
#pragma once
|
||||
|
||||
#include "opengl.h"
|
||||
#include "renderer.h"
|
||||
|
||||
#include "game.hpp"
|
||||
|
||||
#include "state/object.hpp"
|
||||
#include "state/missile.hpp"
|
||||
#include "state/player.hpp"
|
||||
#include "state/planet.hpp"
|
||||
#include "state/ship.hpp"
|
||||
|
||||
class GameWindow : public endofthejedi::GLWindow {
|
||||
private:
|
||||
protected:
|
||||
void init() override {
|
||||
glClearColor(0.5f, 0.6f, 0.7f, 1.0f);
|
||||
resize();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
void render(double time) override {
|
||||
//static bool once = false;
|
||||
//if (!once) {
|
||||
// once = true;
|
||||
// for (int i=0; i<1000; i++) {
|
||||
// m_game.cycle(time);
|
||||
// }
|
||||
//}
|
||||
|
||||
if (!m_game.cycle(static_cast<float>(time/1000000000.0))) {
|
||||
std::cout<<"stopping the game..." << std::endl;
|
||||
stop();
|
||||
}
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
for (const game::Planet *planet : m_game.state()->planets) {
|
||||
drawPlanet(planet->position, planet->radius);
|
||||
}
|
||||
|
||||
for (const game::Ship *ship : m_game.state()->ships) {
|
||||
drawShip(ship->position);
|
||||
}
|
||||
|
||||
for (const game::Player *player : m_game.state()->players) {
|
||||
for (const game::Missile *missile : player->missiles) {
|
||||
drawMissile(missile->position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void resize() override { glViewport(0, 0, getwidth(), getheight()); }
|
||||
|
||||
void drawShip(const glm::vec2 &pos)
|
||||
{
|
||||
//std::cout<<"draw ship @ " << pos.x << ", " << pos.y << std::endl;
|
||||
glm::vec3 color = glm::vec3(0.2, 1.0, 0.3);
|
||||
|
||||
float radius = m_game.state()->shipRadius();
|
||||
|
||||
m_renderer.drawCircle(pos.x, pos.y, radius, color.x, color.y, color.z, 12);
|
||||
}
|
||||
|
||||
void drawPlanet(const glm::vec2 &pos, float radius)
|
||||
{
|
||||
glm::vec3 color = glm::vec3(0.7, 0.1, 0.2);
|
||||
|
||||
//std::cout<<"draw planet @ " << pos.x << ", " << pos.y << std::endl;
|
||||
m_renderer.drawCircle(pos.x, pos.y, radius, color.x, color.y, color.z, 32);
|
||||
}
|
||||
|
||||
void drawMissile(const glm::vec2 &pos)
|
||||
{
|
||||
glm::vec3 color = glm::vec3(1.0, 1.0, 0.3);
|
||||
m_renderer.drawCircle(pos.x, pos.y, 0.005, color.x, color.y, color.z, 5);
|
||||
}
|
||||
|
||||
public:
|
||||
GameWindow(unsigned int width, unsigned int height)
|
||||
: endofthejedi::GLWindow(width, height) {}
|
||||
|
||||
private:
|
||||
Game m_game;
|
||||
endofthejedi::Renderer m_renderer;
|
||||
};
|
||||
|
|
@ -1,36 +1,11 @@
|
|||
#include "opengl.h"
|
||||
#include <iostream>
|
||||
|
||||
class MainWindow : public endofthejedi::GLWindow {
|
||||
private:
|
||||
protected:
|
||||
void init() override {
|
||||
glClearColor(0.5f, 0.6f, 0.7f, 1.0f);
|
||||
resize();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
void render(double time) override {
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glBegin(GL_TRIANGLES);
|
||||
glColor3f(1.0f, 0.0f, 0.0f);
|
||||
glVertex3f(0.0f, -1.0f, 0.0f);
|
||||
glColor3f(0.0f, 1.0f, 0.0f);
|
||||
glVertex3f(-1.0f, 1.0f, 0.0f);
|
||||
glColor3f(0.0f, 0.0f, 1.0f);
|
||||
glVertex3f(1.0f, 1.0f, 0.0f);
|
||||
glEnd();
|
||||
}
|
||||
void resize() override { glViewport(0, 0, getwidth(), getheight()); }
|
||||
|
||||
public:
|
||||
MainWindow(unsigned int width, unsigned int height)
|
||||
: endofthejedi::GLWindow(width, height) {}
|
||||
};
|
||||
//#include "triangle_window.h"
|
||||
#include "game_window.h"
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
MainWindow window(500, 500);
|
||||
GameWindow window(500, 500);
|
||||
window.set_maxfps(60.0);
|
||||
window.loop();
|
||||
window.stop();
|
||||
|
|
|
@ -7,12 +7,12 @@ endofthejedi::Renderer::Renderer() {
|
|||
endofthejedi::Renderer::~Renderer() {}
|
||||
|
||||
void endofthejedi::Renderer::drawCircle(float x, float y, float radius, float r,
|
||||
float g, float b) {
|
||||
float g, float b, int numSides) {
|
||||
glBegin(GL_TRIANGLE_FAN);
|
||||
glVertex2f(x, y); // center of circle
|
||||
for (int i = 0; i <= 64; i++) {
|
||||
for (int i = 0; i <= numSides; i++) {
|
||||
glColor3f(r,g,b);
|
||||
glVertex2f(x + (radius * cos(i * 2 * M_PI / 64)), y + (radius * sin(i * 2 * M_PI / 64)));
|
||||
glVertex2f(x + (radius * cos(i * 2 * M_PI / numSides)), y + (radius * sin(i * 2 * M_PI / numSides)));
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ class Renderer {
|
|||
Renderer();
|
||||
~Renderer();
|
||||
void drawCircle(float x, float y, float radius, float r,
|
||||
float g, float b);
|
||||
float g, float b, int numSides=12);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
22
game/state/commands.cpp
Normal file
22
game/state/commands.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include "commands.hpp"
|
||||
|
||||
namespace game {
|
||||
void ShootCommand::apply(Player *player, State *state) const
|
||||
{
|
||||
(void) state;
|
||||
// TODO spawn missile if alive and enough energy
|
||||
//std::cout<<"apply command " << name() << std::endl;
|
||||
|
||||
player->energy -= m_speed;
|
||||
player->missiles.push_back(new Missile(player->id, player->ship->position, m_angle, m_speed));
|
||||
}
|
||||
|
||||
bool ShootCommand::allowed(const Player *player, const State *state) const
|
||||
{
|
||||
(void) state;
|
||||
|
||||
// TODO
|
||||
return player->alive && player->energy >= m_speed;
|
||||
//return player->alive;
|
||||
}
|
||||
}
|
56
game/state/commands.hpp
Normal file
56
game/state/commands.hpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include "state.hpp"
|
||||
#include "player.hpp"
|
||||
#include "ship.hpp"
|
||||
#include "planet.hpp"
|
||||
#include "missile.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace game {
|
||||
class Command {
|
||||
public:
|
||||
Command()
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Command()
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool allowed(const Player *player, const State *state) const
|
||||
{
|
||||
(void) player;
|
||||
(void) state;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void apply(Player *player, State *state) const
|
||||
{
|
||||
(void) state;
|
||||
(void) player;
|
||||
|
||||
std::cout<<"Command '" << name() << "' not yet implemented!" << std::endl;
|
||||
}
|
||||
|
||||
virtual std::string name() const { return "<unnamed>"; }
|
||||
};
|
||||
|
||||
class ShootCommand : public Command {
|
||||
public:
|
||||
ShootCommand(float angle, float speed) : m_angle(angle), m_speed(speed)
|
||||
{
|
||||
}
|
||||
|
||||
std::string name() const { return "<shoot>"; }
|
||||
|
||||
void apply( Player *player, State *state) const;
|
||||
bool allowed(const Player *player, const State *state) const;
|
||||
|
||||
private:
|
||||
float m_angle;
|
||||
float m_speed;
|
||||
};
|
||||
}
|
0
game/state/explosion.cpp
Normal file
0
game/state/explosion.cpp
Normal file
17
game/state/explosion.hpp
Normal file
17
game/state/explosion.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#if 0
|
||||
class Explosion {
|
||||
public:
|
||||
enum class Kind {
|
||||
Normal,
|
||||
MissileWithShip
|
||||
};
|
||||
|
||||
glm::vec2 position;
|
||||
|
||||
Explosion(Kind kind);
|
||||
|
||||
public:
|
||||
};
|
||||
#endif
|
82
game/state/missile.cpp
Normal file
82
game/state/missile.cpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
#include "missile.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "state.hpp"
|
||||
#include "player.hpp"
|
||||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
namespace game {
|
||||
Missile::Missile(int playerId, const glm::vec2 &pos, float angle, float speed)
|
||||
: playerId(playerId)
|
||||
, position(pos)
|
||||
{
|
||||
velocity = speed * glm::vec2(std::sin(angle), std::cos(angle));
|
||||
|
||||
//std::cout << "spawned missile for player " << playerId << " @ ("
|
||||
// << pos.x << ", " << pos.y << ") speed = " << speed << std::endl;
|
||||
}
|
||||
|
||||
Missile::~Missile()
|
||||
{
|
||||
//std::cout<<"destroyed missile" << std::endl;
|
||||
}
|
||||
|
||||
Missile::Event Missile::advance(const game::State *state, float dt)
|
||||
{
|
||||
glm::vec2 gravityForce = glm::vec2(0.0, 0.0);
|
||||
|
||||
for (const Player *other : state->players) {
|
||||
if (other->ship != nullptr && other->id != playerId) {
|
||||
glm::vec2 diff = other->ship->position - position;
|
||||
float dist = glm::length(diff);
|
||||
if (dist <= other->ship->radius) {
|
||||
// TODO: collect all hits and return the first one only
|
||||
// TODO: find exact hit position!
|
||||
return Missile::Event(position, playerId, other->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get forces
|
||||
for (const Planet *planet : state->planets) {
|
||||
glm::vec2 diff = planet->position - position;
|
||||
float dist = glm::length(diff);
|
||||
if (dist <= planet->radius) {
|
||||
// TODO: collect all hits and return the first one only
|
||||
// TODO: find exact hit position!
|
||||
return Missile::Event(position, Missile::HitObject::HitPlanet);
|
||||
}
|
||||
|
||||
dist *= 20.0;
|
||||
if (dist > 0.001) {
|
||||
float G = 0.1;
|
||||
gravityForce += G * diff / (1.0f + dist*dist);
|
||||
}
|
||||
}
|
||||
|
||||
// simple (and bad) euler integration.
|
||||
position += 0.5f * gravityForce*dt*dt + velocity*dt;
|
||||
velocity += gravityForce * dt;
|
||||
|
||||
//const float thr = 0.01;
|
||||
//float s = glm::length(velocity);
|
||||
//if (s > thr) {
|
||||
// velocity = glm::normalize(velocity) * thr;
|
||||
//}
|
||||
|
||||
glm::vec2 nextPos = position + velocity;
|
||||
|
||||
// TODO: collision checks
|
||||
position = nextPos;
|
||||
|
||||
// check if distance to center of the universe is getting too big
|
||||
float distToCenter = glm::length(position);
|
||||
if (distToCenter > state->maxMissileDistance()) {
|
||||
return Missile::Event(position, Missile::HitObject::LeftUniverse);
|
||||
}
|
||||
|
||||
return Missile::Event(position);
|
||||
}
|
||||
}
|
68
game/state/missile.hpp
Normal file
68
game/state/missile.hpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include "state.hpp"
|
||||
|
||||
#include "ship.hpp"
|
||||
#include "planet.hpp"
|
||||
|
||||
namespace game {
|
||||
class State;
|
||||
|
||||
// missile belongs to a player and optionally fills a trace behind it.
|
||||
// trace then belongs to the player.
|
||||
class Missile {
|
||||
public:
|
||||
enum class HitObject {
|
||||
Nothing,
|
||||
HitPlayer,
|
||||
HitPlanet,
|
||||
// HitMissile,
|
||||
LeftUniverse
|
||||
};
|
||||
|
||||
// missile advances to pos. if hit != Nothing, it hits something and
|
||||
// stops existing afterwards.
|
||||
class Event {
|
||||
public:
|
||||
Event(const glm::vec2 &pos) : Event(pos, HitObject::Nothing)
|
||||
{
|
||||
}
|
||||
|
||||
Event(const glm::vec2 &pos, HitObject hit)
|
||||
: hit(hit), pos(pos)
|
||||
{
|
||||
}
|
||||
|
||||
Event(const glm::vec2 &pos, int playerIdKiller, int playerIdVictim)
|
||||
: Event(pos, HitObject::HitPlayer)
|
||||
{
|
||||
this->playerIdKiller = playerIdKiller;
|
||||
this->playerIdVictim = playerIdVictim;
|
||||
}
|
||||
|
||||
HitObject hit;
|
||||
glm::vec2 pos;
|
||||
|
||||
int playerIdKiller;
|
||||
int playerIdVictim;
|
||||
};
|
||||
|
||||
// XXX
|
||||
int playerId; // owner won't be hit by own missiles
|
||||
glm::vec2 position;
|
||||
glm::vec2 velocity;
|
||||
//Trace *trace;
|
||||
|
||||
Missile(int playerId, const glm::vec2 &pos, float angle, float speed);
|
||||
|
||||
~Missile();
|
||||
|
||||
// try to advance. if something will be hit, return the first hit in
|
||||
// time.
|
||||
Missile::Event advance(const game::State *state, float dt);
|
||||
};
|
||||
}
|
||||
|
0
game/state/object.cpp
Normal file
0
game/state/object.cpp
Normal file
15
game/state/object.hpp
Normal file
15
game/state/object.hpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
class Object {
|
||||
public:
|
||||
Object(const glm::vec2 &pos, float r) : position(pos), radius(r)
|
||||
{
|
||||
}
|
||||
|
||||
const glm::vec2 position;
|
||||
const float radius;
|
||||
};
|
||||
|
0
game/state/planet.cpp
Normal file
0
game/state/planet.cpp
Normal file
12
game/state/planet.hpp
Normal file
12
game/state/planet.hpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "object.hpp"
|
||||
|
||||
namespace game {
|
||||
class Planet : public Object {
|
||||
public:
|
||||
Planet(const glm::vec2 &pos, float r) : Object(pos, r)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
33
game/state/player.cpp
Normal file
33
game/state/player.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include "player.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "commands.hpp"
|
||||
|
||||
#include "ship.hpp"
|
||||
|
||||
namespace game {
|
||||
void Player::popCommand()
|
||||
{
|
||||
//std::cout<<"popCommand()" << std::endl;
|
||||
|
||||
Command *cmd = commands.front();
|
||||
commands.erase(commands.begin());
|
||||
delete(cmd);
|
||||
}
|
||||
|
||||
Command *Player::peekCommand()
|
||||
{
|
||||
return commands.front();
|
||||
}
|
||||
|
||||
bool Player::hasCommandInQueue()
|
||||
{
|
||||
return !commands.empty();
|
||||
}
|
||||
|
||||
void Player::addCommand(Command *cmd)
|
||||
{
|
||||
commands.push_back(cmd);
|
||||
}
|
||||
}
|
36
game/state/player.hpp
Normal file
36
game/state/player.hpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
namespace game {
|
||||
class Ship;
|
||||
class Command;
|
||||
class Missile;
|
||||
|
||||
class Player {
|
||||
public:
|
||||
int id;
|
||||
bool alive;
|
||||
float energy;
|
||||
float deadTimeCounter;
|
||||
Ship *ship;
|
||||
std::vector<Missile*> missiles;
|
||||
|
||||
Player(int id) : id(id), alive(true), energy(0.0), ship(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void addCommand(Command *cmd);
|
||||
bool hasCommandInQueue();
|
||||
|
||||
Command *peekCommand();
|
||||
void popCommand();
|
||||
|
||||
private:
|
||||
std::deque<Command*> commands;
|
||||
};
|
||||
}
|
0
game/state/ship.cpp
Normal file
0
game/state/ship.cpp
Normal file
12
game/state/ship.hpp
Normal file
12
game/state/ship.hpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "object.hpp"
|
||||
|
||||
namespace game {
|
||||
class Ship : public Object {
|
||||
public:
|
||||
Ship(const glm::vec2 &pos, float r) : Object(pos, r)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
262
game/state/state.cpp
Normal file
262
game/state/state.cpp
Normal file
|
@ -0,0 +1,262 @@
|
|||
#include "state.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
#include "object.hpp"
|
||||
#include "missile.hpp"
|
||||
#include "player.hpp"
|
||||
#include "planet.hpp"
|
||||
#include "ship.hpp"
|
||||
#include "commands.hpp"
|
||||
|
||||
#include "util.hpp"
|
||||
|
||||
namespace game {
|
||||
void State::init()
|
||||
{
|
||||
m_shipRadius = 0.05;
|
||||
m_maxMissileDistance = 2.0;
|
||||
m_playerRespawnTime = 2.0;
|
||||
m_defaultEnergy = 10.0;
|
||||
|
||||
bool planetsOnCircle = false;
|
||||
|
||||
int numPlanets = 10;
|
||||
for (int i=0; i<numPlanets; i++) {
|
||||
float t = i / static_cast<float>(numPlanets);
|
||||
t *= 2.0*M_PI;
|
||||
|
||||
// distribute but not in the center
|
||||
int tries = 0;
|
||||
glm::vec2 pos;
|
||||
do {
|
||||
float cr = 0.7;
|
||||
if (planetsOnCircle) {
|
||||
pos = cr * glm::vec2(std::sin(t), std::cos(t));
|
||||
} else {
|
||||
pos = cr * util::randv2_m1_1();
|
||||
}
|
||||
} while(glm::length(pos) < 0.2 && tries++ < 1000);
|
||||
|
||||
planets.push_back(new Planet(pos, 0.03 + 0.07*util::randf_0_1()));
|
||||
}
|
||||
}
|
||||
|
||||
bool State::spawnShipForPlayer(Player *player)
|
||||
{
|
||||
glm::vec2 spawnPos;
|
||||
if (!findFreePositionWithRadius(5.0 * m_shipRadius, spawnPos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
player->ship = new Ship(spawnPos, m_shipRadius);
|
||||
ships.push_back(player->ship);
|
||||
|
||||
player->energy = m_defaultEnergy;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void State::addPlayer(int playerId)
|
||||
{
|
||||
Player *player = new Player(playerId);
|
||||
players.push_back(player);
|
||||
}
|
||||
|
||||
void State::playerLeft(int playerId)
|
||||
{
|
||||
(void) playerId;
|
||||
}
|
||||
|
||||
void State::advancePlayerShipSpawns(float dt)
|
||||
{
|
||||
(void) dt;
|
||||
|
||||
for (Player *player : players) {
|
||||
if (!player->alive) {
|
||||
player->deadTimeCounter += dt;
|
||||
if (player->deadTimeCounter >= m_playerRespawnTime) {
|
||||
player->deadTimeCounter = 0;
|
||||
player->alive = true;
|
||||
std::cout<<"respawning player " << player->id << " now!" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (player->alive && player->ship == NULL) {
|
||||
if (!spawnShipForPlayer(player)) {
|
||||
std::cout<<"could not spawn ship for player!" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void State::advancePlayerCommands(float dt)
|
||||
{
|
||||
(void) dt;
|
||||
for (Player *player : players) {
|
||||
if (player->alive) {
|
||||
player->energy += dt;
|
||||
}
|
||||
|
||||
// try to execute as much queued commands as possible.
|
||||
while (player->hasCommandInQueue()) {
|
||||
Command *command = player->peekCommand();
|
||||
if (!command->allowed(player, this)) {
|
||||
break;
|
||||
}
|
||||
|
||||
command->apply(player, this);
|
||||
player->popCommand();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void State::playerKillsPlayer(Player *killer, Player *victim)
|
||||
{
|
||||
if (killer == nullptr || victim == nullptr) {
|
||||
std::cerr <<"error: killer / victim is NULL!" << std::endl;
|
||||
exit(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout<<"player " << killer->id << " killed " << victim->id << std::endl;
|
||||
|
||||
// destroy ship
|
||||
size_t i;
|
||||
for (i=0; i<ships.size(); i++) {
|
||||
if (ships[i] == victim->ship) {
|
||||
ships.erase(ships.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete(victim->ship);
|
||||
victim->ship = nullptr;
|
||||
|
||||
victim->alive = false;
|
||||
victim->deadTimeCounter = 0.0;
|
||||
|
||||
// TODO
|
||||
// add points
|
||||
//
|
||||
// TODO
|
||||
// message
|
||||
//
|
||||
// TODO
|
||||
// respawn timer
|
||||
}
|
||||
|
||||
void State::advancePlayerMissiles(float dt)
|
||||
{
|
||||
// advance missiles
|
||||
for (Player *player : players) {
|
||||
size_t i = 0;
|
||||
while (i<player->missiles.size()) {
|
||||
Missile *missile = player->missiles[i];
|
||||
|
||||
//std::cout<<"missile: " << (long unsigned int) missile << std::endl;
|
||||
const Missile::Event evt = missile->advance(this, dt);
|
||||
|
||||
if (evt.hit == Missile::HitObject::Nothing) {
|
||||
i++;
|
||||
|
||||
} else {
|
||||
switch(evt.hit) {
|
||||
case Missile::HitObject::HitPlanet:
|
||||
//std::cout<<"hit Planet" << std::endl;
|
||||
break;
|
||||
|
||||
case Missile::HitObject::HitPlayer:
|
||||
//std::cout<<"hit Player" << std::endl;
|
||||
playerKillsPlayer(playerForId(evt.playerIdKiller), playerForId(evt.playerIdVictim));
|
||||
break;
|
||||
|
||||
case Missile::HitObject::LeftUniverse:
|
||||
//std::cout<<"missile left the universe." << std::endl;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
player->missiles.erase(player->missiles.begin() + i);
|
||||
delete(missile);
|
||||
//std::cout<<std::endl;
|
||||
}
|
||||
|
||||
// TODO
|
||||
// add trace to the missile
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void State::advance(float dt)
|
||||
{
|
||||
//std::cout<<"advance ship spawns" << std::endl;
|
||||
advancePlayerShipSpawns(dt);
|
||||
|
||||
//std::cout<<"advance commands" << std::endl;
|
||||
advancePlayerCommands(dt);
|
||||
|
||||
//std::cout<<"advance missiles" << std::endl;
|
||||
advancePlayerMissiles(dt);
|
||||
}
|
||||
|
||||
Player *State::playerForId(int playerId)
|
||||
{
|
||||
for (Player *p : players) {
|
||||
if (p->id == playerId) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool State::findFreePositionWithRadius(float radius, glm::vec2 &pos)
|
||||
{
|
||||
bool first = ships.size() == 0;
|
||||
|
||||
int tries = 0;
|
||||
while(true) {
|
||||
bool noCollision = true;
|
||||
|
||||
//pos = glm::linearRand2(-1.0, 1.0);
|
||||
if (first) {
|
||||
first = false;
|
||||
pos = glm::vec2(0.0, 0.0);
|
||||
} else {
|
||||
pos = glm::vec2(util::randf_0_1(), util::randf_m1_1());
|
||||
//glm::linearRand(-1.0, 1.0), glm::linearRand(-1.0, 1.0));
|
||||
}
|
||||
|
||||
for (Planet *p : planets) {
|
||||
if (glm::distance(p->position, pos) <= radius) {
|
||||
noCollision = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (noCollision) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tries++ >= 1000) {
|
||||
pos = glm::vec2(0.0, 0.0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void State::commandForPlayer(int playerId, game::Command *cmd)
|
||||
{
|
||||
Player *player = playerForId(playerId);
|
||||
if (player != nullptr) {
|
||||
player->addCommand(cmd);
|
||||
}
|
||||
}
|
||||
}
|
64
game/state/state.hpp
Normal file
64
game/state/state.hpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
|
||||
namespace game {
|
||||
class Command;
|
||||
class Missile;
|
||||
class Player;
|
||||
class Planet;
|
||||
class Ship;
|
||||
|
||||
// trace of a missile. exists without a missile at player.
|
||||
//class Trace {
|
||||
//public:
|
||||
// std::vector<glm::vec2> points;
|
||||
//};
|
||||
|
||||
class State {
|
||||
public:
|
||||
void init();
|
||||
void advance(float dt);
|
||||
|
||||
// the (network) layer calling these three functions should keep id's
|
||||
// unique and give one (network) input an id.
|
||||
void addPlayer(int playerId);
|
||||
void playerLeft(int playerId);
|
||||
void commandForPlayer(int playerId, Command *cmd);
|
||||
|
||||
// lookup. return nullptr on invalid playerId
|
||||
Player *playerForId(int playerId);
|
||||
|
||||
float maxMissileDistance() const { return m_maxMissileDistance; }
|
||||
float shipRadius() const { return m_shipRadius; }
|
||||
|
||||
std::vector<Planet*> planets;
|
||||
std::vector<Ship*> ships;
|
||||
std::vector<Player*> players;
|
||||
|
||||
private:
|
||||
void playerKillsPlayer(Player *killer, Player *victim);
|
||||
|
||||
void advancePlayerShipSpawns(float dt);
|
||||
void advancePlayerCommands(float dt);
|
||||
void advancePlayerMissiles(float dt);
|
||||
|
||||
// try to spawn a ship for this player.
|
||||
// return true on success, false on failure to find a spot.
|
||||
bool spawnShipForPlayer(Player *player);
|
||||
|
||||
// find some place where nothing is placed nearby (ships/planets).
|
||||
// usefule for spanwing things
|
||||
bool findFreePositionWithRadius(float r, glm::vec2 &pos);
|
||||
|
||||
float m_maxMissileDistance;
|
||||
float m_playerRespawnTime;
|
||||
float m_shipRadius;
|
||||
float m_defaultEnergy;
|
||||
};
|
||||
};
|
0
game/triangle_window.cpp
Normal file
0
game/triangle_window.cpp
Normal file
36
game/triangle_window.h
Normal file
36
game/triangle_window.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include "opengl.h"
|
||||
|
||||
class TriangleWindow : public endofthejedi::GLWindow {
|
||||
private:
|
||||
protected:
|
||||
void init() override {
|
||||
glClearColor(0.5f, 0.6f, 0.7f, 1.0f);
|
||||
resize();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
void render(double time) override {
|
||||
(void) time;
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glBegin(GL_TRIANGLES);
|
||||
glColor3f(1.0f, 0.0f, 0.0f);
|
||||
glVertex3f(0.0f, -1.0f, 0.0f);
|
||||
glColor3f(0.0f, 1.0f, 0.0f);
|
||||
glVertex3f(-1.0f, 1.0f, 0.0f);
|
||||
glColor3f(0.0f, 0.0f, 1.0f);
|
||||
glVertex3f(1.0f, 1.0f, 0.0f);
|
||||
glEnd();
|
||||
}
|
||||
void resize() override { glViewport(0, 0, getwidth(), getheight()); }
|
||||
|
||||
public:
|
||||
TriangleWindow(unsigned int width, unsigned int height)
|
||||
: endofthejedi::GLWindow(width, height)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
25
game/util.cpp
Normal file
25
game/util.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "util.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
namespace util {
|
||||
float randf_m1_1()
|
||||
{
|
||||
return 2.0f*randf_0_1() - 1.0f;
|
||||
}
|
||||
|
||||
float randf_0_1()
|
||||
{
|
||||
return static_cast<float>(static_cast<double>(rand()) / (double) RAND_MAX);
|
||||
}
|
||||
|
||||
glm::vec2 randv2_m1_1()
|
||||
{
|
||||
return glm::vec2(randf_m1_1(), randf_m1_1());
|
||||
}
|
||||
|
||||
glm::vec2 randv2_0_1()
|
||||
{
|
||||
return glm::vec2(randf_0_1(), randf_0_1());
|
||||
}
|
||||
}
|
12
game/util.hpp
Normal file
12
game/util.hpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
|
||||
namespace util {
|
||||
|
||||
float randf_0_1();
|
||||
float randf_m1_1();
|
||||
|
||||
glm::vec2 randv2_m1_1();
|
||||
glm::vec2 randv2_0_1();
|
||||
}
|
Loading…
Reference in a new issue