feat: add world notifier

This commit is contained in:
2021-11-30 18:36:29 +01:00
parent 0c1824e40b
commit 8171a91ed5
4 changed files with 57 additions and 14 deletions

View File

@@ -10,6 +10,8 @@
#include "Mobs.h" #include "Mobs.h"
#include "Team.h" #include "Team.h"
#include "misc/ObjectNotifier.h"
namespace td { namespace td {
namespace game { namespace game {
typedef std::pair<std::int16_t, std::int16_t> ChunkCoord; typedef std::pair<std::int16_t, std::int16_t> ChunkCoord;
@@ -123,10 +125,14 @@ public:
virtual void OnArrowShot(MobPtr target, Tower* shooter){} virtual void OnArrowShot(MobPtr target, Tower* shooter){}
virtual void OnExplosion(utils::shape::Circle explosion, float centerDamage, 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<WorldListener> WorldNotifier;
class World : public WorldNotifier, public WorldListener{
protected: protected:
TowerTileColorPalette m_TowerPlacePalette; TowerTileColorPalette m_TowerPlacePalette;
Color m_WalkablePalette; Color m_WalkablePalette;

View File

@@ -0,0 +1,36 @@
#pragma once
#include <algorithm>
#include <functional>
#include <vector>
namespace td {
namespace utils {
template <typename Listener>
class ObjectNotifier {
protected:
std::vector<Listener*> 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 <typename Func, typename... Args>
void notifyListeners(Func function, Args... args) {
for (Listener* listener : m_Listeners)
std::bind(function, listener, args...)();
}
};
} // namespace utils
} // namespace td

View File

@@ -213,7 +213,7 @@ void ArcherTower::tick(std::uint64_t delta, World* world) {
std::uint8_t arrows = explosiveArrows ? 2 : getLevel().getLevel(); std::uint8_t arrows = explosiveArrows ? 2 : getLevel().getLevel();
for (MobPtr mob : world->getMobList()) { for (MobPtr mob : world->getMobList()) {
if (isMobInRange(mob)) { if (isMobInRange(mob)) {
world->OnArcherTowerShot(mob, this); world->notifyListeners(&WorldListener::OnArcherTowerShot, mob, this);
m_Timer.applyCooldown(); m_Timer.applyCooldown();
arrowsShot++; arrowsShot++;
if (arrowsShot >= arrows) if (arrowsShot >= arrows)
@@ -229,7 +229,7 @@ void IceTower::tick(std::uint64_t delta, World* world) {
for (MobPtr mob : world->getMobList()) { for (MobPtr mob : world->getMobList()) {
if (isMobInRange(mob)) { if (isMobInRange(mob)) {
mob->addEffect(EffectType::Slowness, 1, this); // slowness for 1s every second 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(); m_Timer.applyCooldown();
} }
} }
@@ -252,7 +252,7 @@ void PoisonTower::tick(std::uint64_t delta, World* world) {
for (MobPtr mob : world->getMobList()) { for (MobPtr mob : world->getMobList()) {
if (isMobInRange(mob)) { if (isMobInRange(mob)) {
if (getLevel().getPath() == TowerPath::Bottom) { if (getLevel().getPath() == TowerPath::Bottom) {
mob->damage(getStats()->getDamage(), this); world->notifyListeners(&WorldListener::OnMobDamage, mob, getStats()->getDamage(), this);
} else { } else {
float durationSec; float durationSec;
switch (getLevel().getLevel()) { switch (getLevel().getLevel()) {

View File

@@ -13,7 +13,7 @@ namespace td {
namespace game { namespace game {
World::World(Game* game) : m_Game(game) { World::World(Game* game) : m_Game(game) {
bindListener(this);
} }
TilePtr World::getTile(std::int32_t x, std::int32_t y) const { 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) { void World::OnArcherTowerShot(MobPtr target, ArcherTower* shooter) {
bool explosiveArrows = shooter->getLevel().getPath() == TowerPath::Bottom; bool explosiveArrows = shooter->getLevel().getPath() == TowerPath::Bottom;
notifyListeners(&WorldListener::OnArrowShot, target, shooter);
if (explosiveArrows) { if (explosiveArrows) {
OnArrowShot(target, shooter); notifyListeners(&WorldListener::OnExplosion, utils::shape::Circle{ target->getCenterX(), target->getCenterY(), 1.0f }, 10.0f, shooter);
OnExplosion({ target->getCenterX(), target->getCenterY(), 1.0f }, 10.0f, shooter);
} else {
OnArrowShot(target, shooter);
} }
} }
void World::OnArrowShot(MobPtr target, Tower* 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) { void World::OnExplosion(utils::shape::Circle explosion, float centerDamage, Tower* shooter) {
for (MobPtr mob : m_Mobs) { for (MobPtr mob : m_Mobs) {
if (mob->collidesWith(explosion)) { if (mob->isAlive() && mob->collidesWith(explosion)) {
// linear distance damage reduction // linear distance damage reduction
float explosionDamage = mob->distance(explosion) / explosion.getRadius() * centerDamage; 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); target->damage(damage, source);
if (target->isDead()) {
notifyListeners(&WorldListener::OnMobDead, target);
}
} }
Team& World::getRedTeam() { Team& World::getRedTeam() {