275 lines
7.3 KiB
C++
275 lines
7.3 KiB
C++
#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 "trace.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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
missile->trace->addPointFromMissile();
|
|
|
|
// TODO:
|
|
// spawn just if the path differs
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
void State::addTrace(Trace *trace)
|
|
{
|
|
traces.push_back(trace);
|
|
}
|
|
}
|