beta heart system
This commit is contained in:
parent
b8c29cad9d
commit
c5f08c8bcc
@ -8,6 +8,7 @@ players:
|
||||
startXPosition: 50
|
||||
fireCooldown: 10
|
||||
speed: 20
|
||||
lives: 3
|
||||
user1:
|
||||
color: red
|
||||
keys:
|
||||
|
@ -20,6 +20,7 @@ struct ConfigData {
|
||||
unsigned playersSpeed;
|
||||
unsigned playersWidth;
|
||||
unsigned playersFireCooldown;
|
||||
unsigned playersLives;
|
||||
vector<PlayerDef> playerDefs;
|
||||
|
||||
|
||||
|
@ -28,7 +28,7 @@ private:
|
||||
bool direction = true;
|
||||
|
||||
vector<missile> missiles;
|
||||
vector<torpedo> torpedos;
|
||||
vector<Torpedo> torpedos;
|
||||
|
||||
PlayMode playMode;
|
||||
vector<Player> 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();
|
||||
};
|
||||
|
@ -5,7 +5,6 @@ enum class PlayMode {
|
||||
NONE,
|
||||
SINGLE,
|
||||
TWO_LOCAL,
|
||||
TWO_TCPIP,
|
||||
EXIT,
|
||||
};
|
||||
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -1,16 +1,16 @@
|
||||
#ifndef GUARD_SCORESMANAGER_H
|
||||
#define GUARD_SCORESMANAGER_H
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include<utility>
|
||||
#include<vector>
|
||||
#include<string>
|
||||
#include "utils.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct ScoreLink{
|
||||
string name;
|
||||
unsigned score;
|
||||
ScoreLink() = default;
|
||||
ScoreLink(const string& name, unsigned score);
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;i<players.size();++i){
|
||||
pm.drawPlayer(players[i].x, confData.playersWidth, confData.playerDefs[i].color);
|
||||
if(players[i].deathAnimCounter%2==0){
|
||||
pm.drawPlayer(players[i].x, confData.playersWidth, confData.playerDefs[i].color);
|
||||
}
|
||||
displayHearts(i);
|
||||
}
|
||||
|
||||
// As said before, the player loop is an illusion, 2 players max
|
||||
for(unsigned i=0;i<players[0].lives;++i){
|
||||
|
||||
}
|
||||
pm.drawHeart(Position(0, 70));
|
||||
}
|
||||
|
||||
void Game::displayHearts(playerID pID) const {
|
||||
|
||||
// As said before, the player loop is an illusion, 2 players max
|
||||
unsigned x;
|
||||
if(pID==PLAYER1)x = 0;
|
||||
else x = pm.getScreenWidth()-HEART_LENGTH;
|
||||
@ -50,14 +49,14 @@ void Game::displayHearts(playerID pID) const {
|
||||
pm.drawHeart(Position(x, y));
|
||||
y+=HEART_LENGTH+5;
|
||||
}
|
||||
if(players[pID].deathAnimCounter>0){
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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<players.size();++i){
|
||||
players[i].id = i;
|
||||
players[i].lives = confData.playersLives;
|
||||
}
|
||||
|
||||
basePos = Position(200, 0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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::enterGameLoop(){ // returns when game is finished
|
||||
// computed in advance for performance reasons
|
||||
chrono::milliseconds maxFrameTime = chrono::milliseconds(1000/confData.maxFPS);
|
||||
|
||||
|
@ -2,39 +2,33 @@
|
||||
|
||||
#define ISPRESSED(ID, X) window.isPressed({confData.playerDefs[ID].keys.X, false})
|
||||
void Game::manageOnePlayer(playerID pID){
|
||||
// manage death
|
||||
|
||||
// we do not use isEliminated here because we can handle it better with if/else
|
||||
|
||||
|
||||
/*
|
||||
* lives = 0 && counter == 0 -> 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;
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user