diff --git a/include/game/BaseGame.h b/include/game/BaseGame.h index 8db9566..963e4e5 100644 --- a/include/game/BaseGame.h +++ b/include/game/BaseGame.h @@ -46,8 +46,8 @@ public: const PlayerList& getPlayers() const { return m_Players; } PlayerList& getPlayers() { return m_Players; } - const Player& getPlayerById(PlayerID id) const { return m_Players.find(id)->second; } - Player& getPlayerById(PlayerID id) { return m_Players.find(id)->second; } + const Player* getPlayerById(PlayerID id) const; + Player* getPlayerById(PlayerID id); }; diff --git a/include/game/Mobs.h b/include/game/Mobs.h index 9108ff7..7c263d0 100644 --- a/include/game/Mobs.h +++ b/include/game/Mobs.h @@ -14,6 +14,8 @@ namespace td { namespace game { +struct WalkableTile; + enum class EffectType : std::uint8_t { Slowness = 0, Stun, @@ -88,22 +90,24 @@ private: Direction m_Direction; std::vector m_Effects; const Tower* m_LastDamage; // the last tower that damaged the mob - bool m_HasReachedCastle; // if true, no need to walk anymore utils::Timer m_EffectFireTimer; utils::Timer m_EffectPoisonTimer; utils::Timer m_EffectHealTimer; + TeamCastle* m_CastleTarget; + utils::CooldownTimer m_AttackTimer; + public: Mob(MobID id, MobLevel level, PlayerID sender) : m_Sender(sender), m_Level(level), - m_HasReachedCastle(false), m_EffectFireTimer(1000), m_EffectPoisonTimer(1000), - m_EffectHealTimer(1000) { + m_EffectFireTimer(1000), m_EffectPoisonTimer(1000), + m_EffectHealTimer(1000), m_CastleTarget(nullptr), m_AttackTimer(1000) { } virtual MobType getType() const = 0; - virtual void tick(std::uint64_t delta); + virtual void tick(std::uint64_t delta, World* world); virtual bool OnDeath(World* world) { return true; } @@ -116,11 +120,11 @@ public: bool isDead() const { return m_Health <= 0; } bool isAlive() const { return m_Health > 0; } const Tower* getLastDamageTower() { return m_LastDamage; } - bool hasReachedEnemyCastle() { return m_HasReachedCastle; } + bool hasReachedEnemyCastle() { return m_CastleTarget != nullptr; } void damage(float dmg, const Tower* damager) { m_Health = std::max(0.0f, m_Health - dmg); m_LastDamage = damager; } void heal(float heal) { m_Health = std::min(static_cast(getStats()->getMaxLife()), m_Health + heal); } - void setMobReachedCastle() { m_HasReachedCastle = true; } // used when mob is in front of the castle + void setMobReachedCastle(TeamCastle* castle) { m_CastleTarget = castle; } // used when mob is in front of the castle bool isImmuneTo(TowerType type); @@ -140,6 +144,12 @@ protected: } private: void updateEffects(std::uint64_t delta); + void attackCastle(std::uint64_t delta, World* world); + void move(std::uint64_t delta, World* world); + void walk(std::uint64_t delta, World* world); + void moveBack(const TeamCastle& castle, World* world); + void changeDirection(const WalkableTile& tile, World* world); + bool isTouchingCastle(const TeamCastle& castle) const; EffectDuration& getEffect(EffectType type); }; @@ -227,8 +237,8 @@ public: virtual void OnMobDamage(MobPtr target, float damage, Tower* damager) {} - virtual void OnMobTouchCastle(MobPtr damager, TeamCastle* enemyCastle) {} - virtual void OnMobCastleDamage(MobPtr damager, TeamCastle* enemyCastle) {} + virtual void OnMobTouchCastle(Mob* damager, TeamCastle* enemyCastle) {} + virtual void OnMobCastleDamage(Mob* damager, TeamCastle* enemyCastle, float damage) {} }; typedef utils::ObjectNotifier MobNotifier; diff --git a/include/game/World.h b/include/game/World.h index b594513..8913ad6 100644 --- a/include/game/World.h +++ b/include/game/World.h @@ -208,6 +208,8 @@ public: const TowerList& getTowers() const { return m_Towers; }; TowerPtr getTowerById(TowerID tower); + const Player* getPlayerById(PlayerID id) const; + WorldNotifier& getWorldNotifier() { return m_WorldNotifier; } MobNotifier& getMobNotifier() { return m_MobNotifier; } @@ -222,11 +224,6 @@ public: virtual void OnMobDamage(MobPtr target, float damage, Tower* source); private: - void moveMobs(std::uint64_t delta); - void moveMob(MobPtr mob, std::uint64_t delta); - void moveBackMob(MobPtr mob, const TeamCastle& castle); - void changeMobDirection(MobPtr mob, WalkableTilePtr tile); - bool isMobTouchingCastle(MobPtr mob, const TeamCastle& castle) const; void tickMobs(std::uint64_t delta); void cleanDeadMobs(); }; diff --git a/include/misc/Time.h b/include/misc/Time.h index 18a912d..0b7ca22 100644 --- a/include/misc/Time.h +++ b/include/misc/Time.h @@ -53,7 +53,7 @@ public: std::uint64_t getInterval() const { return m_Interval; } }; -// utililty class to call function at regular period of time with a cooldown (used for towers) +// utililty class to call function at regular period of time with a cooldown (used for towers and mobs ) class CooldownTimer { private: std::uint64_t m_Cooldown; // in millis diff --git a/src/game/BaseGame.cpp b/src/game/BaseGame.cpp index 1d2b610..d6a01db 100644 --- a/src/game/BaseGame.cpp +++ b/src/game/BaseGame.cpp @@ -17,5 +17,21 @@ void Game::tick(std::uint64_t delta) { } } +Player* Game::getPlayerById(PlayerID id) { + auto it = m_Players.find(id); + + if (it == m_Players.end()) return nullptr; + + return &it->second; +} + +const Player* Game::getPlayerById(PlayerID id) const { + auto it = m_Players.find(id); + + if (it == m_Players.end()) return nullptr; + + return &it->second; +} + } // namespace game } // namespace td diff --git a/src/game/Mobs.cpp b/src/game/Mobs.cpp index 5a2d179..60cac82 100644 --- a/src/game/Mobs.cpp +++ b/src/game/Mobs.cpp @@ -1,4 +1,6 @@ #include "game/Mobs.h" +#include "game/Player.h" +#include "game/World.h" #include #include @@ -30,8 +32,171 @@ void Mob::addEffect(EffectType effectType, float durationSec, const Tower* tower } } -void Mob::tick(std::uint64_t delta) { +void Mob::attackCastle(std::uint64_t delta, World* world) { + if (m_AttackTimer.update(delta)) { + world->getMobNotifier().notifyListeners(&MobListener::OnMobCastleDamage, this, m_CastleTarget, getStats()->getDamage()); + m_AttackTimer.applyCooldown(); + } +} + +void Mob::walk(std::uint64_t delta, World* world) { + float mobWalkSpeed = getStats()->getMovementSpeed(); + + float walkAmount = mobWalkSpeed * ((float)delta / 1000.0f); + + if (hasEffect(EffectType::Slowness)) + walkAmount *= 0.70; // walk 30% slower + + switch (getDirection()) { + case Direction::NegativeX: { + setCenterX(getCenterX() - walkAmount); + break; + } + case Direction::PositiveX: { + setCenterX(getCenterX() + walkAmount); + break; + } + case Direction::NegativeY: { + setCenterY(getCenterY() - walkAmount); + break; + } + case Direction::PositiveY: { + setCenterY(getCenterY() + walkAmount); + break; + } + } +} + +void Mob::move(std::uint64_t delta, World* world) { + TilePtr tile = world->getTile(getCenter().getX(), getCenter().getY()); + + if (tile != nullptr && tile->getType() == TileType::Walk) { + WalkableTilePtr walkTile = std::static_pointer_cast(tile); + changeDirection(*walkTile, world); + } + + if (hasReachedEnemyCastle()) return; + + walk(delta, world); + + TeamColor mobTeam = world->getPlayerById(getSender())->getTeamColor(); + + TeamCastle* enemyCastle = nullptr; + + if (mobTeam == TeamColor::Red) { + enemyCastle = &world->getBlueTeam().getCastle(); + } else if (mobTeam == TeamColor::Blue) { + enemyCastle = &world->getRedTeam().getCastle(); + } + + if (isTouchingCastle(*enemyCastle)) { + moveBack(*enemyCastle, world); + setMobReachedCastle(enemyCastle); + + world->getMobNotifier().notifyListeners(&MobListener::OnMobTouchCastle, this, enemyCastle); + } +} + +void Mob::moveBack(const TeamCastle& enemyCastle, World* world) { + switch (getDirection()) { + case Direction::NegativeX: { + setCenterX(enemyCastle.getBottomRight().getX() + getWidth() / 2.0f); + break; + } + case Direction::PositiveX: { + setCenterX(enemyCastle.getTopLeft().getX() - getWidth() / 2.0f); + break; + } + case Direction::NegativeY: { + setCenterY(enemyCastle.getBottomRight().getY() + getHeight() / 2.0f); + break; + } + case Direction::PositiveY: { + setCenterY(enemyCastle.getTopLeft().getY() - getHeight() / 2.0f); + break; + } + } +} + +void Mob::changeDirection(const WalkableTile& tile, World* world) { + if (getDirection() == tile.direction) return; + + float tileX = static_cast(static_cast(getCenterX())); + float tileY = static_cast(static_cast(getCenterY())); + + switch (getDirection()) { + + case Direction::PositiveY: { + if (tile.direction == Direction::NegativeX) { + if (getTileY() > getTileX()) { + setCenterY(tileY + getTileX()); + setDirection(tile.direction); + } + } else { // tile->direction = Direction::PositiveX + if (getTileY() > 1 - getTileX()) { + setCenterY(tileY + (1 - getTileX())); + setDirection(tile.direction); + } + } + return; + } + + case Direction::NegativeY: { + if (tile.direction == Direction::PositiveX) { + if (getTileY() < getTileX()) { + setCenterY(tileY + getTileX()); + setDirection(tile.direction); + } + } else { // tile.direction = Direction::NegativeX + if (getTileY() < 1 - getTileX()) { + setCenterY(tileY + (1 - getTileX())); + setDirection(tile.direction); + } + } + return; + } + + case Direction::PositiveX: { + if (tile.direction == Direction::NegativeY) { + if (getTileX() > getTileY()) { + setCenterX(tileX + getTileY()); + setDirection(tile.direction); + } + } else { // tile.direction = Direction::PositiveY + if (getTileX() > 1 - getTileY()) { + setCenterX(tileX + (1 - getTileY())); + setDirection(tile.direction); + } + } + return; + } + + case Direction::NegativeX: { + if (tile.direction == Direction::PositiveY) { + if (getTileX() < getTileY()) { + setCenterX(tileX + getTileY()); + setDirection(tile.direction); + } + } else { // tile.direction = Direction::NegativeY + if (getTileX() < 1 - getTileY()) { + setCenterX(tileX + (1 - getTileY())); + setDirection(tile.direction); + } + } + return; + } + + } +} + +bool Mob::isTouchingCastle(const TeamCastle& enemyCastle) const { + return enemyCastle.collidesWith(*this); +} + +void Mob::tick(std::uint64_t delta, World* world) { updateEffects(delta); + move(delta, world); + attackCastle(delta, world); } void Mob::updateEffects(std::uint64_t delta) { diff --git a/src/game/World.cpp b/src/game/World.cpp index 3322697..208b1fc 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -102,7 +102,6 @@ bool World::saveMap(const std::string& fileName) const { } void World::tick(std::uint64_t delta) { - moveMobs(delta); tickMobs(delta); for (TowerPtr tower : m_Towers) { tower->tick(delta, this); @@ -136,163 +135,7 @@ TowerPtr World::removeTower(TowerID towerId) { void World::tickMobs(std::uint64_t delta) { for (MobPtr mob : m_Mobs) { - mob->tick(delta); - } -} - -void World::changeMobDirection(MobPtr mob, WalkableTilePtr tile) { - if (mob->getDirection() == tile->direction) return; - - float tileX = static_cast(static_cast(mob->getCenterX())); - float tileY = static_cast(static_cast(mob->getCenterY())); - - switch (mob->getDirection()) { - - case Direction::PositiveY: { - if (tile->direction == Direction::NegativeX) { - if (mob->getTileY() > mob->getTileX()) { - mob->setCenterY(tileY + mob->getTileX()); - mob->setDirection(tile->direction); - } - } else { // tile->direction = Direction::PositiveX - if (mob->getTileY() > 1 - mob->getTileX()) { - mob->setCenterY(tileY + (1 - mob->getTileX())); - mob->setDirection(tile->direction); - } - } - return; - } - - case Direction::NegativeY: { - if (tile->direction == Direction::PositiveX) { - if (mob->getTileY() < mob->getTileX()) { - mob->setCenterY(tileY + mob->getTileX()); - mob->setDirection(tile->direction); - } - } else { // tile->direction = Direction::NegativeX - if (mob->getTileY() < 1 - mob->getTileX()) { - mob->setCenterY(tileY + (1 - mob->getTileX())); - mob->setDirection(tile->direction); - } - } - return; - } - - case Direction::PositiveX: { - if (tile->direction == Direction::NegativeY) { - if (mob->getTileX() > mob->getTileY()) { - mob->setCenterX(tileX + mob->getTileY()); - mob->setDirection(tile->direction); - } - } else { // tile->direction = Direction::PositiveY - if (mob->getTileX() > 1 - mob->getTileY()) { - mob->setCenterX(tileX + (1 - mob->getTileY())); - mob->setDirection(tile->direction); - } - } - return; - } - - case Direction::NegativeX: { - if (tile->direction == Direction::PositiveY) { - if (mob->getTileX() < mob->getTileY()) { - mob->setCenterX(tileX + mob->getTileY()); - mob->setDirection(tile->direction); - } - } else { // tile->direction = Direction::NegativeY - if (mob->getTileX() < 1 - mob->getTileY()) { - mob->setCenterX(tileX + (1 - mob->getTileY())); - mob->setDirection(tile->direction); - } - } - return; - } - - } -} - -void World::moveMobs(std::uint64_t delta) { - for (MobPtr mob : m_Mobs) { - TilePtr tile = getTile(mob->getCenter().getX(), mob->getCenter().getY()); - - if (tile != nullptr && tile->getType() == TileType::Walk) { - WalkableTilePtr walkTile = std::static_pointer_cast(tile); - changeMobDirection(mob, walkTile); - } - - if (mob->hasReachedEnemyCastle()) continue; - - moveMob(mob, delta); - - TeamColor mobTeam = m_Game->getPlayerById(mob->getSender()).getTeamColor(); - - TeamCastle* enemyCastle = nullptr; - - if (mobTeam == TeamColor::Red) { - enemyCastle = &getBlueTeam().getCastle(); - } else if (mobTeam == TeamColor::Blue) { - enemyCastle = &getRedTeam().getCastle(); - } - - if (isMobTouchingCastle(mob, *enemyCastle)) { - moveBackMob(mob, *enemyCastle); - mob->setMobReachedCastle(); - - getMobNotifier().notifyListeners(&MobListener::OnMobTouchCastle, mob, enemyCastle); - } - } -} - -void World::moveMob(MobPtr mob, std::uint64_t delta) { - float mobWalkSpeed = mob->getStats()->getMovementSpeed(); - - float walkAmount = mobWalkSpeed * ((float)delta / 1000.0f); - - if (mob->hasEffect(EffectType::Slowness)) - walkAmount *= 0.70; // walk 30% slower - - switch (mob->getDirection()) { - case Direction::NegativeX: { - mob->setCenterX(mob->getCenterX() - walkAmount); - break; - } - case Direction::PositiveX: { - mob->setCenterX(mob->getCenterX() + walkAmount); - break; - } - case Direction::NegativeY: { - mob->setCenterY(mob->getCenterY() - walkAmount); - break; - } - case Direction::PositiveY: { - mob->setCenterY(mob->getCenterY() + walkAmount); - break; - } - } -} - -bool World::isMobTouchingCastle(MobPtr mob, const TeamCastle& enemyCastle) const { - return enemyCastle.collidesWith(*mob); -} - -void World::moveBackMob(MobPtr mob, const TeamCastle& enemyCastle) { - switch (mob->getDirection()) { - case Direction::NegativeX: { - mob->setCenterX(enemyCastle.getBottomRight().getX() + mob->getWidth() / 2.0f); - break; - } - case Direction::PositiveX: { - mob->setCenterX(enemyCastle.getTopLeft().getX() - mob->getWidth() / 2.0f); - break; - } - case Direction::NegativeY: { - mob->setCenterY(enemyCastle.getBottomRight().getY() + mob->getHeight() / 2.0f); - break; - } - case Direction::PositiveY: { - mob->setCenterY(enemyCastle.getTopLeft().getY() - mob->getHeight() / 2.0f); - break; - } + mob->tick(delta, this); } } @@ -377,11 +220,11 @@ void World::cleanDeadMobs() { if (mob->isDead()) { if (mob->OnDeath(this)) { //reward players - Player& sender = m_Game->getPlayerById(mob->getSender()); - sender.addExp(mob->getStats()->getExpReward()); + Player* sender = m_Game->getPlayerById(mob->getSender()); + sender->addExp(mob->getStats()->getExpReward()); - Player& killer = m_Game->getPlayerById(mob->getLastDamageTower()->getBuilder()); - killer.addGold(mob->getStats()->getMoneyCost()); + Player* killer = m_Game->getPlayerById(mob->getLastDamageTower()->getBuilder()); + killer->addGold(mob->getStats()->getMoneyCost()); m_Mobs.erase(m_Mobs.begin() + i); } @@ -470,5 +313,9 @@ const Team& World::getTeam(TeamColor team) const { return m_Game->getTeam(team); } +const Player* World::getPlayerById(PlayerID id) const { + return m_Game->getPlayerById(id); +} + } // namespace game } // namespace td diff --git a/src/render/gui/MobTooltip.cpp b/src/render/gui/MobTooltip.cpp index fd3ea0b..4acfab2 100644 --- a/src/render/gui/MobTooltip.cpp +++ b/src/render/gui/MobTooltip.cpp @@ -17,12 +17,14 @@ MobTooltip::MobTooltip(client::Client* client) : GuiWidget(client) { void MobTooltip::render() { if (m_Mob == nullptr) return; - const game::Player& sender = getClient()->getGame().getPlayerById(m_Mob->getSender()); + // TODO: add sender null check + + const game::Player* sender = getClient()->getGame().getPlayerById(m_Mob->getSender()); ImGui::BeginTooltip(); ImGui::Text("Sender :"); ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Text, render::WorldRenderer::getImGuiTeamColor(sender.getTeamColor())); - ImGui::Text("%s", sender.getName().c_str()); + ImGui::PushStyleColor(ImGuiCol_Text, render::WorldRenderer::getImGuiTeamColor(sender->getTeamColor())); + ImGui::Text("%s", sender->getName().c_str()); ImGui::PopStyleColor(); ImGui::Text("Mob HP : %.1f/%i", m_Mob->getHealth(), m_Mob->getStats()->getMaxLife()); ImGui::Text("Mob Type : %s", game::MobFactory::getMobName(m_Mob->getType()).c_str());