#include "state.hpp" #include #include #include #include #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" namespace game { void State::init() { m_nextId = 0; m_time = 0.0; 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); 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())); } planets.push_back(new Planet(glm::vec2(0.0f, 0.0f), 0, 0.1f)); } 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) { std::cout << playerId << " quit" << std::endl; Player *player = playerForId(playerId); if (player != nullptr) { for (Missile *missile : player->missiles) { missile->player = nullptr; } players.remove(player); } } void State::clear(int playerId) { std::cout << playerId << " clear" << std::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 ships.remove(victim->ship); delete(victim->ship); victim->ship = nullptr; victim->alive = false; victim->deadTimeCounter = 0.0; // TODO // add points // TODO // message } void State::advancePlayerMissiles(float dt) { std::vector rm; // advance missiles for (Player *player : players) { for (Missile *missile : player->missiles) { //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) { continue; } 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(); } rm.push_back(missile); } for (Missile *missile : rm) { player->missiles.remove(missile); delete(missile); } } } void State::advanceExplosions(float dt) { std::vector rm; for (Explosion *explosion : explosions) { explosion->age += dt; if (explosion->age >= explosion->maxAge) { rm.push_back(explosion); } } for (Explosion *explosion : rm) { explosions.remove(explosion); delete(explosion); } } void State::advance(float dt) { m_time += 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()); } 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(Trace *trace) { traces.remove(trace); if (trace->missile != nullptr) { // delete backlink. // XXX: there's a missile without a trace now. trace->missile->trace = nullptr; } delete(trace); } 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)); } }