KlassischeKeplerKriege/game/state/state.cpp
2016-09-28 16:12:25 +02:00

374 lines
9.6 KiB
C++

#include "state.hpp"
#include <cmath>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <glm/gtx/norm.hpp>
#include "missile_hit_type.hpp"
#include "object.hpp"
#include "missile.hpp"
#include "player.hpp"
#include "planet.hpp"
#include "ship.hpp"
#include "commands.hpp"
#include "trace.hpp"
#include "explosion.hpp"
#include "util.hpp"
using namespace std;
namespace game {
void State::init()
{
m_shipRadius = 0.02;
m_maxMissileDistance = 2.0;
m_playerRespawnTime = 2.0;
m_defaultEnergy = 10.0;
m_maxNumTraces = 10;
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, i, 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;
}
int State::addPlayer()
{
int playerId = m_nextId++;
Player *player = new Player(playerId);
players.push_back(player);
return playerId;
}
void State::quitPlayer(int playerId)
{
(void) playerId;
cout << playerId << " quit" << endl;
}
void State::clear(int playerId)
{
cout << playerId << " clear" << endl;
}
void State::setName(int playerId, std::string name)
{
// discard if not unique
for (const Player *other : players) {
if (name == other->name) {
std::cout << "name '" << name << "' already given to player #" << other->id << std::endl;
return;
}
}
playerForId(playerId)->name = name;
}
void State::setSpeed(int playerId, double speed)
{
playerForId(playerId)->speed = speed;
}
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->ready(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
}
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);
const bool isHit = (evt.hit != Hit::Nothing);
if (missile->trace != nullptr) {
missile->trace->addPointFromMissile(isHit); // force point if missile gets destroyed a
}
if (!isHit) {
i++;
} else {
switch(evt.hit) {
case Hit::Planet:
//std::cout<<"hit Planet" << std::endl;
break;
case Hit::Ship:
//std::cout<<"hit Player" << std::endl;
playerKillsPlayer(playerForId(evt.playerIdKiller), playerForId(evt.playerIdVictim));
break;
case Hit::BorderOfUniverse:
//std::cout<<"missile left the universe." << std::endl;
break;
default:
break;
}
addExplosionFromHit(&evt);
if (missile->trace != nullptr) {
missile->trace->finish();
}
player->missiles.erase(player->missiles.begin() + i);
delete(missile);
//std::cout<<std::endl;
}
// TODO
// add trace to the missile
}
}
}
void State::advanceExplosions(float dt)
{
size_t i=0;
while(i < explosions.size()) {
Explosion *explosion = explosions[i];
explosion->age += dt;
if (explosion->age >= explosion->maxAge) {
delete(explosion);
explosions.erase(explosions.begin()+i);
continue;
}
i++;
}
}
void State::advance(float dt)
{
advancePlayerShipSpawns(dt);
advanceExplosions(dt);
advancePlayerCommands(dt);
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);
}
}
void State::addTrace(Trace *trace)
{
//int count = 0;
//for (Trace *old : traces) {
// if (old->playerId == trace->playerId) {
// count++;
// }
//}
//if (count > m_maxNumTraces) {
//}
traces.push_back(trace);
}
void State::deleteTrace(size_t index)
{
if (index >= traces.size()) {
std::cerr << "can't find trace with invalid index " << index << std::endl;
return;
}
Trace *trace = traces[index];
//std::cout<<"removing a trace" << std::endl;
if (trace->missile != nullptr) {
// delete backlink.
// XXX: there's a missile without a trace now.
trace->missile->trace = nullptr;
}
traces.erase(traces.begin() + index);
delete(trace);
}
void State::deleteTrace(Trace *trace)
{
size_t i = 0;
while(i < traces.size()) {
if (traces[i] == trace) {
deleteTrace(i);
break;
}
}
}
void State::addExplosionFromHit(const Missile::Event *evt)
{
if (evt->hit == Hit::Nothing || evt->hit == Hit::BorderOfUniverse) {
return;
}
explosions.push_back(new Explosion(evt->position, evt->hit));
}
}