18 Commits

32 changed files with 4907 additions and 291 deletions

View File

@@ -46,8 +46,8 @@ public:
const PlayerList& getPlayers() const { return m_Players; } const PlayerList& getPlayers() const { return m_Players; }
PlayerList& getPlayers() { return m_Players; } PlayerList& getPlayers() { return m_Players; }
const Player& getPlayerById(PlayerID id) const { return m_Players.find(id)->second; } const Player* getPlayerById(PlayerID id) const;
Player& getPlayerById(PlayerID id) { return m_Players.find(id)->second; } Player* getPlayerById(PlayerID id);
}; };

View File

@@ -4,6 +4,8 @@
#include "Types.h" #include "Types.h"
#include "Team.h" #include "Team.h"
#include "misc/ObjectNotifier.h"
#include <vector> #include <vector>
#include <memory> #include <memory>
@@ -12,6 +14,8 @@
namespace td { namespace td {
namespace game { namespace game {
struct WalkableTile;
enum class EffectType : std::uint8_t { enum class EffectType : std::uint8_t {
Slowness = 0, Slowness = 0,
Stun, Stun,
@@ -69,7 +73,7 @@ public:
struct EffectDuration { struct EffectDuration {
EffectType type; EffectType type;
float duration; // in seconds float duration; // in seconds
const Tower* tower; // the tower that gived the effect Tower* tower; // the tower that gived the effect
}; };
const MobStats* getMobStats(MobType type, std::uint8_t level); const MobStats* getMobStats(MobType type, std::uint8_t level);
@@ -86,22 +90,24 @@ private:
Direction m_Direction; Direction m_Direction;
std::vector<EffectDuration> m_Effects; std::vector<EffectDuration> m_Effects;
const Tower* m_LastDamage; // the last tower that damaged the mob 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_EffectFireTimer;
utils::Timer m_EffectPoisonTimer; utils::Timer m_EffectPoisonTimer;
utils::Timer m_EffectHealTimer; utils::Timer m_EffectHealTimer;
TeamCastle* m_CastleTarget;
utils::CooldownTimer m_AttackTimer;
public: public:
Mob(MobID id, MobLevel level, PlayerID sender) : m_Sender(sender), m_Level(level), Mob(MobID id, MobLevel level, PlayerID sender) : m_Sender(sender), m_Level(level),
m_HasReachedCastle(false), m_EffectFireTimer(1000), m_EffectPoisonTimer(1000), m_EffectFireTimer(1000), m_EffectPoisonTimer(1000),
m_EffectHealTimer(1000) { m_EffectHealTimer(1000), m_CastleTarget(nullptr), m_AttackTimer(1000) {
} }
virtual MobType getType() const = 0; 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; } virtual bool OnDeath(World* world) { return true; }
@@ -114,16 +120,16 @@ public:
bool isDead() const { return m_Health <= 0; } bool isDead() const { return m_Health <= 0; }
bool isAlive() const { return m_Health > 0; } bool isAlive() const { return m_Health > 0; }
const Tower* getLastDamageTower() { return m_LastDamage; } 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 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 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); bool isImmuneTo(TowerType type);
bool isImmuneTo(EffectType type); bool isImmuneTo(EffectType type);
void addEffect(EffectType type, float durationSec, const Tower* tower); void addEffect(EffectType type, float durationSec, Tower* tower);
bool hasEffect(EffectType type); bool hasEffect(EffectType type);
float getTileX() { return getCenterX() - static_cast<float>(static_cast<std::int32_t>(getCenterX())); } // returns a float between 0 and 1 excluded float getTileX() { return getCenterX() - static_cast<float>(static_cast<std::int32_t>(getCenterX())); } // returns a float between 0 and 1 excluded
@@ -137,7 +143,13 @@ protected:
setSize(getStats()->getSize().x, getStats()->getSize().y); setSize(getStats()->getSize().x, getStats()->getSize().y);
} }
private: private:
void updateEffects(std::uint64_t delta); void updateEffects(std::uint64_t delta, World* world);
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); EffectDuration& getEffect(EffectType type);
}; };
@@ -218,6 +230,18 @@ MobPtr createMob(MobID id, MobType type, std::uint8_t level, PlayerID sender);
std::string getMobName(MobType type); std::string getMobName(MobType type);
} }
class MobListener {
public:
virtual void OnMobSpawn(Mob* mob) {}
virtual void OnMobDie(Mob* mob) {}
virtual void OnMobDamage(Mob* target, float damage, Tower* damager) {}
virtual void OnMobTouchCastle(Mob* damager, TeamCastle* enemyCastle) {}
virtual void OnMobCastleDamage(Mob* damager, TeamCastle* enemyCastle, float damage) {}
};
typedef utils::ObjectNotifier<MobListener> MobNotifier;
} // namespace game } // namespace game
} // namespace td } // namespace td

View File

@@ -164,6 +164,7 @@ public:
ArcherTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, getType(), x, y, builder) {} ArcherTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, getType(), x, y, builder) {}
constexpr static float ExplosionRadius = 1.5f; constexpr static float ExplosionRadius = 1.5f;
constexpr static float FireDurationSec = 10.0f;
virtual TowerType getType() const { return TowerType::Archer; } virtual TowerType getType() const { return TowerType::Archer; }
virtual void tick(std::uint64_t delta, World* world); virtual void tick(std::uint64_t delta, World* world);

View File

