feat: add world notifier
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
36
include/misc/ObjectNotifier.h
Normal file
36
include/misc/ObjectNotifier.h
Normal 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
|
||||||
@@ -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()) {
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
Reference in New Issue
Block a user