From 8171a91ed5b6300e8ab500755f2316ec249de313 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Tue, 30 Nov 2021 18:36:29 +0100 Subject: [PATCH] feat: add world notifier --- include/game/World.h | 10 ++++++++-- include/misc/ObjectNotifier.h | 36 +++++++++++++++++++++++++++++++++++ src/game/Towers.cpp | 6 +++--- src/game/World.cpp | 19 +++++++++--------- 4 files changed, 57 insertions(+), 14 deletions(-) create mode 100644 include/misc/ObjectNotifier.h diff --git a/include/game/World.h b/include/game/World.h index e54af27..f16242c 100644 --- a/include/game/World.h +++ b/include/game/World.h @@ -10,6 +10,8 @@ #include "Mobs.h" #include "Team.h" +#include "misc/ObjectNotifier.h" + namespace td { namespace game { typedef std::pair ChunkCoord; @@ -123,10 +125,14 @@ public: virtual void OnArrowShot(MobPtr target, Tower* shooter){} virtual void OnExplosion(utils::shape::Circle explosion, float centerDamage, Tower* shooter){} - virtual void OnMobDamage(MobPtr target, float damage){} + virtual void OnMobDamage(MobPtr target, float damage, Tower* damager){} + + virtual void OnMobDead(MobPtr mob){} }; -class World : public WorldListener{ +typedef utils::ObjectNotifier WorldNotifier; + +class World : public WorldNotifier, public WorldListener{ protected: TowerTileColorPalette m_TowerPlacePalette; Color m_WalkablePalette; diff --git a/include/misc/ObjectNotifier.h b/include/misc/ObjectNotifier.h new file mode 100644 index 0000000..2c19f63 --- /dev/null +++ b/include/misc/ObjectNotifier.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +namespace td { +namespace utils { + +template +class ObjectNotifier { +protected: + std::vector m_Listeners; + +public: + void bindListener(Listener* listener) { + m_Listeners.push_back(listener); + } + + void unbindListener(Listener* listener) { + auto iter = std::find(m_Listeners.begin(), m_Listeners.end(), listener); + + if(iter == m_Listeners.end()) return; + + m_Listeners.erase(iter); + } + + template + void notifyListeners(Func function, Args... args) { + for (Listener* listener : m_Listeners) + std::bind(function, listener, args...)(); + } +}; + +} // namespace utils +} // namespace td diff --git a/src/game/Towers.cpp b/src/game/Towers.cpp index 2fc6d5a..0ebc959 100644 --- a/src/game/Towers.cpp +++ b/src/game/Towers.cpp @@ -213,7 +213,7 @@ void ArcherTower::tick(std::uint64_t delta, World* world) { std::uint8_t arrows = explosiveArrows ? 2 : getLevel().getLevel(); for (MobPtr mob : world->getMobList()) { if (isMobInRange(mob)) { - world->OnArcherTowerShot(mob, this); + world->notifyListeners(&WorldListener::OnArcherTowerShot, mob, this); m_Timer.applyCooldown(); arrowsShot++; if (arrowsShot >= arrows) @@ -229,7 +229,7 @@ void IceTower::tick(std::uint64_t delta, World* world) { for (MobPtr mob : world->getMobList()) { if (isMobInRange(mob)) { mob->addEffect(EffectType::Slowness, 1, this); // slowness for 1s every second - mob->damage(damage, this); + world->notifyListeners(&WorldListener::OnMobDamage, mob, damage, this); m_Timer.applyCooldown(); } } @@ -252,7 +252,7 @@ void PoisonTower::tick(std::uint64_t delta, World* world) { for (MobPtr mob : world->getMobList()) { if (isMobInRange(mob)) { if (getLevel().getPath() == TowerPath::Bottom) { - mob->damage(getStats()->getDamage(), this); + world->notifyListeners(&WorldListener::OnMobDamage, mob, getStats()->getDamage(), this); } else { float durationSec; switch (getLevel().getLevel()) { diff --git a/src/game/World.cpp b/src/game/World.cpp index f517a0c..6880b29 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -13,7 +13,7 @@ namespace td { namespace game { World::World(Game* game) : m_Game(game) { - + bindListener(this); } TilePtr World::getTile(std::int32_t x, std::int32_t y) const { @@ -400,30 +400,31 @@ TowerPtr World::getTowerById(TowerID towerID) { void World::OnArcherTowerShot(MobPtr target, ArcherTower* shooter) { bool explosiveArrows = shooter->getLevel().getPath() == TowerPath::Bottom; + notifyListeners(&WorldListener::OnArrowShot, target, shooter); if (explosiveArrows) { - OnArrowShot(target, shooter); - OnExplosion({ target->getCenterX(), target->getCenterY(), 1.0f }, 10.0f, shooter); - } else { - OnArrowShot(target, shooter); + notifyListeners(&WorldListener::OnExplosion, utils::shape::Circle{ target->getCenterX(), target->getCenterY(), 1.0f }, 10.0f, shooter); } } void World::OnArrowShot(MobPtr target, Tower* shooter) { - OnMobDamage(target, shooter->getStats()->getDamage(), shooter); + notifyListeners(&WorldListener::OnMobDamage, target, shooter->getStats()->getDamage(), shooter); } void World::OnExplosion(utils::shape::Circle explosion, float centerDamage, Tower* shooter) { for (MobPtr mob : m_Mobs) { - if (mob->collidesWith(explosion)) { + if (mob->isAlive() && mob->collidesWith(explosion)) { // linear distance damage reduction float explosionDamage = mob->distance(explosion) / explosion.getRadius() * centerDamage; - OnMobDamage(mob, explosionDamage, shooter); + notifyListeners(&WorldListener::OnMobDamage, mob, explosionDamage, shooter); } } } -void World::OnMobDamage(MobPtr target, float damage, Tower* source){ +void World::OnMobDamage(MobPtr target, float damage, Tower* source) { target->damage(damage, source); + if (target->isDead()) { + notifyListeners(&WorldListener::OnMobDead, target); + } } Team& World::getRedTeam() {