@@ -10,8 +10,6 @@
#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;
@@ -118,21 +116,20 @@ typedef std::vector<TowerPtr> TowerList;
class WorldListener { class WorldListener {
public: public:
WorldListener(){} WorldListener() {}
virtual void OnArcherTowerShot(MobPtr target, ArcherTower* shooter){} virtual void OnTowerAdd(TowerPtr tower) {}
virtual void OnTowerRemove(TowerPtr tower) {}
virtual void OnArrowShot(MobPtr target, Tower* shooter){} virtual void OnArcherTowerShot(MobPtr target, ArcherTower* shooter) {}
virtual void OnExplosion(utils::shape::Circle explosion, float centerDamage, Tower* shooter){}
virtual void OnMobDamage(MobPtr target, float damage, Tower* damager){} virtual void OnArrowShot(MobPtr target, bool fire, Tower* shooter) {}
virtual void OnExplosion(utils::shape::Circle explosion, float centerDamage, Tower* shooter) {}
virtual void OnMobDead(MobPtr mob){}
}; };
typedef utils::ObjectNotifier<WorldListener> WorldNotifier; typedef utils::ObjectNotifier<WorldListener> WorldNotifier;
class World : public WorldNotifier, public WorldListener{ class World : public WorldListener, public MobListener {
protected: protected:
TowerTileColorPalette m_TowerPlacePalette; TowerTileColorPalette m_TowerPlacePalette;
Color m_WalkablePalette; Color m_WalkablePalette;
@@ -149,6 +146,9 @@ protected:
TowerList m_Towers; TowerList m_Towers;
Game* m_Game; Game* m_Game;
WorldNotifier m_WorldNotifier;
MobNotifier m_MobNotifier;
public: public:
World(Game* game); World(Game* game);
@@ -165,6 +165,7 @@ public:
void spawnMobAt(MobID id, MobType type, std::uint8_t level, PlayerID sender, float x, float y, Direction dir); void spawnMobAt(MobID id, MobType type, std::uint8_t level, PlayerID sender, float x, float y, Direction dir);
TowerPtr placeTowerAt(TowerID id, TowerType type, std::int32_t x, std::int32_t y, PlayerID builder); TowerPtr placeTowerAt(TowerID id, TowerType type, std::int32_t x, std::int32_t y, PlayerID builder);
TowerPtr removeTower(TowerID id);
TilePtr getTile(std::int32_t x, std::int32_t y) const; TilePtr getTile(std::int32_t x, std::int32_t y) const;
@@ -207,20 +208,25 @@ public:
const TowerList& getTowers() const { return m_Towers; }; const TowerList& getTowers() const { return m_Towers; };
TowerPtr getTowerById(TowerID tower); TowerPtr getTowerById(TowerID tower);
const Player* getPlayerById(PlayerID id) const;
WorldNotifier& getWorldNotifier() { return m_WorldNotifier; }
MobNotifier& getMobNotifier() { return m_MobNotifier; }
// WorldListener // WorldListener
virtual void OnArcherTowerShot(MobPtr target, ArcherTower* shooter); virtual void OnArcherTowerShot(MobPtr target, ArcherTower* shooter);
virtual void OnArrowShot(MobPtr target, Tower* shooter); virtual void OnArrowShot(MobPtr target, bool fire, 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, Tower* source); // MobListener
virtual void OnMobDamage(Mob* target, float damage, Tower* source);
virtual void OnMobDie(Mob* mob);
virtual void OnMobCastleDamage(Mob* damager, TeamCastle* enemyCastle, float damage);
private: 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 tickMobs(std::uint64_t delta);
void cleanDeadMobs(); void cleanDeadMobs();
}; };

View File

@@ -44,6 +44,7 @@ public:
void sendMobs(const std::vector<protocol::MobSend>& mobSends); void sendMobs(const std::vector<protocol::MobSend>& mobSends);
void placeTower(game::TowerType type, const glm::vec2& position); void placeTower(game::TowerType type, const glm::vec2& position);
void upgradeTower(game::TowerID tower, game::TowerLevel level); void upgradeTower(game::TowerID tower, game::TowerLevel level);
void removeTower(game::TowerID tower);
}; };
} // namespace client } // namespace client

View File

@@ -37,6 +37,7 @@ public:
Client* getClient() const { return m_Client; } Client* getClient() const { return m_Client; }
render::Renderer* getRenderer() const { return m_Renderer; } render::Renderer* getRenderer() const { return m_Renderer; }
WorldClient& getWorld() { return m_WorldClient; }
virtual void HandlePacket(const protocol::ConnexionInfoPacket* packet); virtual void HandlePacket(const protocol::ConnexionInfoPacket* packet);
virtual void HandlePacket(const protocol::PlayerJoinPacket* packet); virtual void HandlePacket(const protocol::PlayerJoinPacket* packet);
@@ -48,7 +49,7 @@ public:
virtual void HandlePacket(const protocol::UpdateMoneyPacket* packet); virtual void HandlePacket(const protocol::UpdateMoneyPacket* packet);
virtual void HandlePacket(const protocol::DisconnectPacket* packet); virtual void HandlePacket(const protocol::DisconnectPacket* packet);
virtual void HandlePacket(const protocol::WorldDataPacket* packet); virtual void HandlePacket(const protocol::WorldDataPacket* packet);
virtual void HandlePacket(const protocol::WorldAddTowerPacket* packet);
}; };
} // namespace client } // namespace client

View File

@@ -14,12 +14,13 @@ private:
public: public:
WorldClient(ClientGame* game); WorldClient(ClientGame* game);
virtual void HandlePacket(const protocol::WorldBeginDataPacket* packet) override; virtual void HandlePacket(const protocol::WorldBeginDataPacket* packet);
virtual void HandlePacket(const protocol::WorldDataPacket* packet) override; virtual void HandlePacket(const protocol::WorldDataPacket* packet);
virtual void HandlePacket(const protocol::SpawnMobPacket* packet) override; virtual void HandlePacket(const protocol::SpawnMobPacket* packet);
virtual void HandlePacket(const protocol::UpgradeTowerPacket* packet) override; virtual void HandlePacket(const protocol::UpgradeTowerPacket* packet);
virtual void HandlePacket(const protocol::WorldAddTowerPacket* packet);
virtual void HandlePacket(const protocol::RemoveTowerPacket* packet);
virtual void OnArrowShot(game::MobPtr target, game::Tower* shooter) override;
}; };
} // namespace client } // namespace client

View File

@@ -40,6 +40,7 @@ public:
virtual void HandlePacket(const protocol::PlaceTowerPacket* packet); virtual void HandlePacket(const protocol::PlaceTowerPacket* packet);
virtual void HandlePacket(const protocol::SendMobsPacket* packet); virtual void HandlePacket(const protocol::SendMobsPacket* packet);
virtual void HandlePacket(const protocol::UpgradeTowerPacket* packet); virtual void HandlePacket(const protocol::UpgradeTowerPacket* packet);
virtual void HandlePacket(const protocol::RemoveTowerPacket* packet);
std::uint8_t getID() const { return m_ID; } std::uint8_t getID() const { return m_ID; }
const game::Player* getPlayer() const { return m_Player; } const game::Player* getPlayer() const { return m_Player; }

View File

