From c5f08c8bcc509699e9c13813a85776451371cb9c Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 9 Jan 2022 00:35:09 +0100 Subject: [PATCH] beta heart system --- config.yml | 1 + headers/configData.h | 1 + headers/game.h | 5 +-- headers/playMode.h | 1 - headers/player.h | 5 ++- headers/projectiles.h | 4 +-- headers/scoresManager.h | 8 ++--- headers/utils.h | 3 ++ src/configManagement.cpp | 3 +- src/game/display.cpp | 22 ++++++------- src/game/gameBasics.cpp | 33 ++++++++++++-------- src/game/gameManagers.cpp | 66 ++++++++++++++++++--------------------- src/game/godThings.cpp | 20 +++++++----- src/player.cpp | 17 ++++++++-- src/projectiles.cpp | 2 +- src/scoresManager.cpp | 2 +- 16 files changed, 111 insertions(+), 82 deletions(-) diff --git a/config.yml b/config.yml index 17bc482..92b8e63 100644 --- a/config.yml +++ b/config.yml @@ -8,6 +8,7 @@ players: startXPosition: 50 fireCooldown: 10 speed: 20 + lives: 3 user1: color: red keys: diff --git a/headers/configData.h b/headers/configData.h index 5b6725b..7fb1f2f 100644 --- a/headers/configData.h +++ b/headers/configData.h @@ -20,6 +20,7 @@ struct ConfigData { unsigned playersSpeed; unsigned playersWidth; unsigned playersFireCooldown; + unsigned playersLives; vector playerDefs; diff --git a/headers/game.h b/headers/game.h index 50ac457..192f3c0 100644 --- a/headers/game.h +++ b/headers/game.h @@ -28,7 +28,7 @@ private: bool direction = true; vector missiles; - vector torpedos; + vector torpedos; PlayMode playMode; vector players; @@ -37,6 +37,7 @@ private: unsigned fireCooldown=120; // basic methods + void initGame(); bool updateColumns(); void handleScoreSaving(); Position invIndexToPos(unsigned x, unsigned y) const; @@ -69,7 +70,7 @@ public: // in case someone wants to mess with the code, here's a minimal API, costs nothing to us Game(); void managedGames(); - WinValue playGame(); + WinValue enterGameLoop(); bool reloadConfig(); }; diff --git a/headers/playMode.h b/headers/playMode.h index 39e335e..771c05b 100644 --- a/headers/playMode.h +++ b/headers/playMode.h @@ -5,7 +5,6 @@ enum class PlayMode { NONE, SINGLE, TWO_LOCAL, - TWO_TCPIP, EXIT, }; diff --git a/headers/player.h b/headers/player.h index 4b90cc1..cd92153 100644 --- a/headers/player.h +++ b/headers/player.h @@ -11,7 +11,10 @@ struct Player{ unsigned fireCooldown=0; // TODO remove ? - bool isEliminated(); + bool hasDeathAnimation() const; + bool isEliminated() const; + bool isPlaying() const; + void damage(); }; #endif \ No newline at end of file diff --git a/headers/projectiles.h b/headers/projectiles.h index 2d51980..f76e9e6 100644 --- a/headers/projectiles.h +++ b/headers/projectiles.h @@ -5,10 +5,10 @@ typedef Position missile; -class torpedo : public Position { +class Torpedo : public Position { public: playerID owner; - torpedo(int x, int y, playerID owner); + Torpedo(int x, int y, playerID owner); }; #endif diff --git a/headers/scoresManager.h b/headers/scoresManager.h index d026c96..9b9ef5f 100644 --- a/headers/scoresManager.h +++ b/headers/scoresManager.h @@ -1,16 +1,16 @@ #ifndef GUARD_SCORESMANAGER_H #define GUARD_SCORESMANAGER_H -#include -#include -#include +#include +#include +#include +#include "utils.h" using namespace std; struct ScoreLink{ string name; unsigned score; - ScoreLink() = default; ScoreLink(const string& name, unsigned score); }; diff --git a/headers/utils.h b/headers/utils.h index ad98596..aa28428 100644 --- a/headers/utils.h +++ b/headers/utils.h @@ -9,6 +9,9 @@ #define PROJ_LENGTH_FACTOR 2 // TODO utiliser ca de partout +/* Copy constructuor and assignement are disabled in most of + * our classes so we're sure we can't accidentally copy players + * (We need to explicitly specify the default constructor)*/ #define INV_GET_POS(i) confData.invadersSize*(i)+confData.invadersDistance*(i) diff --git a/src/configManagement.cpp b/src/configManagement.cpp index e621bc7..640ab45 100644 --- a/src/configManagement.cpp +++ b/src/configManagement.cpp @@ -169,6 +169,7 @@ void ConfigBuilder::readConfig() { collectedData.startXPosition = getInt("players.startXPosition"); collectedData.playersSpeed = getInt("players.speed"); collectedData.playersFireCooldown = getInt("players.fireCooldown"); + collectedData.playersLives = getInt("players.lives"); // the scalability behind the vector of players is only an illusion, because we force player count to be 1 or 2 // It was done so the 2+ players implementation could be easier in the future, if wanted @@ -272,6 +273,6 @@ bool Game::reloadConfig() { cerr << "(The old configuration was kept in memory)" << endl; return false; } - confData = builder.collectedData; + confData = move(builder.collectedData); return true; } diff --git a/src/game/display.cpp b/src/game/display.cpp index 49313cd..482adfc 100644 --- a/src/game/display.cpp +++ b/src/game/display.cpp @@ -19,7 +19,7 @@ void Game::displayAll(unsigned fps) const { for(const missile& miss : missiles){ pm.drawMissile(miss, confData.missilesWidth, confData.missilesColor); } - for(const torpedo& tor : torpedos){ + for(const Torpedo& tor : torpedos){ pm.drawTorpedo(tor, confData.torpedosWidth, confData.torpedosColor); } @@ -30,17 +30,16 @@ void Game::displayAll(unsigned fps) const { for(size_t i=0;i0){ - + if(players[pID].hasDeathAnimation()){ + pm.drawHeart(Position(x, y+players[pID].deathAnimCounter*5)); } } void Game::displayInvader(const Position& pos, InvaderType type) const { if(type==InvaderType::NONE)return; - InvaderTypeDef invDef = confData.invadersDef.at(type); + const InvaderTypeDef& invDef = confData.invadersDef.at(type); switch(type){ case InvaderType::TYPEA:{ pm.drawInvaderA(pos, confData.invadersSize, invDef.color); @@ -74,7 +73,6 @@ void Game::displayInvader(const Position& pos, InvaderType type) const { } } - void applyBezier(Position& pos, const Position& point, const double percent) { pos += (point-pos)*percent; } diff --git a/src/game/gameBasics.cpp b/src/game/gameBasics.cpp index d408e63..a46d107 100644 --- a/src/game/gameBasics.cpp +++ b/src/game/gameBasics.cpp @@ -55,7 +55,8 @@ void Game::managedGames() { if(playMode==PlayMode::NONE){ playMode = pm.showInitialMenu(); }else{ - playGame(); // will read the playMode + initGame(); + enterGameLoop(); // will read the playMode handleScoreSaving(); cout << "END OF GAME" << endl; break; // TODO remove @@ -66,19 +67,13 @@ void Game::managedGames() { } -// TODO maybe directly call theses from pm and do not use getters ? - -/** - * Plays the game, and returns once the game is finished - * - * @return @WinValue::PLAYERS if the players won, @WinValue::INVADERS is the invaders won, WinValue::NOBODY else (also in case of error) - */ -WinValue Game::playGame(){ // returns when game is finished - // INIT - // we assume the game has been played before, and so we need to clean used members +// 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(); if(playMode==PlayMode::SINGLE){ players.resize(1); @@ -89,9 +84,21 @@ WinValue Game::playGame(){ // returns when game is finished } players[0].x = confData.startXPosition; - basePos = Position(200, 0); - // GAMELOOP + for(unsigned i=0;i elim - * lives = 1 && counter == 0 -> play - * lives = 0 && counter == 1 -> anim - * lives = 1 && counter == 1 -> anim - */ - - if(players[pID].deathAnimCounter==0){ - if(players[pID].lives==0)return; - }else{ - ++players[pID].deathAnimCounter; - } - + Player& p = players[pID]; if (ISPRESSED(pID, left)){ - if(players[pID].x < confData.playersSpeed) players[pID].x = 0; - else players[pID].x -= confData.playersSpeed; + if(p.x < confData.playersSpeed) p.x = 0; + else p.x -= confData.playersSpeed; } if (ISPRESSED(pID, right)){ - if(players[pID].x + confData.playersWidth + confData.playersSpeed >= pm.getScreenWidth()) players[pID].x = pm.getScreenWidth() - confData.playersWidth - 1; - else players[pID].x += confData.playersSpeed; + if(p.x + confData.playersWidth + confData.playersSpeed >= pm.getScreenWidth()) p.x = pm.getScreenWidth() - confData.playersWidth - 1; + else p.x += confData.playersSpeed; } - if(players[pID].fireCooldown==0) { - if (ISPRESSED(pID, shoot)) { - torpedos.emplace_back(players[pID].x + confData.playersWidth / 2, pm.getScreenHeight() - PLAYER_HEIGHT, pID); - players[pID].fireCooldown = confData.playersFireCooldown; + + + if(p.hasDeathAnimation()) { + ++p.deathAnimCounter; + if (p.deathAnimCounter == 100) { + p.deathAnimCounter = 0; } - }else --players[pID].fireCooldown; + }else{ + if(p.isEliminated())return; + + if(p.fireCooldown==0) { + if (ISPRESSED(pID, shoot)) { + torpedos.emplace_back(p.x + confData.playersWidth / 2, pm.getScreenHeight() - PLAYER_HEIGHT, pID); + p.fireCooldown = confData.playersFireCooldown; + } + }else --p.fireCooldown; + } } /** Makes the players play once @@ -48,7 +42,7 @@ void Game::managePlayers(){ * @return true if the invaders went down from one line (and we should check lower boundary), else false */ bool Game::manageInvaders(){ - if(grid.size()==0)return false; // If there are no more invaders we don't need to manage them + if(grid.empty())return false; // If there are no more invaders we don't need to manage them // shoot if(fireCooldown==0) { fireCooldown = confData.invadersFireCooldown + rand() % 60; @@ -152,12 +146,14 @@ bool Game::checkMissilesAndPlayers() { if(miss_ite->getY()>=pm.getScreenHeight()-PLAYER_HEIGHT){ // check collision on Y // now check collision on X (with both players) for(Player& p : players){ - if(areLinesColliding( - miss_ite->getX(), miss_ite->getX() + confData.missilesWidth, - p.x, p.x + confData.playersWidth)){ - wasColliding = true; - if(p.deathAnimCounter)p.deathAnimCounter = 1; - // do not break, the second player also deserves to be hit + if(p.isPlaying()){ + if(areLinesColliding( + miss_ite->getX(), miss_ite->getX() + confData.missilesWidth, + p.x, p.x + confData.playersWidth)){ + wasColliding = true; + p.damage(); + // do not break, the second player also deserves to be hit + } } } } @@ -190,7 +186,7 @@ bool Game::checkTorpedosAndInvaders() { InvaderType invType = grid[i][grid[i].size()]; - players[tor_ite->owner].score += confData.invadersDef[invType].points; + players[tor_ite->owner].score += confData.invadersDef.at(invType).points; torpedos.erase(tor_ite); grid[i][alienIndex] = InvaderType::NONE; diff --git a/src/game/godThings.cpp b/src/game/godThings.cpp index c3e1a4b..16a65f7 100644 --- a/src/game/godThings.cpp +++ b/src/game/godThings.cpp @@ -69,6 +69,10 @@ bool Game::manageGod() { playerID target; if (players.size() == 1)target = PLAYER1; // don't want to use random if not needed else target = rand() % players.size(); + /* + * Let's just pretend god is drunk and can fire at a player that have a death animation, because + * honestly at this point I want to re-code the whole game engine to allow a better handling of cases like this... + */ Position playerMiddlePos(players[target].x + confData.playersWidth / 2, pm.getScreenHeight() - PLAYER_HEIGHT / 2); @@ -96,13 +100,15 @@ bool Game::manageGod() { // check player collision } else if (invaderPos.getY() + confData.invadersSize >= pm.getScreenHeight() - PLAYER_HEIGHT) { for (Player &p: players) { - if (areLinesColliding( - p.x, p.x + confData.playersWidth, - invaderPos.getX(), invaderPos.getX() + confData.invadersSize - )) { - // TODO manage player death - touched = true; - // do not break, the other player also deserves to be hit + if(p.isPlaying()){ + if (areLinesColliding( + p.x, p.x + confData.playersWidth, + invaderPos.getX(), invaderPos.getX() + confData.invadersSize + )) { + p.damage(); + touched = true; + // do not break, the other player also deserves to be hit + } } } } diff --git a/src/player.cpp b/src/player.cpp index 096d59b..e322ff8 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -1,5 +1,18 @@ #include "player.h" -bool Player::isEliminated() { - return lives == 0 && deathAnimCounter == 0; +bool Player::isPlaying() const { + return !isEliminated() && !hasDeathAnimation(); +} + +bool Player::hasDeathAnimation() const { + return deathAnimCounter!=0; +} + +bool Player::isEliminated() const { + return lives == 0; +} + +void Player::damage() { + --lives; + deathAnimCounter = 1; } \ No newline at end of file diff --git a/src/projectiles.cpp b/src/projectiles.cpp index 76397fd..4dfa44a 100644 --- a/src/projectiles.cpp +++ b/src/projectiles.cpp @@ -1,5 +1,5 @@ #include "projectiles.h" -torpedo::torpedo(int x, int y, playerID owner) : Position(x, y) { +Torpedo::Torpedo(int x, int y, playerID owner) : Position(x, y) { this->owner = owner; } diff --git a/src/scoresManager.cpp b/src/scoresManager.cpp index 4fc7832..bf82608 100644 --- a/src/scoresManager.cpp +++ b/src/scoresManager.cpp @@ -84,7 +84,7 @@ void ScoresManager::inputScore(const string& name, unsigned score) { ++ite; } if(ite==scores.end())scores.emplace(ite, name, score); - if(scores.size()==6)scores.resize(5); + if(scores.size()==6)scores.pop_back(); }