Fixed player attacks
This commit is contained in:
parent
851e591c3c
commit
36aa2fa245
11 changed files with 152 additions and 17 deletions
|
@ -4,9 +4,10 @@
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
#include <box2d/box2d.h>
|
#include <box2d/box2d.h>
|
||||||
|
|
||||||
#define DEVELOPER_MODE true
|
#define DEVELOPER_MODE false
|
||||||
|
|
||||||
#define GAME_NAME "Holesome"
|
#define GAME_NAME "Holesome"
|
||||||
|
#define CLOSE_GAME_BTN sf::Keyboard::Escape
|
||||||
|
|
||||||
// Player
|
// Player
|
||||||
#define DEFAULT_PLAYER_SPEED 5.f // World units per second
|
#define DEFAULT_PLAYER_SPEED 5.f // World units per second
|
||||||
|
@ -15,6 +16,9 @@
|
||||||
#define PLAYER_RADIUS_PER_LEVEL 0.25f
|
#define PLAYER_RADIUS_PER_LEVEL 0.25f
|
||||||
#define DEFAULT_MAX_PLAYER_NUMBER 4
|
#define DEFAULT_MAX_PLAYER_NUMBER 4
|
||||||
#define MIN_PLAYER_COUNT 2
|
#define MIN_PLAYER_COUNT 2
|
||||||
|
#define REDUCED_POINT_DELTA_FOR_ATTACK 20
|
||||||
|
#define ATTACK_PER_SECOND 5.f
|
||||||
|
#define PLAYER_ALIVE_THRESHOLD -10
|
||||||
|
|
||||||
// World
|
// World
|
||||||
#define WORLD_GRAVITY b2Vec2(0.f, -9.8f)
|
#define WORLD_GRAVITY b2Vec2(0.f, -9.8f)
|
||||||
|
|
|
@ -162,17 +162,17 @@ void Game::startCountdown(int durationInSeconds)
|
||||||
|
|
||||||
std::shared_ptr<Player> Game::checkForWinner()
|
std::shared_ptr<Player> Game::checkForWinner()
|
||||||
{
|
{
|
||||||
std::vector<std::shared_ptr<Player>> players = PlayerCollection::getInstance()->getPlayers();
|
std::vector<std::shared_ptr<Player>> remainingPlayers = PlayerCollection::getInstance()->getRemainingPlayers();
|
||||||
|
|
||||||
// Has timer run out or is only one player left?
|
// Has timer run out or is only one player left?
|
||||||
if (!countdown->isFinished() && players.size() > 1)
|
if (!countdown->isFinished() && remainingPlayers.size() > 1)
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return player with highest score as winner
|
// Return player with highest score as winner
|
||||||
std::shared_ptr<Player> winner = nullptr;
|
std::shared_ptr<Player> winner = nullptr;
|
||||||
for (const auto &player: players)
|
for (const auto &player: remainingPlayers)
|
||||||
{
|
{
|
||||||
if (winner == nullptr || player->getPoints() > winner->getPoints())
|
if (winner == nullptr || player->getPoints() > winner->getPoints())
|
||||||
{
|
{
|
||||||
|
|
|
@ -52,8 +52,8 @@ void InputMapper::processEvents()
|
||||||
|
|
||||||
void InputMapper::handleKeyPress(sf::Event::KeyEvent event)
|
void InputMapper::handleKeyPress(sf::Event::KeyEvent event)
|
||||||
{
|
{
|
||||||
// Close game on Escape or Q in DEV Mode
|
// Close game on Special button
|
||||||
if (DEVELOPER_MODE && event.code == sf::Keyboard::Escape)
|
if (event.code == CLOSE_GAME_BTN)
|
||||||
{
|
{
|
||||||
Game::getInstance()->exit();
|
Game::getInstance()->exit();
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -8,7 +8,7 @@ LevelConfig LevelGenerator::generateLevel(const std::string &name)
|
||||||
std::vector<CollectableInLevel> collectables = generateRandomCollectables(levelSize, levelSize);
|
std::vector<CollectableInLevel> collectables = generateRandomCollectables(levelSize, levelSize);
|
||||||
|
|
||||||
return LevelConfig(name,
|
return LevelConfig(name,
|
||||||
120,
|
90,
|
||||||
{
|
{
|
||||||
{1, 2},
|
{1, 2},
|
||||||
{2, 1},
|
{2, 1},
|
||||||
|
|
|
@ -84,11 +84,13 @@ void MapPlayer::updateCollidingWithPlayers()
|
||||||
if (mapPlayerA->player->getPlayerId() == player->getPlayerId())
|
if (mapPlayerA->player->getPlayerId() == player->getPlayerId())
|
||||||
{
|
{
|
||||||
collidingWithPlayers.push_back(mapPlayerB->player->getPlayerId());
|
collidingWithPlayers.push_back(mapPlayerB->player->getPlayerId());
|
||||||
} else if (mapPlayerB->player->getPlayerId() == player->getPlayerId())
|
} else
|
||||||
{
|
{
|
||||||
collidingWithPlayers.push_back(mapPlayerA->player->getPlayerId());
|
collidingWithPlayers.push_back(mapPlayerA->player->getPlayerId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
player->setCollidingPlayers(collidingWithPlayers);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapPlayer::updatePlayer()
|
void MapPlayer::updatePlayer()
|
||||||
|
|
|
@ -117,3 +117,15 @@ std::shared_ptr<MapPlayer> MapSimulation::getMapPlayerByBody(b2Body *body) const
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MapSimulation::removePlayer(int playerId)
|
||||||
|
{
|
||||||
|
// Still in simulation?
|
||||||
|
if (mapPlayersById.find(playerId) == mapPlayersById.end())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
world->DestroyBody(mapPlayersById[playerId]->body);
|
||||||
|
mapPlayersById.erase(playerId);
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ public:
|
||||||
|
|
||||||
void addPlayer(const std::shared_ptr<Player>& player);
|
void addPlayer(const std::shared_ptr<Player>& player);
|
||||||
|
|
||||||
|
void removePlayer(int playerId);
|
||||||
|
|
||||||
[[nodiscard]] std::shared_ptr<MapPlayer> getMapPlayerByBody(b2Body* body) const;
|
[[nodiscard]] std::shared_ptr<MapPlayer> getMapPlayerByBody(b2Body* body) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
#include "player.hpp"
|
#include "player.hpp"
|
||||||
#include "../../logging/easylogging++.h"
|
#include "../../logging/easylogging++.h"
|
||||||
|
#include "player_collection.hpp"
|
||||||
|
#include "../../typography/font_manager.hpp"
|
||||||
|
#include "../physics/map/map_simulation.hpp"
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
@ -37,10 +40,13 @@ void Player::update()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!passiveMode) {
|
if (!passiveMode)
|
||||||
|
{
|
||||||
performInteractiveUpdates();
|
performInteractiveUpdates();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handlePlayerCollisions();
|
||||||
|
|
||||||
GameObject::update();
|
GameObject::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,23 +89,23 @@ void Player::setWorldRadius(float newWorldRadius)
|
||||||
skinSprite->coordinates->setScreenOffset(IsometricCoordinates(-newSize / 2.f));
|
skinSprite->coordinates->setScreenOffset(IsometricCoordinates(-newSize / 2.f));
|
||||||
}
|
}
|
||||||
|
|
||||||
long Player::getPoints() const
|
int Player::getPoints() const
|
||||||
{
|
{
|
||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::updateRadiusBasedOnPoints()
|
void Player::updateRadiusBasedOnPoints()
|
||||||
{
|
{
|
||||||
long points = getPoints();
|
int points = getPoints();
|
||||||
float newWorldRadius = PLAYER_MIN_RADIUS + PLAYER_RADIUS_PER_LEVEL * points / 10.f;
|
float newWorldRadius = PLAYER_MIN_RADIUS + PLAYER_RADIUS_PER_LEVEL * points / 10.f;
|
||||||
|
|
||||||
setWorldRadius(newWorldRadius);
|
setWorldRadius(newWorldRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::consume(int points)
|
void Player::consume(int consumedPoints)
|
||||||
{
|
{
|
||||||
this->points += points;
|
this->points += consumedPoints;
|
||||||
LOG(INFO) << "Player " << playerId << " consumed " << points << " points. Total: " << this->points;
|
LOG(INFO) << "Player " << playerId << " consumed " << consumedPoints << " points. Total: " << this->points;
|
||||||
|
|
||||||
updateRadiusBasedOnPoints();
|
updateRadiusBasedOnPoints();
|
||||||
}
|
}
|
||||||
|
@ -125,3 +131,85 @@ void Player::performInteractiveUpdates()
|
||||||
auto moveDelta = moveDirection * speed * FRAME_TIME.asSeconds();
|
auto moveDelta = moveDirection * speed * FRAME_TIME.asSeconds();
|
||||||
coordinates->move(moveDelta);
|
coordinates->move(moveDelta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Player::setCollidingPlayers(const std::vector<int>& collidingPlayerIds)
|
||||||
|
{
|
||||||
|
collidingPlayers.clear();
|
||||||
|
for (auto collidingPlayerId: collidingPlayerIds)
|
||||||
|
{
|
||||||
|
collidingPlayers.push_back(PlayerCollection::getInstance()->getPlayerById(collidingPlayerId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::handlePlayerCollisions()
|
||||||
|
{
|
||||||
|
if (collidingPlayers.empty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count number of players that are bigger than this player by the required margin
|
||||||
|
float numAttackingPlayers = 0;
|
||||||
|
for (const auto& collidingPlayer: collidingPlayers)
|
||||||
|
{
|
||||||
|
if (collidingPlayer->getPoints() >= points + REDUCED_POINT_DELTA_FOR_ATTACK)
|
||||||
|
{
|
||||||
|
numAttackingPlayers++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reduce points if necessary
|
||||||
|
float damage = numAttackingPlayers * ATTACK_PER_SECOND * FRAME_TIME.asSeconds();
|
||||||
|
points -= damage;
|
||||||
|
|
||||||
|
if (damage > 0) {
|
||||||
|
LOG(INFO) << "Player " << playerId << " lost " << damage << " points due to collisions. Total: " << points;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (points <= PLAYER_ALIVE_THRESHOLD)
|
||||||
|
{
|
||||||
|
setAlive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRadiusBasedOnPoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Player::getAlive() const
|
||||||
|
{
|
||||||
|
return isAlive;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setAlive(bool newAlive)
|
||||||
|
{
|
||||||
|
if (isAlive == newAlive)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!newAlive)
|
||||||
|
{
|
||||||
|
setPassiveMode(true);
|
||||||
|
MapSimulation::getInstance()->removePlayer(getPlayerId());
|
||||||
|
}
|
||||||
|
isAlive = newAlive;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::draw(sf::RenderWindow *window)
|
||||||
|
{
|
||||||
|
if (isAlive) {
|
||||||
|
GameObject::draw(window);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print "You were consumed" message
|
||||||
|
sf::Text text;
|
||||||
|
text.setFont(*FontManager::getInstance()->getDefaultFont());
|
||||||
|
text.setString("x-x");
|
||||||
|
text.setCharacterSize(15);
|
||||||
|
text.setFillColor(sf::Color::Red);
|
||||||
|
text.setOutlineColor(sf::Color::Black);
|
||||||
|
text.setOutlineThickness(2);
|
||||||
|
text.setPosition(coordinates->isometric().x - text.getLocalBounds().width / 2.f,
|
||||||
|
coordinates->isometric().y - text.getLocalBounds().height / 2.f);
|
||||||
|
window->draw(text);
|
||||||
|
}
|
||||||
|
|
|
@ -29,27 +29,37 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] float getWorldRadius() const;
|
[[nodiscard]] float getWorldRadius() const;
|
||||||
|
|
||||||
[[nodiscard]] long getPoints() const;
|
[[nodiscard]] int getPoints() const;
|
||||||
|
|
||||||
void consume(int points);
|
void consume(int consumedPoints);
|
||||||
|
|
||||||
std::string getSkinName() const;
|
std::string getSkinName() const;
|
||||||
|
|
||||||
|
void setAlive(bool newAlive);
|
||||||
|
|
||||||
|
[[nodiscard]] bool getAlive() const;
|
||||||
|
|
||||||
std::shared_ptr<InputIdentity> getInput() const;
|
std::shared_ptr<InputIdentity> getInput() const;
|
||||||
|
|
||||||
void setPassiveMode(bool newPassiveMode);
|
void setPassiveMode(bool newPassiveMode);
|
||||||
|
|
||||||
|
void setCollidingPlayers(const std::vector<int>& collidingPlayerIds);
|
||||||
|
|
||||||
|
void draw(sf::RenderWindow *window) override;
|
||||||
|
|
||||||
TranslatedCoordinates spawnPosition;
|
TranslatedCoordinates spawnPosition;
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<InputIdentity> input;
|
std::shared_ptr<InputIdentity> input;
|
||||||
float radiusInWorld = 0.5f; // In world units
|
float radiusInWorld = 0.5f; // In world units
|
||||||
std::shared_ptr<VersatileSprite> skinSprite;
|
std::shared_ptr<VersatileSprite> skinSprite;
|
||||||
|
std::vector<std::shared_ptr<Player>> collidingPlayers;
|
||||||
|
|
||||||
bool passiveMode = true;
|
bool passiveMode = true;
|
||||||
|
bool isAlive = true;
|
||||||
|
|
||||||
std::string skinName;
|
std::string skinName;
|
||||||
|
|
||||||
long points = DEFAULT_PLAYER_POINTS;
|
float points = DEFAULT_PLAYER_POINTS;
|
||||||
|
|
||||||
int playerId;
|
int playerId;
|
||||||
static inline int playerCreationCounter = 0;
|
static inline int playerCreationCounter = 0;
|
||||||
|
@ -59,6 +69,8 @@ private:
|
||||||
void updateRadiusBasedOnPoints();
|
void updateRadiusBasedOnPoints();
|
||||||
|
|
||||||
void performInteractiveUpdates();
|
void performInteractiveUpdates();
|
||||||
|
|
||||||
|
void handlePlayerCollisions();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -227,3 +227,16 @@ void PlayerCollection::resetPlayers()
|
||||||
spawnPlayer(player->getInput(), player->getSkinName());
|
spawnPlayer(player->getInput(), player->getSkinName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Player>> PlayerCollection::getRemainingPlayers() const
|
||||||
|
{
|
||||||
|
std::vector<std::shared_ptr<Player>> remainingPlayers = {};
|
||||||
|
for (auto &player: getPlayers())
|
||||||
|
{
|
||||||
|
if (player->getAlive())
|
||||||
|
{
|
||||||
|
remainingPlayers.push_back(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return remainingPlayers;
|
||||||
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] std::vector<std::shared_ptr<Player>> getRemovedPlayers() const;
|
[[nodiscard]] std::vector<std::shared_ptr<Player>> getRemovedPlayers() const;
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<std::shared_ptr<Player>> getRemainingPlayers() const;
|
||||||
|
|
||||||
[[nodiscard]] int getMaxPlayerCount() const;
|
[[nodiscard]] int getMaxPlayerCount() const;
|
||||||
|
|
||||||
void lateUpdate() override;
|
void lateUpdate() override;
|
||||||
|
|
Loading…
Reference in a new issue