@@ -19,7 +19,6 @@ public:
void spawnMobs(game::MobType type, std::uint8_t level, game::PlayerID sender, std::uint8_t count); void spawnMobs(game::MobType type, std::uint8_t level, game::PlayerID sender, std::uint8_t count);
game::TowerPtr placeTowerAt(game::TowerType type, std::int32_t x, std::int32_t y, game::PlayerID builder); game::TowerPtr placeTowerAt(game::TowerType type, std::int32_t x, std::int32_t y, game::PlayerID builder);
virtual void OnArrowShot(game::MobPtr target, game::Tower* shooter) override;
}; };
} // namespace server } // namespace server

4465
include/misc/Backward.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -53,7 +53,7 @@ public:
std::uint64_t getInterval() const { return m_Interval; } 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 { class CooldownTimer {
private: private:
std::uint64_t m_Cooldown; // in millis std::uint64_t m_Cooldown; // in millis

View File

@@ -35,7 +35,7 @@ public:
virtual void HandlePacket(const SpawnMobPacket* packet) {} virtual void HandlePacket(const SpawnMobPacket* packet) {}
virtual void HandlePacket(const PlaceTowerPacket* packet) {} virtual void HandlePacket(const PlaceTowerPacket* packet) {}
virtual void HandlePacket(const WorldAddTowerPacket* packet) {} virtual void HandlePacket(const WorldAddTowerPacket* packet) {}
virtual void HandlePacket(const WorldRemoveTowerPacket* packet) {} virtual void HandlePacket(const RemoveTowerPacket* packet) {}
virtual void HandlePacket(const SendMobsPacket* packet) {} virtual void HandlePacket(const SendMobsPacket* packet) {}
virtual void HandlePacket(const UpgradeTowerPacket* packet) {} virtual void HandlePacket(const UpgradeTowerPacket* packet) {}
}; };

View File

