refactor: moved mob functions
This commit is contained in:
@@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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<EffectDuration> 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<float>(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<MobListener> MobNotifier;
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "game/Mobs.h"
|
||||
#include "game/Player.h"
|
||||
#include "game/World.h"
|
||||
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
@@ -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<WalkableTile>(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<float>(static_cast<std::int32_t>(getCenterX()));
|
||||
float tileY = static_cast<float>(static_cast<std::int32_t>(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) {
|
||||
|
||||
@@ -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<float>(static_cast<std::int32_t>(mob->getCenterX()));
|
||||
float tileY = static_cast<float>(static_cast<std::int32_t>(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<WalkableTile>(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
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user