diff --git a/QUESTIONS.md b/QUESTIONS.md index 324831f..620a835 100644 --- a/QUESTIONS.md +++ b/QUESTIONS.md @@ -1,10 +1,5 @@ Questions que je (Thomas Rubini) voudrais poser -- Est-ce que vous préférez l'implémentation orienté objet ou orienté macro pour lire la config ? Pourquoi ? -- Est-ce que traduire les chars A B et C (identifiants des types d'aliens) tirés de la config en valeurs d'enum est une bonne chose, pas important, ou contre-productif ? -- Est-ce que mon implémentation du réseau est bonne ? -- Est-ce mon implémentation du multithreading est bonne ? - - Est-on obligé d'utiliser size_t quand on sait que la taille du vecteur ne dépassera jamais concrètement la taille d'un int (cas précis : taille de 100 maximum, est-on obligé d'utiliser size_t de 8 bytes ?) * Non - Que pensez-vous de la sémantique de déplacement, plutot que la référence constante ? diff --git a/README b/README index dd32c56..3c8b2db 100644 --- a/README +++ b/README @@ -4,6 +4,7 @@ PascalCase pour les noms de classes/structures Nommage en anglais Pas de fonctions de +100 lignes Les guards sont de cette forme : GUARD__H +Lors d'un include, on utilise <> pour la STD, "" pour nos fichiers/MinGL Concernant les const : Afin de limiter l'utilisation du mot const (pour garder une certaine lisibilité du code), les cas suivants n'ont pas besoin d'etre déclarés comme const : diff --git a/headers/configManagement.h b/headers/configManagement.h new file mode 100644 index 0000000..0b2b59e --- /dev/null +++ b/headers/configManagement.h @@ -0,0 +1,50 @@ +/*! + * + * @file configManagement.h + * @author RUBINI Thomas + * @author SIMAILA Djalim + * @date January 2022 + * @version 1.0 + * @brief config parser + * + */ + +#ifndef SPACE_CONFIGMANAGEMENT_H +#define SPACE_CONFIGMANAGEMENT_H + +/* + * This header will only be imported once, but Djalim told me he would hurt me + * if I didn't move it into his own header file instead of the cpp file +*/ + +class ConfigBuilder{ +public: + ConfigData collectedData; + void parseFile(const string& fname); + void readConfig(); + void dumpInternalValues() const; +private: + map internalValues; + + const string& getString(const configKey& key, const string& def) const; + const string& getString(const configKey& key) const; + char getChar(const configKey& key, char def) const; + char getChar(const configKey& key) const; + int getInt(const configKey& key) const; + int getInt(const configKey& key, int def) const; + void getColor(const configKey& key, nsGraphics::RGBAcolor& color, nsGraphics::RGBAcolor& def) const; + void getColor(const configKey& key, nsGraphics::RGBAcolor& color) const; + void getList(const configKey& key, vector& vec) const; + + void readGrid(const configKey& baseKey); + void readPlayer(const configKey& baseKey, PlayerDef&); + void readInvaderType(const configKey& baseKey, InvaderTypeDef&); +}; + +void ConfigBuilder::dumpInternalValues() const { + for(const auto& ite : internalValues){ + cerr << ite.first << " -> " << ite.second << endl; + } +} + +#endif diff --git a/headers/errors.h b/headers/errors.h new file mode 100644 index 0000000..f900184 --- /dev/null +++ b/headers/errors.h @@ -0,0 +1,25 @@ +/*! + * + * @file configData.h + * @author RUBINI Thomas + * @date January 2022 + * @version 1.0 + * @brief Configuration file data storage + * + */ + +#ifndef GUARD_ERRORS_H +#define GUARD_ERRORS_H + +#include +#include + +using namespace std; + +// We keep the same case +class config_error : public runtime_error { +public: + explicit config_error(const string& msg); +}; + +#endif diff --git a/headers/game.h b/headers/game.h index fc3ac27..cc30b0c 100644 --- a/headers/game.h +++ b/headers/game.h @@ -202,37 +202,37 @@ private: void moveTorpedos(); /*! - * @brief - * @return - * @fn + * @brief checks if a missile collides with a player + * @fn checkMissilesAndPlayers(); */ - bool checkMissilesAndPlayers(); + void checkMissilesAndPlayers(); /*! - * @brief - * @return - * @fn + * @brief check if a torpedo collides with an invader + * @return true if there is a collision, false elsewise + * @fn bool checkTorpedosAndInvaders(); */ bool checkTorpedosAndInvaders(); /*! - * @brief - * @return - * @fn + * @brief check if the invaders have reach the players + * @return true if they have reach the player, false elsewise + * @fn bool invadersTouchPlayer() const; */ bool invadersTouchPlayer() const; // god things - + /*! - * + * @brief change god's state to awake + * @fn void awakeGod(); */ void awakeGod(); /*! - * @brief - * @return - * @fn + * @brief make god behave + * @return true if theres no invader left, false elsewise + * @fn bool manageGod(); */ bool manageGod(); @@ -240,28 +240,37 @@ public: // in case someone wants to mess with the code, here's a minimal API, costs nothing to us /*! - * + * @brief constructor for the game class + * @fn Game(); */ Game(); /*! - * + * @brief manages and changes the states of the game + * @fn void managedGames(); */ void managedGames(); /*! - * @brief - * @return - * @fn + * @brief enter the main gameplay game loop + * @return the value of the winners can be the players, the invaders or god + * @fn WinValue enterGameLoop(); */ WinValue enterGameLoop(); /*! - * @brief - * @return - * @fn + * @brief reload the configuration file for a new game + * @return false if an error occured, true elsewise + * @fn bool reloadConfig(); */ bool reloadConfig(); + + /*! + * @brief tells if all players are dead + * @return true if all player are dead, false otherwise + * @fn bool arePlayersDead(); + */ + bool arePlayersDead(); }; #endif \ No newline at end of file diff --git a/headers/god.h b/headers/god.h index 1f4de1e..6fac89f 100644 --- a/headers/god.h +++ b/headers/god.h @@ -6,6 +6,9 @@ * @version 1.0 * @brief Special entity known as "God" * + * Well, i (Djalim) discovered that Thomas put God in the game + * I didnt think it was a good idea but no man can stop Thomas + * */ #ifndef GUARD_GOD_H @@ -15,7 +18,7 @@ #include "invadersGrid.h" /*! - * @brief + * @brief list of all known god states */ enum class GodState{ NONE, @@ -40,18 +43,18 @@ enum class GodState{ /*! * @class God - * @brief + * @brief stores all God's data */ class God{ public: /*! - * @brief + * @brief god's current state */ GodState state; /*! - * @brief + * @brief manage all sorts of things, gods secrets remains unknown */ unsigned counter; @@ -59,34 +62,34 @@ public: // we do not use a Position because it is used for pixel X and Y /*! - * @brief + * @brief x pixel coordinate of the invader thrown by the hand of god */ unsigned thrownInvPosX; /*! - * @brief + * @brief y pixel coordinate of the invader thrown by the hand of god */ unsigned thrownInvPosY; /*! - * @brief + * @brief type of the invader thrown by the hand of god */ InvaderType thrownInvType; /*! - * @brief + * @brief direction of the thrown invader movement */ Position thrownVector; /*! - * @brief + * @brief position of a point for bezier's curve */ Position thrownTransition; /*! - * @brief - * @param[in] screenWidth : - * @return + * @brief give initial the pixel coordinates of god's right hand + * @param[in] screenWidth : width of the screen in pixel + * @return pixel coordinates of the hand * @fn Position getRightHandPos(unsigned screenWidth) const; */ Position getRightHandPos(unsigned screenWidth) const; diff --git a/headers/invadersGrid.h b/headers/invadersGrid.h index 44fa32e..da2d20e 100644 --- a/headers/invadersGrid.h +++ b/headers/invadersGrid.h @@ -53,7 +53,7 @@ public: * @return * @fn */ - unsigned randomValid() const; + unsigned randomValidInv() const; }; // class InvadersColumn diff --git a/headers/scoresManager.h b/headers/scoresManager.h index a353d87..9be7cd9 100644 --- a/headers/scoresManager.h +++ b/headers/scoresManager.h @@ -18,17 +18,62 @@ using namespace std; +/*! + * @struct ScoreLink + * @brief Makes a link between a player username and their score + */ struct ScoreLink{ + + /*! + * @brief player username + */ string name; + + /*! + * @brief player score + */ unsigned score; + + /*! + * @brief constructor of the struct + * @param[in] name : player username + * @param[in] score : player score + * @fn ScoreLink(string name, unsigned score); + */ ScoreLink(string name, unsigned score); }; + +/*! + * @class ScoresManager + * @brief manage the score in the game and inside the score file + */ class ScoresManager { public: + + /*! + * @brief list of pairs of player names and their score + */ vector scores; + + /*! + * @brief add player name and their score in the list of scores + * @param[in] name : player name + * @param[in] score : player score + * @fn void inputScore(string name, unsigned score); + */ void inputScore(string name, unsigned score); + + /*! + * @brief read the score file and put all of its data inside the list of score + * @fn void readFile(); + */ void readFile(); + + /*! + * @brief write the score list into the score file + * @fn void writeFile() const; + */ void writeFile() const; }; diff --git a/headers/utils.h b/headers/utils.h index eb7915b..8d50f19 100644 --- a/headers/utils.h +++ b/headers/utils.h @@ -1,3 +1,13 @@ +/*! + * + * @file utils.h + * @author RUBINI Thomas + * @date January 2022 + * @version 1.0 + * @brief utilies for the game + * + */ + #ifndef GUARD_UTILS_H #define GUARD_UTILS_H @@ -28,11 +38,14 @@ using namespace std; using nsGraphics::RGBAcolor; - +/*! + * @brief list of win values + */ enum class WinValue{ NOBODY, // should never be used PLAYERS, INVADERS, + GOD, }; @@ -42,9 +55,25 @@ typedef unsigned playerID; #define PLAYER2 1 // didn't want to use Position because of the semantic with x and y + +/*! + * @brief tells if 2 lines are colliding in a 1 dimentionnal space + * @param[in] start1 : position of the first point of the first line + * @param[in] end1: posision of the last point of the first line + * @param[in] start2 : position of the first point of the seconde line + * @param[in] end2: posision of the last point of the second line + * @return true if they are +olliding, false elsewise */ bool areLinesColliding(unsigned start1, unsigned end1, unsigned start2, unsigned end2); // change draw position for a specified size (keeps the same center) + +/*! + * @brief change the size of a Position object + * @param[in,out] pos : Position object + * @param[in] sizeFrom: current size of the objet + * @param[in] sizeTo : new size of the object + */ void applyTransformation(Position& pos, unsigned sizeFrom, unsigned sizeTo); #endif \ No newline at end of file diff --git a/src/configManagement.cpp b/src/configManagement.cpp index c93dd60..545d363 100644 --- a/src/configManagement.cpp +++ b/src/configManagement.cpp @@ -11,33 +11,9 @@ #include #include "game.h" +#include "configManagement.h" +#include "errors.h" -// you will MOVE THIS OUT OF THIS FILE - -class ConfigBuilder{ -public: - ConfigData collectedData; - void parseFile(const string& fname); - void readConfig(); - void dumpInternalValues() const; -private: - map internalValues; - const string& getString(const configKey& key) const; - char getChar(const configKey& key) const; - int getInt(const configKey& key) const; - void getColor(const configKey& key, nsGraphics::RGBAcolor&) const; - void getList(const configKey& key, vector&) const; - - void readGrid(const configKey& baseKey); - void readPlayer(const configKey& baseKey, PlayerDef&); - void readInvaderType(const configKey& baseKey, InvaderTypeDef&); -}; - -void ConfigBuilder::dumpInternalValues() const { - for(const auto& ite : internalValues){ - cerr << ite.first << " -> " << ite.second << endl; - } -} void trimSpaces(string& str){ str.erase(0, str.find_first_not_of(' ')); @@ -56,12 +32,12 @@ void sanitizeValue(string& val) { /* * WARNING : This implementation of YAML is not meant to be complete, but to work with our specific needs - * It also can't detect and report errors in a non YAML-compliant file + * It also can't detect and report errors in a non-YAML-compliant file */ void ConfigBuilder::parseFile(const string& fname) { ifstream file(fname); - if(!file.is_open())throw runtime_error("Error while opening config.yml. Check file location ?"); + if(!file.is_open())throw config_error("Error while opening config.yml. Check file location ?"); vector keyParts; unsigned listIndex; @@ -91,7 +67,7 @@ void ConfigBuilder::parseFile(const string& fname) { }else{ match = line.find(':'); - if (match == string::npos)throw runtime_error("Invalid line : " + line); + if (match == string::npos)throw config_error("Invalid line : " + line); string key = line.substr(0, match); string value = line.substr(match + 1); trimSpaces(key); @@ -148,7 +124,7 @@ void ConfigBuilder::readGrid(const configKey& baseKey) { break; } default:{ - throw runtime_error("Invalid invader ID in grid definition : "+ to_string(s[i])); + throw config_error("Invalid invader ID in grid definition : "+ to_string(s[i])); } } } @@ -212,26 +188,60 @@ void ConfigBuilder::readConfig() { getColor("projectiles.torpedos.color", collectedData.torpedosColor); } -int ConfigBuilder::getInt(const configKey& key) const { - return stoi(getString(key)); -} - -char ConfigBuilder::getChar(const configKey& key) const { - return getString(key)[0]; +const string& ConfigBuilder::getString(const configKey& key, const string& def) const { + try{ + return getString(key); + }catch(config_error& e){ + cerr << e.what() << endl; + return def; + } } const string& ConfigBuilder::getString(const configKey& key) const { if(internalValues.contains(key)){ return internalValues.at(key); }else{ - throw runtime_error("Non-existent key requested : "+key); + throw config_error("Non-existent key requested : "+key); + } +} + +int ConfigBuilder::getInt(const configKey& key) const { + try{ + return stoi(getString(key)); + }catch(invalid_argument& e){ + cerr << e.what() << endl; + throw config_error("Invalid int data for key "+key+" : |"+getString(key)+"|"); + } +} + +int ConfigBuilder::getInt(const configKey& key, int def) const { + try{ + return getInt(key); + }catch(config_error& e){ + cerr << e.what() << endl; + return def; + } +} + +char ConfigBuilder::getChar(const configKey& key) const { + string s = getString(key); + if(s.size()!=1)throw config_error("Invalid char data for key "+key+" : |"+s+"|"); + return s[0]; +} + +char ConfigBuilder::getChar(const configKey& key, char def) const { + try{ + return getChar(key); + }catch(config_error& e){ + cerr << e.what() << endl; + return def; } } void ConfigBuilder::getList(const configKey& key, vector& toPopulate) const { size_t i=0; string fullKey = key+".0"; - if(!internalValues.contains(fullKey))throw runtime_error("Non-existent list key requested : "+key); + if(!internalValues.contains(fullKey))throw config_error("Non-existent list key requested : "+key); do{ toPopulate.push_back(internalValues.at(fullKey)); @@ -259,14 +269,10 @@ void ConfigBuilder::getColor(const configKey& key, nsGraphics::RGBAcolor& color) else if (colorStr == "purple")color = nsGraphics::KPurple; else if (colorStr == "teal")color = nsGraphics::KTeal; else if (colorStr == "navy")color = nsGraphics::KNavy; - else throw runtime_error("Invalid color string : "+colorStr); + else throw config_error("Invalid color string : "+colorStr); } -/** - * - * @return false if there was an error - */ bool Game::reloadConfig() { map strValues; ConfigBuilder builder; @@ -275,7 +281,7 @@ bool Game::reloadConfig() { builder.parseFile("config.yml"); parsed = true; builder.readConfig(); - }catch(runtime_error& e){ + }catch(config_error& e){ if(parsed)cerr << "An error occured while reading the configuration :" << endl; else cerr << "An error occured while parsing the configuration :" << endl; cerr << e.what() << endl; diff --git a/src/errors.cpp b/src/errors.cpp new file mode 100644 index 0000000..d266781 --- /dev/null +++ b/src/errors.cpp @@ -0,0 +1,5 @@ +#include "errors.h" + +config_error::config_error(const string& msg) : runtime_error(msg) { + +} \ No newline at end of file diff --git a/src/game/gameBasics.cpp b/src/game/gameBasics.cpp index 334aa10..61e8889 100644 --- a/src/game/gameBasics.cpp +++ b/src/game/gameBasics.cpp @@ -60,7 +60,6 @@ void Game::managedGames() { // we assume the game has been played before, and so we need to clean used members void Game::initGame(){ grid = confData.grid; // will copy the grid - updateColumns(); // Would have liked to to that in the "config grid", but.. I'm lazy // we re-construct players objects, we don't have to clear all members and can rely on the construction value (set in .h file) players.clear(); @@ -114,16 +113,19 @@ WinValue Game::enterGameLoop(){ // returns when game is finished } } - manageGod(); + if(manageGod())return WinValue::PLAYERS; + if(arePlayersDead())return WinValue::GOD; moveMissiles(); remCollidingProjectiles(); moveTorpedos(); remCollidingProjectiles(); - if(checkMissilesAndPlayers())return WinValue::INVADERS; + checkMissilesAndPlayers(); if(checkTorpedosAndInvaders())return WinValue::PLAYERS; + if(arePlayersDead())return WinValue::INVADERS; + displayAll(fps); pm.endFrame(); @@ -146,3 +148,10 @@ WinValue Game::enterGameLoop(){ // returns when game is finished Position Game::invIndexToPos(unsigned x, unsigned y) const { return basePos+Position(INV_GET_POS(x), INV_GET_POS(y)); } + +bool Game::arePlayersDead() { + for(Player& p : players){ + if(!p.isEliminated())return false; + } + return true; +} diff --git a/src/game/gameManagers.cpp b/src/game/gameManagers.cpp index 314dc5e..dc356e6 100644 --- a/src/game/gameManagers.cpp +++ b/src/game/gameManagers.cpp @@ -48,10 +48,6 @@ void Game::managePlayers(){ for(unsigned i=0;i