@@ -33,12 +33,12 @@ enum class PacketType : std::uint8_t {
UpdatePlayerTeam, UpdatePlayerTeam,
ServerTps, ServerTps,
WorldAddTower, WorldAddTower,
WorldRemoveTower,
// client <--> server // client <--> server
KeepAlive, KeepAlive,
Disconnect, Disconnect,
UpgradeTower, UpgradeTower,
RemoveTower,
}; };
struct WorldHeader { struct WorldHeader {
@@ -477,13 +477,13 @@ public:
virtual PacketType getType() const { return PacketType::WorldAddTower; } virtual PacketType getType() const { return PacketType::WorldAddTower; }
}; };
class WorldRemoveTowerPacket : public Packet { class RemoveTowerPacket : public Packet {
private: private:
game::TowerID m_TowerID; game::TowerID m_TowerID;
public: public:
WorldRemoveTowerPacket() {} RemoveTowerPacket() {}
WorldRemoveTowerPacket(game::TowerID id) : m_TowerID(id) {} RemoveTowerPacket(game::TowerID id) : m_TowerID(id) {}
virtual ~WorldRemoveTowerPacket() {} virtual ~RemoveTowerPacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize() const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
@@ -491,7 +491,7 @@ public:
game::TowerID getTowerID() const { return m_TowerID; } game::TowerID getTowerID() const { return m_TowerID; }
virtual PacketType getType() const { return PacketType::WorldRemoveTower; } virtual PacketType getType() const { return PacketType::RemoveTower; }
}; };
class UpgradeTowerPacket : public Packet { class UpgradeTowerPacket : public Packet {

View File

@@ -12,15 +12,19 @@ namespace render {
class VertexCache { class VertexCache {
typedef std::vector<float> Vector; typedef std::vector<float> Vector;
typedef std::pair<Vector::iterator, Vector::iterator> ElementsIndex;
typedef std::pair<ElementsIndex, ElementsIndex> DataIndex; struct DataIndex {
Vector position;
Vector color;
};
private: private:
Vector m_Positions; std::size_t m_VertexCount;
Vector m_Colors;
std::unordered_map<std::uint64_t, DataIndex> m_Indexes; std::unordered_map<std::uint64_t, DataIndex> m_Indexes;
std::unique_ptr<GL::VertexArray> m_VertexArray; std::unique_ptr<GL::VertexArray> m_VertexArray;
public: public:
VertexCache() : m_VertexCount(0) {}
void addData(std::uint64_t index, std::vector<float> positions, std::vector<float> colors); void addData(std::uint64_t index, std::vector<float> positions, std::vector<float> colors);
void removeData(std::uint64_t index); void removeData(std::uint64_t index);
void clear(); void clear();

View File

@@ -23,7 +23,7 @@ class ClientGame;
namespace render { namespace render {
class WorldRenderer { class WorldRenderer : public game::WorldListener {
private: private:
client::ClientGame* m_Client; client::ClientGame* m_Client;
Renderer* m_Renderer; Renderer* m_Renderer;
@@ -46,9 +46,6 @@ public:
void loadModels(); void loadModels();
void addTower(game::TowerPtr tower);
void removeTower(game::TowerPtr tower);
static ImVec4 getImGuiTeamColor(game::TeamColor color); static ImVec4 getImGuiTeamColor(game::TeamColor color);
void update(); void update();
@@ -58,6 +55,11 @@ public:
void moveCam(float relativeX, float relativeY, float aspectRatio); void moveCam(float relativeX, float relativeY, float aspectRatio);
void changeZoom(float zoom); void changeZoom(float zoom);
// WorldListener
virtual void OnTowerAdd(game::TowerPtr tower);
virtual void OnTowerRemove(game::TowerPtr tower);
private: private:
void click(); void click();
void renderWorld() const; void renderWorld() const;
@@ -69,6 +71,7 @@ private:
void renderMobTooltip() const; void renderMobTooltip() const;
void detectClick(); void detectClick();
void detectMobHovering() const; void detectMobHovering() const;
void removeTower();
glm::vec2 getCursorWorldPos() const; glm::vec2 getCursorWorldPos() const;
glm::vec2 getClickWorldPos() const; glm::vec2 getClickWorldPos() const;

View File

@@ -4,7 +4,7 @@
#include <ctime> #include <ctime>
#define TD_VERSION "alpha-0.0.3" #define TD_VERSION "alpha-0.0.5"
namespace td { namespace td {
namespace utils { namespace utils {

View File

@@ -6,14 +6,26 @@
// Description : Hello World in C++, Ansi-style // Description : Hello World in C++, Ansi-style
//============================================================================ //============================================================================
#if !defined(NDEBUG) && !defined(_WIN32)
#define BACKWARD_HAS_UNWIND 1
#define BACKWARD_HAS_DW 1
#endif
#include "window/Display.h" #include "window/Display.h"
#include "updater/Updater.h" #include "updater/Updater.h"
#include "misc/Backward.hpp"
#ifdef __ANDROID__ #ifdef __ANDROID__
extern "C" extern "C"
#endif #endif
int main(int argc, const char* args[]) { int main(int argc, const char* args[]) {
#if !defined(NDEBUG)
// setup signal handling
backward::SignalHandling sh;
#endif
// remove the outdated binary
td::utils::Updater::removeOldFile(); td::utils::Updater::removeOldFile();
Display::create(); Display::create();

View File

@@ -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 game
} // namespace td } // namespace td

View File

@@ -1,4 +1,6 @@
#include "game/Mobs.h" #include "game/Mobs.h"
#include "game/Player.h"
#include "game/World.h"
#include <map> #include <map>
#include <algorithm> #include <algorithm>
@@ -18,7 +20,7 @@ EffectDuration& Mob::getEffect(EffectType effectType) {
return *std::find_if(m_Effects.begin(), m_Effects.end(), [&effectType](EffectDuration effect) { return effect.type == effectType;}); return *std::find_if(m_Effects.begin(), m_Effects.end(), [&effectType](EffectDuration effect) { return effect.type == effectType;});
} }
void Mob::addEffect(EffectType effectType, float durationSec, const Tower* tower) { void Mob::addEffect(EffectType effectType, float durationSec, Tower* tower) {
if (isImmuneTo(effectType)) if (isImmuneTo(effectType))
return; return;
if (hasEffect(effectType)) { if (hasEffect(effectType)) {
@@ -30,11 +32,176 @@ 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) {
updateEffects(delta); if(!hasReachedEnemyCastle()) return;
if (m_AttackTimer.update(delta)) {
world->getMobNotifier().notifyListeners(&MobListener::OnMobCastleDamage, this, m_CastleTarget, getStats()->getDamage());
m_AttackTimer.applyCooldown();
}
} }
void Mob::updateEffects(std::uint64_t delta) { 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, world);
move(delta, world);
attackCastle(delta, world);
}
void Mob::updateEffects(std::uint64_t delta, World* world) {
float deltaSec = (float)delta / 1000.0f; float deltaSec = (float)delta / 1000.0f;
for (std::size_t i = 0; i < m_Effects.size(); i++) { for (std::size_t i = 0; i < m_Effects.size(); i++) {
EffectDuration& effect = m_Effects[i]; EffectDuration& effect = m_Effects[i];
@@ -63,12 +230,12 @@ void Mob::updateEffects(std::uint64_t delta) {
} }
if (hasEffect(EffectType::Fire)) { if (hasEffect(EffectType::Fire)) {
if (m_EffectFireTimer.update(delta)) { if (m_EffectFireTimer.update(delta)) {
damage(3, getEffect(EffectType::Fire).tower); world->getMobNotifier().notifyListeners(&MobListener::OnMobDamage, this, 3.0f, getEffect(EffectType::Fire).tower);
} }
} }
if (hasEffect(EffectType::Poison)) { if (hasEffect(EffectType::Poison)) {
if (m_EffectPoisonTimer.update(delta)) { if (m_EffectPoisonTimer.update(delta)) {
damage(1, getEffect(EffectType::Poison).tower); world->getMobNotifier().notifyListeners(&MobListener::OnMobDamage, this, 1.0f, getEffect(EffectType::Poison).tower);
} }
} }
if (hasEffect(EffectType::Heal)) { if (hasEffect(EffectType::Heal)) {

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->notifyListeners(&WorldListener::OnArcherTowerShot, mob, this); world->getWorldNotifier().notifyListeners(&WorldListener::OnArcherTowerShot, mob, this);
m_Timer.applyCooldown(); m_Timer.applyCooldown();
arrowsShot++; arrowsShot++;
if (arrowsShot >= arrows) if (arrowsShot >= arrows)
@@ -229,8 +229,8 @@ 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
if(damage > 0) if (damage > 0)
world->notifyListeners(&WorldListener::OnMobDamage, mob, damage, this); world->getMobNotifier().notifyListeners(&MobListener::OnMobDamage, mob.get(), damage, this);
m_Timer.applyCooldown(); m_Timer.applyCooldown();
} }
} }
@@ -253,7 +253,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) {
world->notifyListeners(&WorldListener::OnMobDamage, mob, getStats()->getDamage(), this); world->getMobNotifier().notifyListeners(&MobListener::OnMobDamage, mob.get(), getStats()->getDamage(), this);
} else { } else {
float durationSec; float durationSec;
switch (getLevel().getLevel()) { switch (getLevel().getLevel()) {

View File

@@ -13,7 +13,8 @@ namespace td {
namespace game { namespace game {
World::World(Game* game) : m_Game(game) { World::World(Game* game) : m_Game(game) {
bindListener(this); getWorldNotifier().bindListener(this);
getMobNotifier().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 {
@@ -101,7 +102,6 @@ bool World::saveMap(const std::string& fileName) const {
} }
void World::tick(std::uint64_t delta) { void World::tick(std::uint64_t delta) {
moveMobs(delta);
tickMobs(delta); tickMobs(delta);
for (TowerPtr tower : m_Towers) { for (TowerPtr tower : m_Towers) {
tower->tick(delta, this); tower->tick(delta, this);
@@ -114,6 +114,7 @@ void World::spawnMobAt(MobID id, MobType type, std::uint8_t level, PlayerID send
mob->setCenter({ x, y }); mob->setCenter({ x, y });
mob->setDirection(dir); mob->setDirection(dir);
m_Mobs.push_back(mob); m_Mobs.push_back(mob);
getMobNotifier().notifyListeners(&MobListener::OnMobSpawn, mob.get());
} }
TowerPtr World::placeTowerAt(TowerID id, TowerType type, std::int32_t x, std::int32_t y, PlayerID builder) { TowerPtr World::placeTowerAt(TowerID id, TowerType type, std::int32_t x, std::int32_t y, PlayerID builder) {
@@ -122,163 +123,20 @@ TowerPtr World::placeTowerAt(TowerID id, TowerType type, std::int32_t x, std::in
return tower; return tower;
} }
TowerPtr World::removeTower(TowerID towerId) {
auto it = std::find_if(m_Towers.begin(), m_Towers.end(), [towerId](TowerPtr tower) { return tower->getID() == towerId;});
if (it == m_Towers.end()) return nullptr;
TowerPtr tower = *it;
m_Towers.erase(it);
return tower;
}
void World::tickMobs(std::uint64_t delta) { void World::tickMobs(std::uint64_t delta) {
for (MobPtr mob : m_Mobs) { for (MobPtr mob : m_Mobs) {
mob->tick(delta); mob->tick(delta, this);
}
}
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();
const 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();
}
}
}
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;
}
} }
} }
@@ -357,20 +215,23 @@ bool World::CanPlaceBigTower(const glm::vec2& worldPos, PlayerID playerID) const
return false; return false;
} }
void World::OnMobDie(Mob* mob) {
if (mob->OnDeath(this)) { // check if the mob is actually dead (slimes ...)
//reward players
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());
}
}
void World::cleanDeadMobs() { void World::cleanDeadMobs() {
// safely remove mobs when unused
for (std::size_t i = 0; i < m_Mobs.size(); i++) { for (std::size_t i = 0; i < m_Mobs.size(); i++) {
MobPtr mob = m_Mobs[i]; MobPtr mob = m_Mobs[i];
if (mob->isDead()) { if (mob->isDead()) {
if (mob->OnDeath(this)) { m_Mobs.erase(m_Mobs.begin() + i);
//reward players
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());
m_Mobs.erase(m_Mobs.begin() + i);
}
} }
} }
} }
@@ -399,15 +260,20 @@ 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 fireArrows = shooter->getLevel().getPath() == TowerPath::Bottom;
notifyListeners(&WorldListener::OnArrowShot, target, shooter); bool explosiveArrows = shooter->getLevel().getLevel() == 4 && fireArrows;
getWorldNotifier().notifyListeners(&WorldListener::OnArrowShot, target, fireArrows, shooter);
if (explosiveArrows) { if (explosiveArrows) {
notifyListeners(&WorldListener::OnExplosion, utils::shape::Circle{ target->getCenterX(), target->getCenterY(), ArcherTower::ExplosionRadius }, shooter->getStats()->getDamage(), shooter); getWorldNotifier().notifyListeners(&WorldListener::OnExplosion, utils::shape::Circle{ target->getCenterX(), target->getCenterY(), ArcherTower::ExplosionRadius }, shooter->getStats()->getDamage(), shooter);
} }
} }
void World::OnArrowShot(MobPtr target, Tower* shooter) { void World::OnArrowShot(MobPtr target, bool fireArrow, Tower* shooter) {
notifyListeners(&WorldListener::OnMobDamage, target, shooter->getStats()->getDamage(), shooter); getMobNotifier().notifyListeners(&MobListener::OnMobDamage, target.get(), shooter->getStats()->getDamage(), shooter);
if (fireArrow) {
target->addEffect(EffectType::Fire, ArcherTower::FireDurationSec, shooter);
}
} }
void World::OnExplosion(utils::shape::Circle explosion, float centerDamage, Tower* shooter) { void World::OnExplosion(utils::shape::Circle explosion, float centerDamage, Tower* shooter) {
@@ -415,15 +281,22 @@ void World::OnExplosion(utils::shape::Circle explosion, float centerDamage, Towe
if (mob->isAlive() && 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;
notifyListeners(&WorldListener::OnMobDamage, mob, explosionDamage, shooter); getMobNotifier().notifyListeners(&MobListener::OnMobDamage, mob.get(), explosionDamage, shooter);
} }
} }
} }
void World::OnMobDamage(MobPtr target, float damage, Tower* source) { void World::OnMobCastleDamage(Mob* damager, TeamCastle* enemyCastle, float damage) {
enemyCastle->damage(damage);
if (enemyCastle->getLife() <= 0) {
// TODO: a team has won
}
}
void World::OnMobDamage(Mob* target, float damage, Tower* source) {
target->damage(damage, source); target->damage(damage, source);
if (target->isDead()) { if (target->isDead()) {
notifyListeners(&WorldListener::OnMobDead, target); getMobNotifier().notifyListeners(&MobListener::OnMobDie, target);
} }
} }
@@ -451,5 +324,9 @@ const Team& World::getTeam(TeamColor team) const {
return m_Game->getTeam(team); return m_Game->getTeam(team);
} }
const Player* World::getPlayerById(PlayerID id) const {
return m_Game->getPlayerById(id);
}
} // namespace game } // namespace game
} // namespace td } // namespace td

View File

@@ -64,5 +64,10 @@ void Client::upgradeTower(game::TowerID tower, game::TowerLevel level) {
m_Connexion.sendPacket(&packet); m_Connexion.sendPacket(&packet);
} }
void Client::removeTower(game::TowerID tower) {
protocol::RemoveTowerPacket packet(tower);
m_Connexion.sendPacket(&packet);
}
} // namespace client } // namespace client
} // namespace td } // namespace td

View File

@@ -1,10 +1,7 @@
#include "game/client/ClientGame.h" #include "game/client/ClientGame.h"
#include "protocol/PacketDispatcher.h" #include "protocol/PacketDispatcher.h"
//#include "game/Team.h"
#include "game/client/Client.h" #include "game/client/Client.h"
#include <iostream>
namespace td { namespace td {
namespace client { namespace client {
@@ -21,7 +18,6 @@ m_WorldRenderer(&m_WorldClient, this) {
GetDispatcher()->RegisterHandler(protocol::PacketType::UpdateMoney, this); GetDispatcher()->RegisterHandler(protocol::PacketType::UpdateMoney, this);
GetDispatcher()->RegisterHandler(protocol::PacketType::Disconnect, this); GetDispatcher()->RegisterHandler(protocol::PacketType::Disconnect, this);
GetDispatcher()->RegisterHandler(protocol::PacketType::WorldData, this); GetDispatcher()->RegisterHandler(protocol::PacketType::WorldData, this);
GetDispatcher()->RegisterHandler(protocol::PacketType::WorldAddTower, this);
} }
ClientGame::~ClientGame() { ClientGame::~ClientGame() {
@@ -112,9 +108,5 @@ void ClientGame::renderWorld() {
} }
} }
void ClientGame::HandlePacket(const protocol::WorldAddTowerPacket* packet) {
m_WorldRenderer.addTower(m_WorldClient.placeTowerAt(packet->getTowerID(), packet->getTowerType(), packet->getTowerX(), packet->getTowerY(), packet->getBuilder()));
}
} // namespace client } // namespace client
} // namespace td } // namespace td

View File

@@ -7,10 +7,12 @@ namespace td {
namespace client { namespace client {
WorldClient::WorldClient(ClientGame* game) : game::World(game), protocol::PacketHandler(game->GetDispatcher()), m_Game(game) { WorldClient::WorldClient(ClientGame* game) : game::World(game), protocol::PacketHandler(game->GetDispatcher()), m_Game(game) {
GetDispatcher()->RegisterHandler(protocol::PacketType::WorldAddTower, this);
GetDispatcher()->RegisterHandler(protocol::PacketType::WorldBeginData, this); GetDispatcher()->RegisterHandler(protocol::PacketType::WorldBeginData, this);
GetDispatcher()->RegisterHandler(protocol::PacketType::WorldData, this); GetDispatcher()->RegisterHandler(protocol::PacketType::WorldData, this);
GetDispatcher()->RegisterHandler(protocol::PacketType::SpawnMob, this); GetDispatcher()->RegisterHandler(protocol::PacketType::SpawnMob, this);
GetDispatcher()->RegisterHandler(protocol::PacketType::UpgradeTower, this); GetDispatcher()->RegisterHandler(protocol::PacketType::UpgradeTower, this);
GetDispatcher()->RegisterHandler(protocol::PacketType::RemoveTower, this);
} }
void WorldClient::HandlePacket(const protocol::WorldBeginDataPacket* packet) { void WorldClient::HandlePacket(const protocol::WorldBeginDataPacket* packet) {
@@ -32,8 +34,18 @@ void WorldClient::HandlePacket(const protocol::UpgradeTowerPacket* packet) {
tower->upgrade(packet->getTowerLevel().getLevel(), packet->getTowerLevel().getPath()); tower->upgrade(packet->getTowerLevel().getLevel(), packet->getTowerLevel().getPath());
} }
void WorldClient::OnArrowShot(game::MobPtr target, game::Tower* tower) { void WorldClient::HandlePacket(const protocol::WorldAddTowerPacket* packet) {
World::OnArrowShot(target, tower); game::TowerPtr newTower = placeTowerAt(packet->getTowerID(), packet->getTowerType(), packet->getTowerX(), packet->getTowerY(), packet->getBuilder());
getWorldNotifier().notifyListeners(&WorldListener::OnTowerAdd, newTower);
}
void WorldClient::HandlePacket(const protocol::RemoveTowerPacket* packet) {
game::TowerPtr tower = removeTower(packet->getTowerID());
if (tower != nullptr) {
getWorldNotifier().notifyListeners(&WorldListener::OnTowerRemove, tower);
}
} }
} // namespace client } // namespace client

View File

@@ -41,6 +41,7 @@ void ServerConnexion::registerHandlers() {
GetDispatcher()->RegisterHandler(protocol::PacketType::PlaceTower, this); GetDispatcher()->RegisterHandler(protocol::PacketType::PlaceTower, this);
GetDispatcher()->RegisterHandler(protocol::PacketType::SendMobs, this); GetDispatcher()->RegisterHandler(protocol::PacketType::SendMobs, this);
GetDispatcher()->RegisterHandler(protocol::PacketType::UpgradeTower, this); GetDispatcher()->RegisterHandler(protocol::PacketType::UpgradeTower, this);
GetDispatcher()->RegisterHandler(protocol::PacketType::RemoveTower, this);
} }
bool ServerConnexion::updateSocket() { bool ServerConnexion::updateSocket() {
@@ -159,6 +160,8 @@ void ServerConnexion::HandlePacket(const protocol::PlaceTowerPacket* packet) {
game::TowerPtr tower = world->placeTowerAt(towerType, packet->getTowerX(), packet->getTowerY(), m_ID); game::TowerPtr tower = world->placeTowerAt(towerType, packet->getTowerX(), packet->getTowerY(), m_ID);
world->getWorldNotifier().notifyListeners(&game::WorldListener::OnTowerAdd, tower);
protocol::WorldAddTowerPacket addTowerPacket(tower->getID(), packet->getTowerX(), packet->getTowerY(), packet->getTowerType(), m_ID); protocol::WorldAddTowerPacket addTowerPacket(tower->getID(), packet->getTowerX(), packet->getTowerY(), packet->getTowerType(), m_ID);
m_Server->broadcastPacket(&addTowerPacket); m_Server->broadcastPacket(&addTowerPacket);
} }
@@ -179,6 +182,12 @@ void ServerConnexion::HandlePacket(const protocol::UpgradeTowerPacket* packet) {
m_Server->broadcastPacket(packet); m_Server->broadcastPacket(packet);
} }
void ServerConnexion::HandlePacket(const protocol::RemoveTowerPacket* packet) {
//TODO: verify the packet
m_Server->broadcastPacket(packet);
}
ServerConnexion::~ServerConnexion() { ServerConnexion::~ServerConnexion() {
if (GetDispatcher() != nullptr) if (GetDispatcher() != nullptr)
GetDispatcher()->UnregisterHandler(this); GetDispatcher()->UnregisterHandler(this);

View File

@@ -22,14 +22,20 @@ void ServerWorld::spawnMobs(game::MobType type, std::uint8_t level, game::Player
enemyMobSpawn = &getTeam(game::TeamColor::Red).getSpawn(); enemyMobSpawn = &getTeam(game::TeamColor::Red).getSpawn();
} }
std::int32_t spawnCenterX = enemyMobSpawn->getCenterX(); float spawnWidth = enemyMobSpawn->getWidth();
std::int32_t spawnCenterY = enemyMobSpawn->getCenterY(); float spawnHeight = enemyMobSpawn->getHeight();
std::int32_t minSpawnY = spawnCenterY - 2; float spawnCenterX = enemyMobSpawn->getCenterX();
std::int32_t maxSpawnY = spawnCenterY + 2; float spawnCenterY = enemyMobSpawn->getCenterY();
std::int32_t minSpawnX = spawnCenterX - 2; auto mobStats = getMobStats(type, level);
std::int32_t maxSpawnX = spawnCenterX + 2; auto mobSize = mobStats->getSize();
float minSpawnX = spawnCenterX - spawnWidth / 2.0f + mobSize.x / 2.0f;
float maxSpawnX = spawnCenterX + spawnWidth / 2.0f - mobSize.x / 2.0f;
float minSpawnY = spawnCenterY - spawnHeight / 2.0f + mobSize.y / 2.0f;
float maxSpawnY = spawnCenterY + spawnHeight / 2.0f - mobSize.y / 2.0f;
float mobX = utils::getRandomReal<float>(minSpawnX, maxSpawnX); float mobX = utils::getRandomReal<float>(minSpawnX, maxSpawnX);
float mobY = utils::getRandomReal<float>(minSpawnY, maxSpawnY); float mobY = utils::getRandomReal<float>(minSpawnY, maxSpawnY);
@@ -49,9 +55,5 @@ game::TowerPtr ServerWorld::placeTowerAt(game::TowerType type, std::int32_t x, s
return tower; return tower;
} }
void ServerWorld::OnArrowShot(game::MobPtr target, game::Tower* shooter) {
World::OnArrowShot(target, shooter);
}
} // namespace server } // namespace server
} // namespace td } // namespace td

View File

@@ -28,7 +28,7 @@ static std::map<PacketType, PacketCreator> packets = {
{PacketType::SpawnMob, []() -> PacketPtr {return std::make_unique<SpawnMobPacket>(); } }, {PacketType::SpawnMob, []() -> PacketPtr {return std::make_unique<SpawnMobPacket>(); } },
{PacketType::PlaceTower, []() -> PacketPtr {return std::make_unique<PlaceTowerPacket>(); } }, {PacketType::PlaceTower, []() -> PacketPtr {return std::make_unique<PlaceTowerPacket>(); } },
{PacketType::WorldAddTower, []() -> PacketPtr {return std::make_unique<WorldAddTowerPacket>(); } }, {PacketType::WorldAddTower, []() -> PacketPtr {return std::make_unique<WorldAddTowerPacket>(); } },
{PacketType::WorldRemoveTower, []() -> PacketPtr {return std::make_unique<WorldRemoveTowerPacket>(); } }, {PacketType::RemoveTower, []() -> PacketPtr {return std::make_unique<RemoveTowerPacket>(); } },
{PacketType::SendMobs, []() -> PacketPtr {return std::make_unique<SendMobsPacket>(); } }, {PacketType::SendMobs, []() -> PacketPtr {return std::make_unique<SendMobsPacket>(); } },
{PacketType::UpgradeTower, []() -> PacketPtr {return std::make_unique<UpgradeTowerPacket>(); } }, {PacketType::UpgradeTower, []() -> PacketPtr {return std::make_unique<UpgradeTowerPacket>(); } },
}; };

View File

@@ -485,13 +485,13 @@ void WorldAddTowerPacket::Deserialize(DataBuffer& data) {
data >> m_TowerID >> m_TowerX >> m_TowerY >> m_TowerType >> m_Builder; data >> m_TowerID >> m_TowerX >> m_TowerY >> m_TowerType >> m_Builder;
} }
DataBuffer WorldRemoveTowerPacket::Serialize() const { DataBuffer RemoveTowerPacket::Serialize() const {
DataBuffer data; DataBuffer data;
data << getID() << m_TowerID; data << getID() << m_TowerID;
return data; return data;
} }
void WorldRemoveTowerPacket::Deserialize(DataBuffer& data) { void RemoveTowerPacket::Deserialize(DataBuffer& data) {
data >> m_TowerID; data >> m_TowerID;
} }
@@ -543,7 +543,7 @@ REGISTER_DISPATCH_CLASS(ServerTpsPacket);
REGISTER_DISPATCH_CLASS(SpawnMobPacket); REGISTER_DISPATCH_CLASS(SpawnMobPacket);
REGISTER_DISPATCH_CLASS(PlaceTowerPacket); REGISTER_DISPATCH_CLASS(PlaceTowerPacket);
REGISTER_DISPATCH_CLASS(WorldAddTowerPacket); REGISTER_DISPATCH_CLASS(WorldAddTowerPacket);
REGISTER_DISPATCH_CLASS(WorldRemoveTowerPacket); REGISTER_DISPATCH_CLASS(RemoveTowerPacket);
REGISTER_DISPATCH_CLASS(SendMobsPacket); REGISTER_DISPATCH_CLASS(SendMobsPacket);
REGISTER_DISPATCH_CLASS(UpgradeTowerPacket); REGISTER_DISPATCH_CLASS(UpgradeTowerPacket);

View File

@@ -5,46 +5,43 @@ namespace td {
namespace render { namespace render {
void VertexCache::addData(std::uint64_t index, std::vector<float> positions, std::vector<float> colors) { void VertexCache::addData(std::uint64_t index, std::vector<float> positions, std::vector<float> colors) {
ElementsIndex positionsIndexes; m_Indexes.insert({ index, {positions, colors} });
positionsIndexes.first = m_Positions.end(); m_VertexCount += colors.size(); // one color per vertex
m_Positions.insert(m_Positions.end(), positions.begin(), positions.end());
positionsIndexes.second = m_Positions.end() - 1;
ElementsIndex colorsIndexes;
colorsIndexes.first = m_Colors.end();
m_Colors.insert(m_Colors.end(), colors.begin(), colors.end());
colorsIndexes.second = m_Colors.end() - 1;
m_Indexes.insert({ index, {positionsIndexes, colorsIndexes} });
} }
void VertexCache::removeData(std::uint64_t index) { void VertexCache::removeData(std::uint64_t index) {
auto it = m_Indexes.find(index); auto it = m_Indexes.find(index);
if (it != m_Indexes.end()) { if (it != m_Indexes.end()) {
DataIndex indexes = it->second;
ElementsIndex positionsIndexes = indexes.first;
ElementsIndex colorsIndexes = indexes.second;
m_Positions.erase(positionsIndexes.first, positionsIndexes.second);
m_Colors.erase(colorsIndexes.first, colorsIndexes.second);
m_Indexes.erase(it); m_Indexes.erase(it);
m_VertexCount -= it->second.color.size(); // one color per vertex
} }
} }
void VertexCache::clear() { void VertexCache::clear() {
m_Positions.clear();
m_Colors.clear();
m_Indexes.clear(); m_Indexes.clear();
m_VertexCount = 0;
} }
void VertexCache::updateVertexArray() { void VertexCache::updateVertexArray() {
m_VertexArray = std::make_unique<GL::VertexArray>(m_Colors.size()); // one color per vertex m_VertexArray = std::make_unique<GL::VertexArray>(m_VertexCount); // one color per vertex
GL::VertexBuffer positionsBuffer(m_Positions, 2); Vector positions;
positions.reserve(m_VertexCount * 2);
Vector colors;
colors.reserve(m_VertexCount);
for (auto it = m_Indexes.begin(); it != m_Indexes.end(); it++) {
const DataIndex& data = it->second;
positions.insert(positions.end(), data.position.begin(), data.position.end());
colors.insert(colors.end(), data.color.begin(), data.color.end());
}
GL::VertexBuffer positionsBuffer(positions, 2);
positionsBuffer.addVertexAttribPointer(0, 2, 0); positionsBuffer.addVertexAttribPointer(0, 2, 0);
GL::VertexBuffer colorsBuffer(m_Colors, 1); GL::VertexBuffer colorsBuffer(colors, 1);
colorsBuffer.addVertexAttribPointer(1, 1, 0); colorsBuffer.addVertexAttribPointer(1, 1, 0);
m_VertexArray->bind(); m_VertexArray->bind();

View File

@@ -37,6 +37,7 @@ WorldRenderer::WorldRenderer(game::World* world, client::ClientGame* client) : m
m_Renderer->setCamMovement({}); m_Renderer->setCamMovement({});
m_TowerPlacePopup = std::make_unique<gui::TowerPlacePopup>(m_Client->getClient()); m_TowerPlacePopup = std::make_unique<gui::TowerPlacePopup>(m_Client->getClient());
m_MobTooltip = std::make_unique<gui::MobTooltip>(m_Client->getClient()); m_MobTooltip = std::make_unique<gui::MobTooltip>(m_Client->getClient());
m_Client->getWorld().getWorldNotifier().bindListener(this);
} }
void WorldRenderer::updateCursorPos() { void WorldRenderer::updateCursorPos() {
@@ -64,6 +65,18 @@ void WorldRenderer::update() {
m_PopupOpened = false; m_PopupOpened = false;
} }
} }
if (ImGui::IsMouseDoubleClicked(1)) removeTower();
}
void WorldRenderer::removeTower() {
glm::vec2 cursorPos = getCursorWorldPos();
game::TowerPtr clickedTower = m_World->getTower(cursorPos);
if (clickedTower != nullptr) {
m_Client->getClient()->removeTower(clickedTower->getID());
}
} }
void WorldRenderer::renderWorld() const { void WorldRenderer::renderWorld() const {
@@ -235,7 +248,7 @@ void WorldRenderer::renderMobTooltip() const {
void WorldRenderer::detectMobHovering() const { void WorldRenderer::detectMobHovering() const {
glm::vec2 cursorWorldPos = getCursorWorldPos(); glm::vec2 cursorWorldPos = getCursorWorldPos();
for (game::MobPtr mob : m_World->getMobList()) { for (game::MobPtr mob : m_World->getMobList()) {
if(mob->collidesWith({cursorWorldPos.x, cursorWorldPos.y})){ if (mob->collidesWith({ cursorWorldPos.x, cursorWorldPos.y })) {
m_MobTooltip->setMob(mob.get()); m_MobTooltip->setMob(mob.get());
return; return;
} }
@@ -243,13 +256,13 @@ void WorldRenderer::detectMobHovering() const {
m_MobTooltip->setMob(nullptr); m_MobTooltip->setMob(nullptr);
} }
void WorldRenderer::addTower(game::TowerPtr tower) { void WorldRenderer::OnTowerAdd(game::TowerPtr tower) {
const WorldLoader::RenderData& renderData = WorldLoader::loadTowerModel(tower); WorldLoader::RenderData renderData = WorldLoader::loadTowerModel(tower);
m_TowersCache.addData(tower->getID(), renderData.positions, renderData.colors); m_TowersCache.addData(tower->getID(), renderData.positions, renderData.colors);
m_TowersCache.updateVertexArray(); m_TowersCache.updateVertexArray();
} }
void WorldRenderer::removeTower(game::TowerPtr tower) { void WorldRenderer::OnTowerRemove(game::TowerPtr tower) {
m_TowersCache.removeData(tower->getID()); m_TowersCache.removeData(tower->getID());
m_TowersCache.updateVertexArray(); m_TowersCache.updateVertexArray();
} }

View File

@@ -17,12 +17,14 @@ MobTooltip::MobTooltip(client::Client* client) : GuiWidget(client) {
void MobTooltip::render() { void MobTooltip::render() {
if (m_Mob == nullptr) return; 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::BeginTooltip();
ImGui::Text("Sender :"); ImGui::Text("Sender :");
ImGui::SameLine(); ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Text, render::WorldRenderer::getImGuiTeamColor(sender.getTeamColor())); ImGui::PushStyleColor(ImGuiCol_Text, render::WorldRenderer::getImGuiTeamColor(sender->getTeamColor()));
ImGui::Text("%s", sender.getName().c_str()); ImGui::Text("%s", sender->getName().c_str());
ImGui::PopStyleColor(); ImGui::PopStyleColor();
ImGui::Text("Mob HP : %.1f/%i", m_Mob->getHealth(), m_Mob->getStats()->getMaxLife()); 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()); ImGui::Text("Mob Type : %s", game::MobFactory::getMobName(m_Mob->getType()).c_str());

View File

@@ -35,6 +35,12 @@ target("TowerDefense")
end end
else else
if is_os("linux") then
add_links("dw")
end
if is_os("windows") then
add_links("dbghelp", "psapi", "kernel32", "msvcr90")
end
add_cxflags("-pedantic -Wall -Wextra -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wlogical-op -Wmissing-declarations -Wmissing-include-dirs -Wnoexcept -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-null-sentinel -Wstrict-overflow=5 -Wswitch-default -Wundef -Wno-unused") add_cxflags("-pedantic -Wall -Wextra -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wlogical-op -Wmissing-declarations -Wmissing-include-dirs -Wnoexcept -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-null-sentinel -Wstrict-overflow=5 -Wswitch-default -Wundef -Wno-unused")
end end