828 lines
23 KiB
C++
828 lines
23 KiB
C++
#include <iostream>
|
|
#include <fstream>
|
|
#include <algorithm>
|
|
#include <boost/filesystem.hpp>
|
|
#include <SDL/SDL_net.h>
|
|
|
|
#include "Model.h"
|
|
#include "Physics.h"
|
|
#include "PhysicsBase.h"
|
|
|
|
#include "EntityFactory.h"
|
|
#include "AsteroidsEvents.h"
|
|
#include "AsteroidEntity.h"
|
|
|
|
#include <assert.h>
|
|
|
|
namespace asteroids {
|
|
|
|
static Model* ModelInstance = NULL;
|
|
|
|
Engine::Variable Model::HighscoreServerName ("highscore_server_name", "asteroids.fysx.org");
|
|
Engine::Variable Model::HighscoreServerPath ("highscore_server_path", "/highscore/highscore.php?format=raw");
|
|
Engine::Variable Model::UseServerHighscore ("use_server_highscore", "false");
|
|
|
|
/*
|
|
* Inherited Module functions
|
|
*/
|
|
int Model::OnInit (int argc, char* argv[]) {
|
|
int result = Engine::ModelBase::OnInit (argc, argv);
|
|
|
|
Engine::LogMessage ("Model Initialization!");
|
|
|
|
ModelInstance = this;
|
|
|
|
mGameState = GameStatePaused;
|
|
mLastGameState = GameStatePaused;
|
|
|
|
/// \TODO use <optional> or similar for initialization of mCurrentLevelIndex
|
|
mCurrentLevelIndex = 99999;
|
|
|
|
if (InitLevelList() == 0)
|
|
Engine::LogError ("No levels found!");
|
|
|
|
// Reset the newest highscore entry index which may be used for highlighting
|
|
// the newest entry.
|
|
mNewestHighscoreEntryIndex = std::numeric_limits<unsigned int>::max();
|
|
mHighscoreIsOnline = false;
|
|
|
|
// initialize event handlers and register them
|
|
Engine::RegisterListener (this, EventGameOver);
|
|
Engine::RegisterListener (this, EventShipExplode);
|
|
|
|
mPlayerName = "Player";
|
|
|
|
mLevelAuthor = "";
|
|
mLevelTitle = "";
|
|
mLevelName = "";
|
|
mLevelParTimeSeconds = -1.f;
|
|
|
|
// initialize SDL_net to be able to retrieve highscore from the internet
|
|
if (SDLNet_Init() == -1) {
|
|
Engine::LogError("SDLNet_Init: %s\n", SDLNet_GetError());
|
|
}
|
|
|
|
Engine::RegisterListener (this, EventAccelerateStart);
|
|
Engine::RegisterListener (this, EventAccelerateStop);
|
|
Engine::RegisterListener (this, EventLevelComplete);
|
|
|
|
return result;
|
|
}
|
|
|
|
void Model::OnDestroy() {
|
|
Engine::ModelBase::OnDestroy();
|
|
mAsteroids.clear();
|
|
mLevelList.clear();
|
|
SaveLocalHighscoreList();
|
|
|
|
SDLNet_Quit();
|
|
}
|
|
|
|
bool Model::OnReceiveEvent (const Engine::EventBasePtr &event) {
|
|
switch (event->mEventType) {
|
|
case EventAccelerateStart:
|
|
Engine::PlaySoundLoop(Engine::GetResourceFullPath("/data/sounds/thrust.wav"), -1);
|
|
break;
|
|
case EventAccelerateStop:
|
|
Engine::HaltSoundLoop(Engine::GetResourceFullPath("/data/sounds/thrust.wav"));
|
|
break;
|
|
case EventShipExplode:
|
|
OnShipExplode();
|
|
break;
|
|
case EventLevelComplete:
|
|
OnLevelComplete ();
|
|
break;
|
|
case EventGameOver:
|
|
return OnGameOver();
|
|
break;
|
|
|
|
default: Engine::LogWarning ("Received Event with type %d but don't know what to do with it!", event->mEventType);
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Module specific functions
|
|
*/
|
|
void Model::Process () {
|
|
if (mLastGameState == mGameState) {
|
|
if (mGameState == GameStateRunning) {
|
|
// Only if we are still controlling the ship we increase the level
|
|
// timer
|
|
if (Engine::GetPlayerEntityId() != Engine::NullEntityId)
|
|
mLevelTimeSeconds += Engine::GetFrameDuration();
|
|
|
|
Engine::ModelBase::Process();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// when we are here we know that something has changed so we need to take
|
|
// some action.
|
|
Engine::LogDebug ("Switching from %s->%s", GetStringGameState(mLastGameState), GetStringGameState(mGameState));
|
|
|
|
// ... and we have to set the last game state to the current gamestate
|
|
// otherwise we end up in an infinite loop of performing the switching
|
|
// action.
|
|
mLastGameState = mGameState;
|
|
}
|
|
|
|
unsigned int Model::InitLevelList () {
|
|
std::string level_dir_name = Engine::GetResourceFullPath("/data/levels/");
|
|
Engine::LogDebug ("Searching for levels in %s", level_dir_name.c_str());
|
|
|
|
mLevelList.clear();
|
|
|
|
boost::filesystem::path level_dir(level_dir_name);
|
|
|
|
if (!boost::filesystem::exists(level_dir)) {
|
|
Engine::LogError ("Could not init level list: \todo %s does not exist!");
|
|
}
|
|
|
|
if (!boost::filesystem::is_directory(level_dir)) {
|
|
Engine::LogError ("Could not init level list: \todo %s is not a directory!");
|
|
}
|
|
|
|
boost::filesystem::directory_iterator end_iter;
|
|
for (boost::filesystem::directory_iterator dir_iter(level_dir);
|
|
dir_iter != end_iter;
|
|
++dir_iter) {
|
|
if (boost::filesystem::is_regular_file (dir_iter->status())) {
|
|
std::string level_relative_path (level_dir_name);
|
|
level_relative_path += dir_iter->path().filename();
|
|
mLevelList.push_back (level_relative_path);
|
|
Engine::LogDebug ("Found level %s", mLevelList[mLevelList.size()-1].c_str());
|
|
}
|
|
}
|
|
|
|
std::sort(mLevelList.begin(), mLevelList.end());
|
|
|
|
return mLevelList.size();
|
|
}
|
|
|
|
void Model::ParseHighscoreStream (std::istream &highscore_stream) {
|
|
std::string line;
|
|
|
|
while (getline(highscore_stream, line)) {
|
|
std::string name;
|
|
unsigned int points;
|
|
|
|
std::string::size_type delimiter = line.find ('\t');
|
|
if (delimiter == std::string::npos)
|
|
break;
|
|
|
|
name = line.substr(0, delimiter);
|
|
|
|
std::istringstream points_stream(line.substr(delimiter + 1, line.size()));
|
|
points_stream >> points;
|
|
|
|
Engine::LogDebug ("Read Highscore Entry Name: %s Points: %d", name.c_str(), points);
|
|
AddLocalHighscoreEntry (name, points);
|
|
}
|
|
}
|
|
|
|
void Model::LoadHighscoreList () {
|
|
mHighscoreList.clear();
|
|
|
|
if (mHighscoreList.size() < 10) {
|
|
AddLocalHighscoreEntry ("Imperator", 1000000);
|
|
AddLocalHighscoreEntry ("Darth Vader", 800000);
|
|
AddLocalHighscoreEntry ("Luke Skywalker", 600000);
|
|
AddLocalHighscoreEntry ("Han Solo", 400000);
|
|
AddLocalHighscoreEntry ("Princess Leia", 200000);
|
|
AddLocalHighscoreEntry ("C3PO", 100000);
|
|
AddLocalHighscoreEntry ("R2-D2", 50000);
|
|
AddLocalHighscoreEntry ("Chewy", 10000);
|
|
AddLocalHighscoreEntry ("Mr. Ewok", 5000);
|
|
AddLocalHighscoreEntry ("Jabba the Hutt", 1000);
|
|
}
|
|
|
|
mHighscoreIsOnline = false;
|
|
|
|
if (Model::UseServerHighscore.GetBoolValue()) {
|
|
Engine::LogDebug ("Retrieving Highscore from server");
|
|
std::stringstream global_highscore_stream;
|
|
|
|
if (PullGlobalHighscore(global_highscore_stream)) {
|
|
ParseHighscoreStream(global_highscore_stream);
|
|
mHighscoreIsOnline = true;
|
|
return;
|
|
}
|
|
|
|
Engine::LogMessage ("Could not load highscore from server, falling back to local.");
|
|
}
|
|
|
|
LoadLocalHighscoreList();
|
|
SaveLocalHighscoreList();
|
|
}
|
|
|
|
void Model::LoadLocalHighscoreList () {
|
|
std::string highscore_filename = Engine::GetUserDirFullPath("/highscore.dat").c_str();
|
|
boost::filesystem::path highscore_file(highscore_filename);
|
|
|
|
// if the file does not exist, we create it and write standard values into
|
|
// it.
|
|
if (!boost::filesystem::exists(highscore_file)) {
|
|
Engine::LogDebug ("Local highscore file not found!");
|
|
return;
|
|
}
|
|
|
|
if (!boost::filesystem::is_regular_file(highscore_file)) {
|
|
Engine::LogError ("Could not load highscore file: %s is not a regular file!", highscore_file.filename().c_str());
|
|
}
|
|
|
|
Engine::LogDebug ("Loading highscore file '%s'", highscore_filename.c_str());
|
|
|
|
std::ifstream score_stream (highscore_filename.c_str());
|
|
ParseHighscoreStream (score_stream);
|
|
|
|
score_stream.close();
|
|
}
|
|
|
|
void Model::SaveLocalHighscoreList () {
|
|
Engine::LogDebug ("Saving local highscore file");
|
|
std::ofstream highscore_file (Engine::GetUserDirFullPath("/highscore.dat").c_str());
|
|
|
|
std::list<HighscoreEntry>::iterator iter = mHighscoreList.begin();
|
|
|
|
while (iter != mHighscoreList.end()) {
|
|
highscore_file << iter->name << "\t" << iter->points << std::endl;
|
|
iter++;
|
|
}
|
|
|
|
highscore_file.close();
|
|
}
|
|
|
|
bool highscore_cmp (Model::HighscoreEntry a, Model::HighscoreEntry b) {
|
|
return a.points > b.points;
|
|
}
|
|
|
|
/** \brief Addes an entry to the highscore list and takes care that the list stays valid
|
|
*
|
|
* \TODO Re-think usage of mNewestHighscoreEntryIndex variable in this function
|
|
*/
|
|
unsigned int Model::AddLocalHighscoreEntry(const std::string &name, const unsigned int points) {
|
|
HighscoreEntry entry;
|
|
entry.name = name;
|
|
entry.points = points;
|
|
|
|
Engine::LogDebug ("Adding entry %s points = %d", name.c_str(), points);
|
|
|
|
unsigned int counter = 0;
|
|
std::list<HighscoreEntry>::iterator iter = mHighscoreList.begin();
|
|
while (iter != mHighscoreList.end()) {
|
|
if (name == iter->name && points == iter->points)
|
|
break;
|
|
|
|
if (points >= iter->points) {
|
|
mHighscoreList.insert (iter, entry);
|
|
mNewestHighscoreEntryIndex = counter;
|
|
break;
|
|
}
|
|
iter++;
|
|
counter ++;
|
|
}
|
|
|
|
if (mHighscoreList.size() < 10) {
|
|
mHighscoreList.push_back(entry);
|
|
mNewestHighscoreEntryIndex = mHighscoreList.size();
|
|
return mHighscoreList.size();
|
|
}
|
|
|
|
while (mHighscoreList.size() > 10) {
|
|
mHighscoreList.pop_back();
|
|
}
|
|
|
|
if (counter < 10)
|
|
return counter;
|
|
|
|
mNewestHighscoreEntryIndex = 99999;
|
|
|
|
return 99999;
|
|
}
|
|
|
|
bool Model::PullGlobalHighscore(std::stringstream &highscore_stream) {
|
|
highscore_stream.str("");
|
|
|
|
IPaddress server_address;
|
|
|
|
if (SDLNet_ResolveHost (&server_address, Model::HighscoreServerName.GetStringValue().c_str(), 80) == -1) {
|
|
Engine::LogWarning ("Could not retrieve global highscore list: host name resolution for server %s failed!",
|
|
Model::HighscoreServerName.GetStringValue().c_str());
|
|
return false;
|
|
}
|
|
|
|
char ip_address_char[4];
|
|
memcpy (&ip_address_char[0], &server_address.host, sizeof(char) * 4);
|
|
|
|
int ip_address[4];
|
|
ip_address[0] = static_cast<int>(ip_address_char[0]);
|
|
ip_address[1] = static_cast<int>(ip_address_char[1]);
|
|
ip_address[2] = static_cast<int>(ip_address_char[2]);
|
|
ip_address[3] = static_cast<int>(ip_address_char[3]);
|
|
|
|
if (ip_address[0] < 0)
|
|
ip_address[0] += 256;
|
|
if (ip_address[1] < 0)
|
|
ip_address[1] += 256;
|
|
if (ip_address[2] < 0)
|
|
ip_address[2] += 256;
|
|
if (ip_address[3] < 0)
|
|
ip_address[3] += 256;
|
|
|
|
Engine::LogDebug ("Pulling global highscore from server %s (%d.%d.%d.%d)",
|
|
Model::HighscoreServerName.GetStringValue().c_str(),
|
|
ip_address[0],
|
|
ip_address[1],
|
|
ip_address[2],
|
|
ip_address[3]
|
|
);
|
|
|
|
TCPsocket server_socket = SDLNet_TCP_Open (&server_address);
|
|
if (!server_socket) {
|
|
Engine::LogError ("SDL_net tcp open: %s", SDLNet_GetError());
|
|
return false;
|
|
}
|
|
|
|
std::string http_query_string;
|
|
http_query_string = std::string ("GET ") + Model::HighscoreServerPath.GetStringValue() + std::string(" HTTP/1.1\r\nHost: asteroids.fysx.org\r\nConnection: close\r\n\r\n");
|
|
|
|
int bytes_sent;
|
|
bytes_sent = SDLNet_TCP_Send (server_socket, http_query_string.c_str(), http_query_string.size());
|
|
|
|
if (bytes_sent != http_query_string.size()) {
|
|
Engine::LogError ("SDL_net tcp send: %s", SDLNet_GetError());
|
|
return false;
|
|
}
|
|
|
|
char receive_buffer[255];
|
|
std::string http_result;
|
|
|
|
bool receiving = true;
|
|
while (receiving) {
|
|
// reset the buffer
|
|
bzero (&receive_buffer[0], 255);
|
|
|
|
// read the data
|
|
int received_bytes = SDLNet_TCP_Recv (server_socket, receive_buffer, 255);
|
|
|
|
if (received_bytes <= 0) {
|
|
receiving = false;
|
|
}
|
|
|
|
http_result.append (receive_buffer, received_bytes);
|
|
}
|
|
|
|
// we have to strip the whitespaces twice to cut out the content
|
|
http_result = strip_whitespaces (http_result);
|
|
http_result = http_result.substr (http_result.find ("\n\r"), http_result.size());
|
|
http_result = strip_whitespaces (http_result);
|
|
|
|
Engine::LogDebug ("Received Highscore: %s", http_result.c_str());
|
|
|
|
SDLNet_TCP_Close (server_socket);
|
|
|
|
highscore_stream.str(http_result);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Model::SubmitGlobalHigscoreEntry (std::string name, const unsigned int points) {
|
|
IPaddress server_address;
|
|
|
|
if (SDLNet_ResolveHost (&server_address, Model::HighscoreServerName.GetStringValue().c_str(), 80) == -1) {
|
|
Engine::LogWarning ("Cannot submit highscore entry: host name resolution for server %s failed!",
|
|
Model::HighscoreServerName.GetStringValue().c_str());
|
|
return false;
|
|
}
|
|
|
|
char ip_address_char[4];
|
|
memcpy (&ip_address_char[0], &server_address.host, sizeof(char) * 4);
|
|
|
|
int ip_address[4];
|
|
ip_address[0] = static_cast<int>(ip_address_char[0]);
|
|
ip_address[1] = static_cast<int>(ip_address_char[1]);
|
|
ip_address[2] = static_cast<int>(ip_address_char[2]);
|
|
ip_address[3] = static_cast<int>(ip_address_char[3]);
|
|
|
|
if (ip_address[0] < 0)
|
|
ip_address[0] += 256;
|
|
if (ip_address[1] < 0)
|
|
ip_address[1] += 256;
|
|
if (ip_address[2] < 0)
|
|
ip_address[2] += 256;
|
|
if (ip_address[3] < 0)
|
|
ip_address[3] += 256;
|
|
|
|
std::string escaped_name = name;
|
|
|
|
while (escaped_name.find(" ") != std::string::npos) {
|
|
escaped_name.replace (escaped_name.find(" "), 1, "%20");
|
|
}
|
|
|
|
Engine::LogDebug ("Escaped name (%u) = '%s'", escaped_name.size(), escaped_name.c_str());
|
|
|
|
Engine::LogDebug ("Submitting highscore player_name='%s' score_value='%d' to server %s (%d.%d.%d.%d)",
|
|
name.c_str(),
|
|
points,
|
|
Model::HighscoreServerName.GetStringValue().c_str(),
|
|
ip_address[0],
|
|
ip_address[1],
|
|
ip_address[2],
|
|
ip_address[3]
|
|
);
|
|
|
|
TCPsocket server_socket = SDLNet_TCP_Open (&server_address);
|
|
if (!server_socket) {
|
|
Engine::LogError ("SDL_net tcp open: %s", SDLNet_GetError());
|
|
return false;
|
|
}
|
|
|
|
std::stringstream points_stringstream;
|
|
points_stringstream << points;
|
|
|
|
std::stringstream hash_input;
|
|
hash_input << name << ":" << points << ":" << sha256_hash ("asteroids rule");
|
|
|
|
std::string key = sha256_hash (hash_input.str());
|
|
|
|
std::string http_query_string;
|
|
http_query_string = std::string ("GET ")
|
|
+ Model::HighscoreServerPath.GetStringValue()
|
|
+ std::string("&player_name=") + escaped_name
|
|
+ std::string("&score_value=") + points_stringstream.str()
|
|
+ std::string("&key=") + key
|
|
+ std::string(" HTTP/1.1\r\nHost: asteroids.fysx.org\r\nConnection: close\r\n\r\n");
|
|
|
|
Engine::LogDebug ("http submitting query: %s", http_query_string.c_str());
|
|
|
|
int bytes_sent;
|
|
bytes_sent = SDLNet_TCP_Send (server_socket, http_query_string.c_str(), http_query_string.size());
|
|
|
|
if (bytes_sent != http_query_string.size()) {
|
|
Engine::LogError ("SDL_net tcp send: %s", SDLNet_GetError());
|
|
return false;
|
|
}
|
|
|
|
char receive_buffer[255];
|
|
std::string http_result;
|
|
|
|
bool receiving = true;
|
|
while (receiving) {
|
|
// reset the buffer
|
|
bzero (&receive_buffer[0], 255);
|
|
|
|
// read the data
|
|
int received_bytes = SDLNet_TCP_Recv (server_socket, receive_buffer, 255);
|
|
|
|
if (received_bytes <= 0) {
|
|
receiving = false;
|
|
}
|
|
|
|
http_result.append (receive_buffer, received_bytes);
|
|
}
|
|
|
|
// we have to strip the whitespaces twice to cut out the content
|
|
http_result = strip_whitespaces (http_result);
|
|
http_result = http_result.substr (http_result.find ("\n\r"), http_result.size());
|
|
http_result = strip_whitespaces (http_result);
|
|
|
|
SDLNet_TCP_Close (server_socket);
|
|
|
|
if (http_result == "OK") {
|
|
Engine::LogDebug ("Submission successful: %s", http_result.c_str());
|
|
LoadHighscoreList();
|
|
return true;
|
|
} else {
|
|
Engine::LogMessage ("Submission unsuccessful: %s", http_result.c_str());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Model::SubmitHighscoreEntry (const std::string &name, const unsigned int points) {
|
|
if (Model::UseServerHighscore.GetBoolValue()) {
|
|
if (SubmitGlobalHigscoreEntry (name, points)) {
|
|
return;
|
|
}
|
|
|
|
Engine::LogWarning ("Could not send highscore entry to server!");
|
|
}
|
|
|
|
// if it did not succeed or we do not want to use the global highscore
|
|
// fall back to the local highscore system.
|
|
AddLocalHighscoreEntry (name, points);
|
|
SaveLocalHighscoreList();
|
|
}
|
|
|
|
int Model::DoLoadLevel (const char* filename) {
|
|
Engine::LogMessage ("Loading level from %s", filename);
|
|
std::fstream level_file (filename, std::ios::in);
|
|
|
|
if (!level_file) {
|
|
Engine::LogError ("Unable to open file %s for reading!", filename);
|
|
exit (-1);
|
|
}
|
|
|
|
ClearEntities();
|
|
mAsteroids.clear();
|
|
|
|
std::string entity_type_str;
|
|
|
|
int entity_count = 0;
|
|
SetLevelTitle ("");
|
|
SetLevelAuthor ("");
|
|
mLevelTimeSeconds = 0.f;
|
|
mLevelParTimeSeconds = -1.f;
|
|
mLevelPoints = 0;
|
|
mLevelTimeBonusPoints = 0;
|
|
|
|
while (level_file >> entity_type_str) {
|
|
if (entity_type_str[0] == '#') {
|
|
getline (level_file, entity_type_str);
|
|
Engine::LogDebug ("Read Comment: %s", entity_type_str.c_str());
|
|
continue;
|
|
} else if (entity_type_str == "Title") {
|
|
std::string level_title;
|
|
|
|
getline (level_file, level_title);
|
|
level_title = strip_whitespaces (level_title);
|
|
SetLevelTitle (level_title);
|
|
continue;
|
|
} else if (entity_type_str == "Author") {
|
|
std::string level_author;
|
|
|
|
getline (level_file, level_author);
|
|
level_author = strip_whitespaces (level_author);
|
|
SetLevelAuthor (level_author);
|
|
continue;
|
|
} else if (entity_type_str == "ParTime") {
|
|
std::string partime_string;
|
|
|
|
getline (level_file, partime_string);
|
|
partime_string = strip_whitespaces (partime_string);
|
|
std::stringstream partime_stream (partime_string);
|
|
|
|
float partime = -1.;
|
|
partime_stream >> partime;
|
|
|
|
if (partime > 0.)
|
|
mLevelParTimeSeconds = partime;
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
GameEntityType entity_type = GameEntityTypeUnknown;
|
|
|
|
if (entity_type_str == "GameEntityTypeShip")
|
|
entity_type = GameEntityTypeShip;
|
|
else if (entity_type_str == "GameEntityTypeAsteroid")
|
|
entity_type = GameEntityTypeAsteroid;
|
|
else {
|
|
Engine::LogError ("Unknown Entity type: %s", entity_type_str.c_str());
|
|
exit (-1);
|
|
}
|
|
|
|
Engine::EntityBase* entity = CreateEntity (entity_type);
|
|
|
|
bool is_player;
|
|
level_file >> is_player;
|
|
|
|
if (is_player) {
|
|
mPlayerEntityId = entity->mId;
|
|
Engine::LogDebug ("Entity with id '%d' is player", entity->mId);
|
|
}
|
|
|
|
level_file >> entity->mPhysicState->mPosition[0];
|
|
level_file >> entity->mPhysicState->mPosition[1];
|
|
level_file >> entity->mPhysicState->mPosition[2];
|
|
|
|
level_file >> entity->mPhysicState->mOrientation[0];
|
|
level_file >> entity->mPhysicState->mOrientation[1];
|
|
level_file >> entity->mPhysicState->mOrientation[2];
|
|
|
|
level_file >> entity->mPhysicState->mVelocity[0];
|
|
level_file >> entity->mPhysicState->mVelocity[1];
|
|
level_file >> entity->mPhysicState->mVelocity[2];
|
|
|
|
level_file >> entity->mPhysicState->mAngleVelocity;
|
|
|
|
entity_count ++;
|
|
}
|
|
|
|
if (mLevelParTimeSeconds == -1.f) {
|
|
Engine::LogWarning ("Level %s has no par time set. Setting to 120s.", filename);
|
|
mLevelParTimeSeconds = 120.f;
|
|
}
|
|
|
|
level_file.close();
|
|
|
|
Engine::LogDebug ("%d Entities loaded!", mEntities.size());
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Model::DoSaveLevel (const char* filename) {
|
|
Engine::LogMessage ("Saving level to %s", filename);
|
|
std::fstream level_file (filename, std::ios::out);
|
|
|
|
if (!level_file) {
|
|
Engine::LogError ("Unable to open file %s for writing!", filename);
|
|
exit (-1);
|
|
}
|
|
|
|
level_file << "# Format" << std::endl;
|
|
level_file << "# <Type> <player?> <xpos> <ypos> <zpos> <zrot> <yrot> <xrot> <xvel> <yvel> <zvel> <rotvel>" << std::endl;
|
|
|
|
if (GetLevelTitle() != "") {
|
|
level_file << "Title " << mLevelTitle << std::endl;
|
|
}
|
|
if (GetLevelAuthor() != "") {
|
|
level_file << "Author " << mLevelAuthor << std::endl;
|
|
}
|
|
|
|
std::map<unsigned int, Engine::EntityBase*>::iterator iter = mEntities.begin();
|
|
unsigned int player_id = GetPlayerEntityId();
|
|
|
|
for (iter = mEntities.begin(); iter != mEntities.end(); iter++) {
|
|
Engine::EntityBase* game_entity = iter->second;
|
|
|
|
level_file << GetStringGameEntityType((GameEntityType)game_entity->mType) << "\t"
|
|
// this stores the player id
|
|
<< (game_entity->mId == player_id) << "\t"
|
|
<< game_entity->mPhysicState->mPosition[0] << "\t"
|
|
<< game_entity->mPhysicState->mPosition[1] << "\t"
|
|
<< game_entity->mPhysicState->mPosition[2] << "\t"
|
|
<< game_entity->mPhysicState->mOrientation[0] << "\t"
|
|
<< game_entity->mPhysicState->mOrientation[1] << "\t"
|
|
<< game_entity->mPhysicState->mOrientation[2] << "\t"
|
|
<< game_entity->mPhysicState->mVelocity[0] << "\t"
|
|
<< game_entity->mPhysicState->mVelocity[1] << "\t"
|
|
<< game_entity->mPhysicState->mVelocity[2] << "\t"
|
|
<< game_entity->mPhysicState->mAngleVelocity << "\t"
|
|
<< std::endl;
|
|
}
|
|
|
|
level_file.close();
|
|
return 0;
|
|
}
|
|
|
|
void Model::LoadLevelFromIndex (unsigned int level_index) {
|
|
if (level_index < mLevelList.size()) {
|
|
mCurrentLevelIndex = level_index;
|
|
DoLoadLevel(mLevelList[mCurrentLevelIndex].c_str());
|
|
}
|
|
}
|
|
|
|
void Model::ReloadLevel () {
|
|
Engine::LogDebug ("Reloading level %d", mCurrentLevelIndex + 1);
|
|
|
|
if (mCurrentLevelIndex < 0 || mCurrentLevelIndex >= mLevelList.size())
|
|
Engine::LogError ("Invalid level index: %u", mCurrentLevelIndex);
|
|
|
|
DoLoadLevel(mLevelList[mCurrentLevelIndex].c_str());
|
|
}
|
|
|
|
void Model::ProceedToNextLevel () {
|
|
Engine::LogDebug ("Proceeding to next level %d", mCurrentLevelIndex + 1);
|
|
mCurrentLevelIndex++;
|
|
|
|
if (mCurrentLevelIndex == mLevelList.size()) {
|
|
Engine::EventBasePtr gameover_event (new Engine::EventBase());
|
|
gameover_event->mEventType = EventGameOver;
|
|
QueueEvent (gameover_event);
|
|
} else {
|
|
DoLoadLevel(mLevelList[mCurrentLevelIndex].c_str());
|
|
}
|
|
}
|
|
|
|
void Model::SetGameState (const unsigned int &state) {
|
|
mLastGameState = mGameState;
|
|
mGameState = state;
|
|
}
|
|
|
|
bool Model::OnGameOver() {
|
|
Engine::LogDebug ("Points = %d lowest = %d", mPoints, mHighscoreList.back().points );
|
|
|
|
if (mPoints > mHighscoreList.back().points) {
|
|
Engine::LogMessage ("New Highscore!");
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
void Model::OnNewGame() {
|
|
ClearEntities();
|
|
|
|
mNewestHighscoreEntryIndex = std::numeric_limits<unsigned int>::max();
|
|
mPlayerLives = 3;
|
|
mCurrentLevelIndex = 0;
|
|
mPoints = 0;
|
|
mLevelPoints = 0;
|
|
|
|
DoLoadLevel (mLevelList[mCurrentLevelIndex].c_str());
|
|
}
|
|
|
|
void Model::OnShipExplode () {
|
|
Engine::PlaySound(Engine::GetResourceFullPath("/data/sounds/ship_destroyed.wav"));
|
|
|
|
if (GetPlayerEntityId() == Engine::NullEntityId) {
|
|
return;
|
|
}
|
|
|
|
mPlayerLives --;
|
|
|
|
if (mPlayerLives == 0) {
|
|
Engine::EventBasePtr gameover_event (new Engine::EventBase());
|
|
gameover_event->mEventType = EventGameOver;
|
|
QueueEvent (gameover_event);
|
|
} else {
|
|
Engine::EventBasePtr playerdied_event (new Engine::EventBase());
|
|
playerdied_event->mEventType = EventPlayerDied;
|
|
QueueEvent (playerdied_event);
|
|
}
|
|
}
|
|
|
|
void Model::OnLevelComplete() {
|
|
// calculate the bonus points
|
|
float level_time = roundf(mLevelTimeSeconds);
|
|
float level_par_time = roundf(mLevelParTimeSeconds);
|
|
int bonus_points = 0;
|
|
|
|
if (level_time <= level_par_time) {
|
|
// secret time bonus formula
|
|
float a = 2,
|
|
b = -4.2,
|
|
c = 2.2;
|
|
float t_n = mLevelTimeSeconds / mLevelParTimeSeconds;
|
|
float bonus_points_f = roundf ( (a * t_n * t_n + b * t_n + c) * static_cast<float>( mLevelPoints ));
|
|
if (bonus_points_f < 0.)
|
|
bonus_points_f = 0.;
|
|
|
|
// round to the closest decimal number
|
|
bonus_points_f = roundf (bonus_points_f / 10.) * 10. + 100.;
|
|
mLevelTimeBonusPoints = static_cast<unsigned int> (bonus_points_f);
|
|
|
|
Engine::LogDebug("Bonus Points: t_n = %f bonus_points_f = %f bonus_points = %u", t_n, bonus_points_f, mLevelTimeBonusPoints);
|
|
|
|
} else {
|
|
mLevelTimeBonusPoints = 0;
|
|
}
|
|
|
|
mLevelPoints += mLevelTimeBonusPoints;
|
|
mPoints += mLevelPoints;
|
|
}
|
|
|
|
void Model::OnCreateEntity (const int type, const unsigned int id) {
|
|
GameEntityType entity_type = (GameEntityType) type;
|
|
|
|
if (entity_type == GameEntityTypeAsteroid) {
|
|
mAsteroids.push_back (id);
|
|
}
|
|
}
|
|
|
|
void Model::OnKillEntity (const Engine::EntityBase *entity) {
|
|
GameEntityType entity_type = (GameEntityType) entity->mType;
|
|
|
|
if (entity_type == GameEntityTypeAsteroid) {
|
|
Engine::PlaySound(Engine::GetResourceFullPath("/data/sounds/rock_destroyed.wav"));
|
|
|
|
unsigned int i;
|
|
const AsteroidEntity *asteroid = static_cast<const AsteroidEntity*>(entity);
|
|
|
|
if (GetPlayerEntityId() != Engine::NullEntityId) {
|
|
mLevelPoints += (150 + asteroid->mSubAsteroidsCount * 75) * 6;
|
|
}
|
|
|
|
for (i = 0; i < mAsteroids.size(); i++) {
|
|
if (mAsteroids.at(i) == entity->mId) {
|
|
std::vector<unsigned int>::iterator entity_iter = mAsteroids.begin() + i;
|
|
mAsteroids.erase (entity_iter);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mAsteroids.size() == 0) {
|
|
Engine::EventBasePtr level_complete_event (new Engine::EventBase());
|
|
level_complete_event->mEventType = EventLevelComplete;
|
|
|
|
QueueEvent (level_complete_event);
|
|
}
|
|
}
|
|
}
|
|
|
|
float Model::GetWorldWidth () {
|
|
return static_cast<Physics*>(mPhysics)->GetWorldWidth();
|
|
}
|
|
|
|
float Model::GetWorldHeight () {
|
|
return static_cast<Physics*>(mPhysics)->GetWorldHeight();
|
|
}
|
|
|
|
}
|
|
|
|
|